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;
143 auto *layer = playerLayer();
144 if (!layer.readyForDisplay)
149 size_t width, height;
150 qint64 startTime = -1;
151 QCFType<CVPixelBufferRef> pixelBuffer = copyPixelBufferFromLayer(width, height, startTime);
157 auto buffer = std::make_unique<AVFVideoBuffer>(
this, std::move(pixelBuffer));
159 auto format = buffer->videoFormat();
160 format.setRotation(m_rotation);
161 format.setMirrored(m_mirrored);
162 frame = QVideoFramePrivate::createFrame(std::move(buffer), format);
164 if (startTime >= 0) {
165 frame.setStartTime(startTime);
168 AVPlayerItem *playerItem = [layer.player currentItem];
171 for (AVPlayerItemTrack *track in playerItem.tracks) {
172 if ([track.assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
173 fps = track.assetTrack.nominalFrameRate;
178 frame.setEndTime(startTime + qint64(1000000.0 / fps));
182 m_sink->setVideoFrame(frame);
185QCFType<CVPixelBufferRef>
188 AVPlayerLayer *layer = playerLayer();
192 qWarning(
"copyPixelBufferFromLayer: invalid layer");
197 AVPlayerItem * item = [[layer player] currentItem];
199 if (!m_videoOutput) {
200 if (!m_outputSettings)
202 m_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:m_outputSettings];
203 [m_videoOutput setDelegate:nil queue:nil];
205 if (!m_subtitleOutput) {
206 m_subtitleOutput = [[AVPlayerItemLegibleOutput alloc] init];
207 m_subtitleDelegate = [[SubtitleDelegate alloc] initWithRenderer:
this];
208 [m_subtitleOutput setDelegate:m_subtitleDelegate queue:dispatch_get_main_queue()];
210 if (![item.outputs containsObject:m_videoOutput])
211 [item addOutput:m_videoOutput];
212 if (![item.outputs containsObject:m_subtitleOutput])
213 [item addOutput:m_subtitleOutput];
215 CFTimeInterval currentCAFrameTime = CACurrentMediaTime();
216 CMTime currentCMFrameTime = [m_videoOutput itemTimeForHostTime:currentCAFrameTime];
218 if (CMTimeCompare(currentCMFrameTime, kCMTimeZero) < 0) {
222 if (![m_videoOutput hasNewPixelBufferForItemTime:currentCMFrameTime])
225 CMTime outItemTimeForDisplay = kCMTimeInvalid;
226 CVPixelBufferRef pixelBuffer =
227 [m_videoOutput copyPixelBufferForItemTime:currentCMFrameTime
228 itemTimeForDisplay:&outItemTimeForDisplay];
231 qWarning(
"copyPixelBufferForItemTime returned nil");
232 CMTimeShow(currentCMFrameTime);
238 CMTIME_IS_VALID(outItemTimeForDisplay) ? outItemTimeForDisplay : currentCMFrameTime;
239 startTime = qint64(qreal(pts.value) / pts.timescale * 1000000.0);
241 width = CVPixelBufferGetWidth(pixelBuffer);
242 height = CVPixelBufferGetHeight(pixelBuffer);
248 return QCFType<CVPixelBufferRef>{ pixelBuffer };
251#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()