6#include <avfvideobuffer_p.h>
7#include <avfvideosink_p.h>
8#include <mediaplayer/avfdisplaylink_p.h>
10#include <QtMultimedia/qvideoframeformat.h>
11#include <QtMultimedia/private/qavfhelpers_p.h>
12#include <QtMultimedia/private/qvideoframe_p.h>
13#include <QtGui/rhi/qrhi.h>
14#include <QtCore/qdebug.h>
16#import <AVFoundation/AVFoundation.h>
17#include <CoreVideo/CVPixelBuffer.h>
18#include <CoreVideo/CVImageBuffer.h>
22@interface SubtitleDelegate : NSObject <AVPlayerItemLegibleOutputPushDelegate>
24 AVFVideoRendererControl *m_renderer;
27- (
void)legibleOutput:(AVPlayerItemLegibleOutput *)output
28 didOutputAttributedStrings:(NSArray<NSAttributedString *> *)strings
29 nativeSampleBuffers:(NSArray *)nativeSamples
30 forItemTime:(CMTime)itemTime;
34@implementation SubtitleDelegate
36-(id)initWithRenderer: (AVFVideoRendererControl *)renderer
38 if (!(self = [super init]))
41 m_renderer = renderer;
46- (
void)legibleOutput:(AVPlayerItemLegibleOutput *)output
47 didOutputAttributedStrings:(NSArray<NSAttributedString *> *)strings
48 nativeSampleBuffers:(NSArray *)nativeSamples
49 forItemTime:(CMTime)itemTime
52 for (NSAttributedString *s : strings) {
54 text += QChar::LineSeparator;
55 text += QString::fromNSString(s.string);
57 m_renderer->setSubtitleText(text);
63AVFVideoRendererControl::AVFVideoRendererControl(QObject *parent)
66 m_displayLink =
new AVFDisplayLink(
this);
68 connect(m_displayLink, &AVFDisplayLink::tick,
this, &AVFVideoRendererControl::updateVideoFrame);
74 qDebug() << Q_FUNC_INFO;
76 m_displayLink->stop();
77 m_displayLink->disconnect(
this);
79 [m_videoOutput release];
81 [m_subtitleOutput release];
82 if (m_subtitleDelegate)
83 [m_subtitleDelegate release];
89 qDebug() <<
"reconfigure";
92 m_displayLink->stop();
96 QMutexLocker locker(&m_mutex);
98 m_displayLink->start();
105 if (m_layer == layer)
108 AVPlayerLayer *plLayer = playerLayer();
111 [[[plLayer player] currentItem] removeOutput:m_videoOutput];
113 if (m_subtitleOutput)
114 [[[plLayer player] currentItem] removeOutput:m_subtitleOutput];
117 if (!layer && m_sink)
118 m_sink->setVideoFrame(QVideoFrame());
120 AVFVideoSinkInterface::setLayer(layer);
125 m_rotation = rotation;
130 m_mirrored = mirrored;
141 auto *layer = playerLayer();
142 if (!layer.readyForDisplay)
147 size_t width, height;
148 qint64 startTime = -1;
149 QCFType<CVPixelBufferRef> pixelBuffer = copyPixelBufferFromLayer(width, height, startTime);
155 auto buffer = std::make_unique<AVFVideoBuffer>(
this, std::move(pixelBuffer));
157 auto format = buffer->videoFormat();
158 format.setRotation(m_rotation);
159 format.setMirrored(m_mirrored);
160 frame = QVideoFramePrivate::createFrame(std::move(buffer), format);
162 if (startTime >= 0) {
163 frame.setStartTime(startTime);
166 AVPlayerItem *playerItem = [layer.player currentItem];
169 for (AVPlayerItemTrack *track in playerItem.tracks) {
170 if ([track.assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
171 fps = track.assetTrack.nominalFrameRate;
176 frame.setEndTime(startTime + qint64(1000000.0 / fps));
180 m_sink->setVideoFrame(frame);
183QCFType<CVPixelBufferRef>
186 AVPlayerLayer *layer = playerLayer();
190 qWarning(
"copyPixelBufferFromLayer: invalid layer");
195 AVPlayerItem * item = [[layer player] currentItem];
197 if (!m_videoOutput) {
198 if (!m_outputSettings)
200 m_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:m_outputSettings];
201 [m_videoOutput setDelegate:nil queue:nil];
203 if (!m_subtitleOutput) {
204 m_subtitleOutput = [[AVPlayerItemLegibleOutput alloc] init];
205 m_subtitleDelegate = [[SubtitleDelegate alloc] initWithRenderer:
this];
206 [m_subtitleOutput setDelegate:m_subtitleDelegate queue:dispatch_get_main_queue()];
208 if (![item.outputs containsObject:m_videoOutput])
209 [item addOutput:m_videoOutput];
210 if (![item.outputs containsObject:m_subtitleOutput])
211 [item addOutput:m_subtitleOutput];
213 CFTimeInterval currentCAFrameTime = CACurrentMediaTime();
214 CMTime currentCMFrameTime = [m_videoOutput itemTimeForHostTime:currentCAFrameTime];
216 if (CMTimeCompare(currentCMFrameTime, kCMTimeZero) < 0) {
220 if (![m_videoOutput hasNewPixelBufferForItemTime:currentCMFrameTime])
223 CMTime outItemTimeForDisplay = kCMTimeInvalid;
224 CVPixelBufferRef pixelBuffer =
225 [m_videoOutput copyPixelBufferForItemTime:currentCMFrameTime
226 itemTimeForDisplay:&outItemTimeForDisplay];
229 qWarning(
"copyPixelBufferForItemTime returned nil");
230 CMTimeShow(currentCMFrameTime);
236 CMTIME_IS_VALID(outItemTimeForDisplay) ? outItemTimeForDisplay : currentCMFrameTime;
237 startTime = qint64(qreal(pts.value) / pts.timescale * 1000000.0);
239 width = CVPixelBufferGetWidth(pixelBuffer);
240 height = CVPixelBufferGetHeight(pixelBuffer);
246 return QCFType<CVPixelBufferRef>{ pixelBuffer };
249#include "moc_avfvideorenderercontrol_p.cpp"
void setVideoMirrored(bool mirrored)
~AVFVideoRendererControl() override
void setVideoRotation(QtVideo::Rotation)
void setLayer(CALayer *layer) override
void reconfigure() override
virtual void setOutputSettings()