summaryrefslogtreecommitdiffstats
path: root/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm')
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm220
1 files changed, 220 insertions, 0 deletions
diff --git a/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm b/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm
new file mode 100644
index 000000000..002d688eb
--- /dev/null
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm
@@ -0,0 +1,220 @@
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "avfvideorenderercontrol_p.h"
+#include "avfdisplaylink_p.h"
+#include <avfvideobuffer_p.h>
+#include "qavfhelpers_p.h"
+
+#include <QtMultimedia/qvideoframeformat.h>
+
+#include <avfvideosink_p.h>
+#include <rhi/qrhi.h>
+
+#include <QtCore/qdebug.h>
+
+#import <AVFoundation/AVFoundation.h>
+#include <CoreVideo/CVPixelBuffer.h>
+#include <CoreVideo/CVImageBuffer.h>
+
+QT_USE_NAMESPACE
+
+@interface SubtitleDelegate : NSObject <AVPlayerItemLegibleOutputPushDelegate>
+{
+ AVFVideoRendererControl *m_renderer;
+}
+
+- (void)legibleOutput:(AVPlayerItemLegibleOutput *)output
+ didOutputAttributedStrings:(NSArray<NSAttributedString *> *)strings
+ nativeSampleBuffers:(NSArray *)nativeSamples
+ forItemTime:(CMTime)itemTime;
+
+@end
+
+@implementation SubtitleDelegate
+
+-(id)initWithRenderer: (AVFVideoRendererControl *)renderer
+{
+ if (!(self = [super init]))
+ return nil;
+
+ m_renderer = renderer;
+
+ return self;
+}
+
+- (void)legibleOutput:(AVPlayerItemLegibleOutput *)output
+ didOutputAttributedStrings:(NSArray<NSAttributedString *> *)strings
+ nativeSampleBuffers:(NSArray *)nativeSamples
+ forItemTime:(CMTime)itemTime
+{
+ QString text;
+ for (NSAttributedString *s : strings) {
+ if (!text.isEmpty())
+ text += QChar::LineSeparator;
+ text += QString::fromNSString(s.string);
+ }
+ m_renderer->setSubtitleText(text);
+}
+
+@end
+
+
+AVFVideoRendererControl::AVFVideoRendererControl(QObject *parent)
+ : QObject(parent)
+{
+ m_displayLink = new AVFDisplayLink(this);
+ connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp)));
+}
+
+AVFVideoRendererControl::~AVFVideoRendererControl()
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+ m_displayLink->stop();
+ if (m_videoOutput)
+ [m_videoOutput release];
+ if (m_subtitleOutput)
+ [m_subtitleOutput release];
+ if (m_subtitleDelegate)
+ [m_subtitleDelegate release];
+}
+
+void AVFVideoRendererControl::reconfigure()
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << "reconfigure";
+#endif
+ if (!m_layer) {
+ m_displayLink->stop();
+ return;
+ }
+
+ QMutexLocker locker(&m_mutex);
+
+ m_displayLink->start();
+
+ nativeSizeChanged();
+}
+
+void AVFVideoRendererControl::setLayer(CALayer *layer)
+{
+ if (m_layer == layer)
+ return;
+
+ AVPlayerLayer *plLayer = playerLayer();
+ if (plLayer) {
+ if (m_videoOutput)
+ [[[plLayer player] currentItem] removeOutput:m_videoOutput];
+
+ if (m_subtitleOutput)
+ [[[plLayer player] currentItem] removeOutput:m_subtitleOutput];
+ }
+
+ if (!layer && m_sink)
+ m_sink->setVideoFrame(QVideoFrame());
+
+ AVFVideoSinkInterface::setLayer(layer);
+}
+
+void AVFVideoRendererControl::setVideoRotation(QtVideo::Rotation rotation)
+{
+ m_rotation = rotation;
+}
+
+void AVFVideoRendererControl::setVideoMirrored(bool mirrored)
+{
+ m_mirrored = mirrored;
+}
+
+void AVFVideoRendererControl::updateVideoFrame(const CVTimeStamp &ts)
+{
+ Q_UNUSED(ts);
+
+ if (!m_sink)
+ return;
+
+ if (!m_layer)
+ return;
+
+ auto *layer = playerLayer();
+ if (!layer.readyForDisplay)
+ return;
+ nativeSizeChanged();
+
+ QVideoFrame frame;
+ size_t width, height;
+ CVPixelBufferRef pixelBuffer = copyPixelBufferFromLayer(width, height);
+ if (!pixelBuffer)
+ return;
+ AVFVideoBuffer *buffer = new AVFVideoBuffer(this, pixelBuffer);
+// qDebug() << "Got pixelbuffer with format" << fmt << Qt::hex << CVPixelBufferGetPixelFormatType(pixelBuffer);
+ CVPixelBufferRelease(pixelBuffer);
+
+ frame = QVideoFrame(buffer, buffer->videoFormat());
+ frame.setRotation(m_rotation);
+ frame.setMirrored(m_mirrored);
+ m_sink->setVideoFrame(frame);
+}
+
+CVPixelBufferRef AVFVideoRendererControl::copyPixelBufferFromLayer(size_t& width, size_t& height)
+{
+ AVPlayerLayer *layer = playerLayer();
+ //Is layer valid
+ if (!layer) {
+#ifdef QT_DEBUG_AVF
+ qWarning("copyPixelBufferFromLayer: invalid layer");
+#endif
+ return nullptr;
+ }
+
+ AVPlayerItem * item = [[layer player] currentItem];
+
+ if (!m_videoOutput) {
+ if (!m_outputSettings)
+ setOutputSettings();
+ m_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:m_outputSettings];
+ [m_videoOutput setDelegate:nil queue:nil];
+ }
+ if (!m_subtitleOutput) {
+ m_subtitleOutput = [[AVPlayerItemLegibleOutput alloc] init];
+ m_subtitleDelegate = [[SubtitleDelegate alloc] initWithRenderer:this];
+ [m_subtitleOutput setDelegate:m_subtitleDelegate queue:dispatch_get_main_queue()];
+ }
+ if (![item.outputs containsObject:m_videoOutput])
+ [item addOutput:m_videoOutput];
+ if (![item.outputs containsObject:m_subtitleOutput])
+ [item addOutput:m_subtitleOutput];
+
+ CFTimeInterval currentCAFrameTime = CACurrentMediaTime();
+ CMTime currentCMFrameTime = [m_videoOutput itemTimeForHostTime:currentCAFrameTime];
+ // happens when buffering / loading
+ if (CMTimeCompare(currentCMFrameTime, kCMTimeZero) < 0) {
+ return nullptr;
+ }
+
+ if (![m_videoOutput hasNewPixelBufferForItemTime:currentCMFrameTime])
+ return nullptr;
+
+ CVPixelBufferRef pixelBuffer = [m_videoOutput copyPixelBufferForItemTime:currentCMFrameTime
+ itemTimeForDisplay:nil];
+ if (!pixelBuffer) {
+#ifdef QT_DEBUG_AVF
+ qWarning("copyPixelBufferForItemTime returned nil");
+ CMTimeShow(currentCMFrameTime);
+#endif
+ return nullptr;
+ }
+
+ width = CVPixelBufferGetWidth(pixelBuffer);
+ height = CVPixelBufferGetHeight(pixelBuffer);
+// auto f = CVPixelBufferGetPixelFormatType(pixelBuffer);
+// char fmt[5];
+// memcpy(fmt, &f, 4);
+// fmt[4] = 0;
+// qDebug() << "copyPixelBuffer" << f << fmt << width << height;
+ return pixelBuffer;
+}
+
+#include "moc_avfvideorenderercontrol_p.cpp"