// Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QFFMPEGPLAYBACKENGINE_P_H #define QFFMPEGPLAYBACKENGINE_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // /* Playback engine design description. * * * PLAYBACK ENGINE OBJECTS * * - Playback engine manages 7 objects inside, each one works in a separate thread. * Each object inherits PlaybackEngineObject. The objects are: * Demuxer * Stream Decoders: audio, video, subtitles * Renderers: audio, video, subtitles * * * THREADS: * * - By default, each object works in a separate thread. It's easy to reconfigure * to using several objects in thread. * - New thread is allocated if a new object is created and the engine doesn't * have free threads. If it does, the thread is to be reused. * - If all objects for some thread are deleted, the thread becomes free and the engine * postpones its termination. * * OBJECTS WEAK CONNECTIVITY * * - The objects know nothing about others and about PlaybackEngine. * For any interactions the objects use slots/signals. * * - PlaybackEngine knows the objects object and is able to create/delete them and * call their public methods. * */ #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QAudioSink; class QVideoSink; class QAudioOutput; class QAudioBufferOutput; class QFFmpegMediaPlayer; class QPlaybackOptions; namespace QFFmpeg { class PlaybackEngine : public QObject { Q_OBJECT public: explicit PlaybackEngine(const QPlaybackOptions &options); ~PlaybackEngine() override; void setMedia(MediaDataHolder media); void setVideoSink(QVideoSink *sink); void setAudioSink(QAudioOutput *output); void setAudioSink(QPlatformAudioOutput *output); void setAudioBufferOutput(QAudioBufferOutput *output); void setState(QMediaPlayer::PlaybackState state); void play() { setState(QMediaPlayer::PlayingState); } void pause() { setState(QMediaPlayer::PausedState); } void stop() { setState(QMediaPlayer::StoppedState); } void seek(TrackPosition pos); void setLoops(int loopsCount); void setPlaybackRate(float rate); float playbackRate() const; void setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber); TrackPosition currentPosition(bool topPos = true) const; TrackDuration duration() const; bool isSeekable() const; const QList & streamInfo(QPlatformMediaPlayer::TrackType trackType) const; const QMediaMetaData &metaData() const; int activeTrack(QPlatformMediaPlayer::TrackType type) const; void setPitchCompensation(bool enabled); signals: void endOfStream(); void errorOccured(int, const QString &); void loopChanged(); void buffered(); protected: // objects managing struct ObjectDeleter { void operator()(PlaybackEngineObject *) const; PlaybackEngine *engine = nullptr; }; template using ObjectPtr = std::unique_ptr; using RendererPtr = ObjectPtr; using StreamPtr = ObjectPtr; template ObjectPtr createPlaybackEngineObject(Args &&...args); virtual RendererPtr createRenderer(QPlatformMediaPlayer::TrackType trackType); template void updateActiveAudioOutput(AudioOutput *output); void updateActiveVideoOutput(QVideoSink *sink, bool cleanOutput = false); private: void createStreamAndRenderer(QPlatformMediaPlayer::TrackType trackType); void createDemuxer(); void registerObject(PlaybackEngineObject &object); template void forEachExistingObject(Action &&action); template void forEachExistingObject(Action &&action); void forceUpdate(); void recreateObjects(); void createObjectsIfNeeded(); void updateObjectsPausedState(); void deleteFreeThreads(); void onFirsPacketFound(quint64 id, TrackPosition absSeekPos); void onRendererSynchronized(quint64 id, SteadyClock::time_point timePoint, TrackPosition trackPosition); void onRendererFinished(); void onRendererLoopChanged(quint64 id, TrackPosition offset, int loopIndex); void triggerStepIfNeeded(); static QString objectThreadName(const PlaybackEngineObject &object); std::optional codecContextForTrack(QPlatformMediaPlayer::TrackType trackType); bool hasMediaStream() const; void finilizeTime(TrackPosition pos); void finalizeOutputs(); bool hasRenderer(quint64 id) const; void updateVideoSinkSize(QVideoSink *prevSink = nullptr); TrackPosition boundPosition(TrackPosition position) const; AudioRenderer *getAudioRenderer(); private: MediaDataHolder m_media; TimeController m_timeController; std::unordered_map> m_threads; bool m_threadsDirty = false; QPointer m_videoSink; QPointer m_audioOutput; QPointer m_audioBufferOutput; QMediaPlayer::PlaybackState m_state = QMediaPlayer::StoppedState; ObjectPtr m_demuxer; std::array m_streams; std::array m_renderers; bool m_shouldUpdateTimeOnFirstPacket = false; bool m_seekPending = false; std::array, QPlatformMediaPlayer::NTrackTypes> m_codecContexts; int m_loops = QMediaPlayer::Once; LoopOffset m_currentLoopOffset; bool m_pitchCompensation = true; QPlaybackOptions m_options; }; template PlaybackEngine::ObjectPtr PlaybackEngine::createPlaybackEngineObject(Args &&...args) { auto result = ObjectPtr(new T(std::forward(args)...), { this }); registerObject(*result); return result; } } // namespace QFFmpeg QT_END_NAMESPACE #endif // QFFMPEGPLAYBACKENGINE_P_H