7#include "private/qplatformaudiooutput_p.h"
8#include "private/qplatformvideosink_p.h"
9#include "private/qaudiobufferoutput_p.h"
27template<
typename Array>
30 using T =
typename Array::value_type;
31 return { T{ {}, {} }, T{ {}, {} }, T{ {}, {} } };
48 qCDebug(qLcPlaybackEngine) <<
"Create PlaybackEngine";
49 qRegisterMetaType<QFFmpeg::Packet>();
50 qRegisterMetaType<QFFmpeg::Frame>();
54 qCDebug(qLcPlaybackEngine) <<
"Delete PlaybackEngine";
57 forEachExistingObject([](
auto &
object) {
object.reset(); });
61void PlaybackEngine::onRendererFinished()
63 auto isAtEnd = [
this](
auto trackType) {
64 return !m_renderers[trackType] || m_renderers[trackType]->isAtEnd();
83 qCDebug(qLcPlaybackEngine) <<
"Playback engine end of stream";
93 if (loopIndex > m_currentLoopOffset.
index) {
94 m_currentLoopOffset = {
offset, loopIndex };
96 }
else if (loopIndex == m_currentLoopOffset.
index &&
offset != m_currentLoopOffset.
pos) {
97 qWarning() <<
"Unexpected offset for loop" << loopIndex <<
":" <<
offset <<
"vs"
98 << m_currentLoopOffset.
pos;
103void PlaybackEngine::onRendererSynchronized(
quint64 id, std::chrono::steady_clock::time_point tp,
106 if (!hasRenderer(
id))
112 m_timeController.
sync(tp,
pos);
114 forEachExistingObject<Renderer>([&](
auto &
renderer) {
124 if (
state == m_state)
127 const auto prevState = std::exchange(m_state,
state);
138 triggerStepIfNeeded();
140 updateObjectsPausedState();
143void PlaybackEngine::updateObjectsPausedState()
148 forEachExistingObject([&](
auto &
object) {
149 bool objectPaused =
false;
151 if constexpr (std::is_same_v<
decltype(*object),
Renderer &>)
152 objectPaused = paused;
154 auto streamPaused = [](
bool p,
auto &
r) {
155 const auto needMoreFrames =
r &&
r->stepInProgress();
156 return p && !needMoreFrames;
159 if constexpr (std::is_same_v<
decltype(*object), StreamDecoder &>)
160 objectPaused = streamPaused(paused,
renderer(
object->trackType()));
162 objectPaused = std::accumulate(m_renderers.begin(), m_renderers.end(), paused,
166 object->setPaused(objectPaused);
173 if (!std::exchange(
engine->m_threadsDirty,
true))
183 auto threadName = objectThreadName(
object);
184 auto &
thread = m_threads[threadName];
186 thread = std::make_unique<QThread>();
192 object.moveToThread(
thread.get());
201 ? createPlaybackEngineObject<VideoRenderer>(m_timeController, m_videoSink, m_media.
rotation())
204 return m_audioOutput || m_audioBufferOutput
205 ? createPlaybackEngineObject<AudioRenderer>(m_timeController, m_audioOutput, m_audioBufferOutput)
209 ? createPlaybackEngineObject<SubtitleRenderer>(m_timeController, m_videoSink)
216template<
typename C,
typename Action>
217void PlaybackEngine::forEachExistingObject(Action &&action)
219 auto handleNotNullObject = [&](
auto &
object) {
220 if constexpr (std::is_base_of_v<C, std::remove_reference_t<
decltype(*object)>>)
225 handleNotNullObject(m_demuxer);
226 std::for_each(m_streams.begin(), m_streams.end(), handleNotNullObject);
227 std::for_each(m_renderers.begin(), m_renderers.end(), handleNotNullObject);
230template<
typename Action>
231void PlaybackEngine::forEachExistingObject(Action &&action)
233 forEachExistingObject<PlaybackEngineObject>(std::forward<Action>(action));
241 m_timeController.
sync(m_currentLoopOffset.
pos +
pos);
249 qWarning() <<
"Cannot set loops for non-seekable source";
253 if (std::exchange(m_loops, loops) == loops)
256 qCDebug(qLcPlaybackEngine) <<
"set playback engine loops:" << loops <<
"prev loops:" << m_loops
257 <<
"index:" << m_currentLoopOffset.
index;
260 m_demuxer->setLoops(loops);
263void PlaybackEngine::triggerStepIfNeeded()
276QString PlaybackEngine::objectThreadName(
const PlaybackEngineObject &
object)
279 if (
auto stream = qobject_cast<const StreamDecoder *>(&
object))
297void PlaybackEngine::recreateObjects()
301 forEachExistingObject([](
auto &
object) {
object.reset(); });
303 createObjectsIfNeeded();
306void PlaybackEngine::createObjectsIfNeeded()
317void PlaybackEngine::forceUpdate()
320 triggerStepIfNeeded();
321 updateObjectsPausedState();
326 auto codec = codecForTrack(trackType);
328 auto &
renderer = m_renderers[trackType];
340 &PlaybackEngine::onRendererSynchronized);
343 &PlaybackEngine::onRendererLoopChanged);
347 &PlaybackEngine::updateObjectsPausedState);
350 &PlaybackEngine::onRendererFinished);
353 auto &
stream = m_streams[trackType] =
354 createPlaybackEngineObject<StreamDecoder>(*
codec,
renderer->seekPosition());
371 auto &
result = m_codecs[trackType];
375 <<
"Create codec for stream:" << streamIndex <<
"trackType:" << trackType;
381 "Cannot create codec," + maybeCodec.error());
385 result = maybeCodec.value();
391bool PlaybackEngine::hasMediaStream()
const
397void PlaybackEngine::createDemuxer()
399 std::array<int, QPlatformMediaPlayer::NTrackTypes> streamIndexes = { -1, -1, -1 };
401 bool hasStreams =
false;
402 forEachExistingObject<StreamDecoder>([&](
auto &
stream) {
404 const auto trackType =
stream->trackType();
411 const PositionWithOffset positionWithOffset{
currentPosition(
false), m_currentLoopOffset };
413 m_demuxer = createPlaybackEngineObject<Demuxer>(m_media.
avContext(), positionWithOffset,
414 streamIndexes, m_loops);
418 forEachExistingObject<StreamDecoder>([&](
auto &
stream) {
429 forEachExistingObject([&](
auto &
object) {
430 using Type = std::remove_reference_t<
decltype(*object)>;
431 if constexpr (!std::is_same_v<Type, Demuxer>)
433 &Type::setInitialPosition);
437 m_timeController.
sync(tp,
pos);
444void PlaybackEngine::deleteFreeThreads() {
445 m_threadsDirty =
false;
446 auto freeThreads = std::move(m_threads);
448 forEachExistingObject([&](
auto &
object) {
449 m_threads.insert(freeThreads.extract(objectThreadName(*
object)));
452 for (
auto &[
name, thr] : freeThreads)
455 for (
auto &[
name, thr] : freeThreads)
465 m_media = std::move(media);
466 updateVideoSinkSize();
471 auto prev = std::exchange(m_videoSink,
sink);
475 updateVideoSinkSize(prev);
478 if (!
sink || !prev) {
511 std::optional<qint64>
pos;
513 for (
size_t i = 0;
i < m_renderers.size(); ++
i) {
522 const auto rendererPos =
renderer->lastPosition();
524 : topPos ? std::max(*
pos, rendererPos)
525 : std::min(*
pos, rendererPos);
531 return boundPosition(*
pos - m_currentLoopOffset.
pos);
541const QList<MediaDataHolder::StreamInfo> &
562 m_codecs[trackType] = {};
564 m_renderers[trackType].reset();
565 m_streams = defaultObjectsArray<decltype(m_streams)>();
568 updateVideoSinkSize();
569 createObjectsIfNeeded();
570 updateObjectsPausedState();
573void PlaybackEngine::finilizeTime(
qint64 pos)
579 m_currentLoopOffset = {};
582void PlaybackEngine::finalizeOutputs()
584 if (m_audioBufferOutput)
591bool PlaybackEngine::hasRenderer(
quint64 id)
const
593 return std::any_of(m_renderers.begin(), m_renderers.end(),
594 [
id](
auto &
renderer) { return renderer && renderer->id() == id; });
597template <
typename AudioOutput>
607 if (
auto renderer = qobject_cast<SubtitleRenderer *>(
615void PlaybackEngine::updateVideoSinkSize(
QVideoSink *prevSink)
617 auto platformVideoSink = m_videoSink ? m_videoSink->
platformVideoSink() :
nullptr;
618 if (!platformVideoSink)
621 if (prevSink && prevSink->platformVideoSink())
622 platformVideoSink->
setNativeSize(prevSink->platformVideoSink()->nativeSize());
625 if (streamIndex >= 0) {
626 const auto context = m_media.
avContext();
627 const auto stream = context->streams[streamIndex];
628 const AVRational pixelAspectRatio =
629 av_guess_sample_aspect_ratio(context,
stream,
nullptr);
633 { pixelAspectRatio.num, pixelAspectRatio.den });
649#include "moc_qffmpegplaybackengine_p.cpp"
\qmltype AudioOutput \instantiates QAudioOutput
static QMaybe< Codec > create(AVStream *stream, AVFormatContext *formatContext)
void firstPacketFound(TimePoint tp, qint64 trackPos)
void onPacketProcessed(Packet)
static RequestingSignal signalByTrackType(QPlatformMediaPlayer::TrackType trackType)
void error(int code, const QString &errorString)
int activeTrack(QPlatformMediaPlayer::TrackType type) const
void setLoops(int loopsCount)
void setVideoSink(QVideoSink *sink)
void setState(QMediaPlayer::PlaybackState state)
void updateActiveAudioOutput(AudioOutput *output)
virtual RendererPtr createRenderer(QPlatformMediaPlayer::TrackType trackType)
void setAudioBufferOutput(QAudioBufferOutput *output)
void setPlaybackRate(float rate)
void setAudioSink(QAudioOutput *output)
qint64 currentPosition(bool topPos=true) const
void errorOccured(int, const QString &)
ObjectPtr< Renderer > RendererPtr
~PlaybackEngine() override
const QList< MediaDataHolder::StreamInfo > & streamInfo(QPlatformMediaPlayer::TrackType trackType) const
void setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber)
void setMedia(MediaDataHolder media)
const QMediaMetaData & metaData() const
void updateActiveVideoOutput(QVideoSink *sink, bool cleanOutput=false)
float playbackRate() const
void synchronized(Id id, TimePoint tp, qint64 pos)
void frameProcessed(Frame)
void onFinalFrameReceived()
void loopChanged(Id id, qint64 offset, int index)
void onFrameProcessed(Frame frame)
void packetProcessed(Packet)
void onFinalPacketReceived()
void requestHandleFrame(Frame frame)
PlaybackRate playbackRate() const
void sync(qint64 trackPos=0)
Clock::time_point TimePoint
void setPlaybackRate(PlaybackRate playbackRate)
qint64 currentPosition(const Clock::duration &offset=Clock::duration{ 0 }) const
void setPaused(bool paused)
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
QThread * thread() const
Returns the thread in which the object lives.
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void start(Priority=InheritPriority)
The QVideoSink class represents a generic sink for video data.
QPlatformVideoSink * platformVideoSink() const
Array defaultObjectsArray()
static constexpr bool shouldPauseStreams
Combined button and popup list for selecting options.
static QDBusError::ErrorType get(const char *name)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
QT_BEGIN_NAMESPACE constexpr const T & qMin(const T &a, const T &b)
constexpr const T & qMax(const T &a, const T &b)
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLintptr offset
GLsizei GLenum GLboolean sink
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
unsigned long long quint64
QT_BEGIN_NAMESPACE typedef uchar * output
QSvgRenderer * renderer
[0]
void operator()(PlaybackEngineObject *) const