diff options
Diffstat (limited to 'src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm')
-rw-r--r-- | src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm | 220 |
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" |