Revision control

++ // 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 <QtFFmpegMediaPluginImpl/private/qffmpegplaybackenginedefs_p.h>
++ #include <QtFFmpegMediaPluginImpl/private/qffmpegtimecontroller_p.h>
++ #include <QtFFmpegMediaPluginImpl/private/qffmpegmediadataholder_p.h>
++ #include <QtFFmpegMediaPluginImpl/private/qffmpegcodeccontext_p.h>
++ #include <QtFFmpegMediaPluginImpl/private/qffmpegplaybackutils_p.h>
++ #include <QtFFmpegMediaPluginImpl/private/qffmpegtime_p.h>
++ #include <QtMultimedia/qplaybackoptions.h>
++
++ #include <QtCore/qpointer.h>
++
++ #include <unordered_map>
++
++ QT_BEGIN_NAMESPACE
++
++ class QAudioSink;
++ class QVideoSink;
++ class QAudioOutput;
++ class QAudioBufferOutput;
++ class QFFmpegMediaPlayer;
++ class QPlaybackOptions;
++
++ namespace QFFmpeg
++ {
++
++ class PlaybackEngine : public QObject
++ {
++ Q_OBJECT
++ public:
++ using TrackType = QPlatformMediaPlayer::TrackType;
++
++ 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(TrackType type, int streamNumber);
++
++ TrackPosition currentPosition(bool topPos = true) const;
++
++ TrackDuration duration() const;
++
++ bool isSeekable() const;
++
++ const QList<MediaDataHolder::StreamInfo> &
++ streamInfo(TrackType trackType) const;
++
++ const QMediaMetaData &metaData() const;
++
++ int activeTrack(TrackType type) const;
++
++ void setPitchCompensation(bool enabled);
++
++ Q_SIGNALS:
++ void endOfStream();
++ void errorOccured(QMediaPlayer::Error, const QString &);
++ void loopChanged();
++ void buffered();
++
++ protected: // objects managing
++ struct ObjectDeleter
++ {
++ void operator()(PlaybackEngineObject *) const;
++
++ PlaybackEngine *engine = nullptr;
++ };
++
++ template<typename T>
++ using ObjectPtr = std::unique_ptr<T, ObjectDeleter>;
++
++ using RendererPtr = ObjectPtr<Renderer>;
++ using StreamPtr = ObjectPtr<StreamDecoder>;
++
++ template<typename T, typename... Args>
++ ObjectPtr<T> createPlaybackEngineObject(Args &&...args);
++
++ virtual RendererPtr createRenderer(TrackType trackType);
++
++ template <typename AudioOutput>
++ void updateActiveAudioOutput(AudioOutput *output);
++
++ void updateActiveVideoOutput(QVideoSink *sink, bool cleanOutput = false);
++
++ private:
++ void createStreamAndRenderer(TrackType trackType);
++
++ void createDemuxer();
++
++ void registerObject(PlaybackEngineObject &object);
++
++ template<typename C, typename Action>
++ void forEachExistingObject(Action &&action);
++
++ template<typename Action>
++ void forEachExistingObject(Action &&action);
++
++ void forceUpdate();
++
++ void recreateObjects();
++
++ void createObjectsIfNeeded();
++
++ void updateObjectsPausedState();
++
++ void deleteFreeThreads();
++
++ void onFirstPacketFound(const PlaybackEngineObjectID &id, TrackPosition absSeekPos);
++
++ void onRendererSynchronized(const PlaybackEngineObjectID &id, SteadyClock::time_point timePoint,
++ TrackPosition trackPosition);
++
++ void onRendererFinished(const PlaybackEngineObjectID &id);
++
++ void onRendererLoopChanged(const PlaybackEngineObjectID &id, TrackPosition offset,
++ int loopIndex);
++
++ void triggerStepIfNeeded();
++
++ static QString objectThreadName(const PlaybackEngineObject &object);
++
++ std::optional<CodecContext> codecContextForTrack(TrackType trackType);
++
++ bool hasMediaStream() const;
++
++ void finilizeTime(TrackPosition pos);
++
++ void finalizeOutputs();
++
++ bool hasRenderer(const PlaybackEngineObjectID &id) const;
++
++ template <typename T>
++ bool checkObjectID(T &object, const PlaybackEngineObjectID &id) const
++ {
++ return object && object->objectID() == id.objectID && id.sessionID == m_currentID.sessionID;
++ }
++
++ void updateVideoSinkSize(QVideoSink *prevSink = nullptr);
++
++ TrackPosition boundPosition(TrackPosition position) const;
++
++ AudioRenderer *getAudioRenderer();
++
++ private:
++ MediaDataHolder m_media;
++
++ TimeController m_timeController;
++
++ std::unordered_map<QString, std::unique_ptr<QThread>> m_threads;
++ bool m_threadsDirty = false;
++
++ QPointer<QVideoSink> m_videoSink;
++ QPointer<QAudioOutput> m_audioOutput;
++ QPointer<QAudioBufferOutput> m_audioBufferOutput;
++
++ QMediaPlayer::PlaybackState m_state = QMediaPlayer::StoppedState;
++
++ ObjectPtr<Demuxer> m_demuxer;
++ QPlatformMediaPlayer::TrackTypeMap<StreamPtr> m_streams;
++ QPlatformMediaPlayer::TrackTypeMap<RendererPtr> m_renderers;
++
++ bool m_seekPending = false;
++
++ QPlatformMediaPlayer::TrackTypeMap<std::optional<CodecContext>> m_codecContexts;
++ int m_loops = QMediaPlayer::Once;
++ LoopOffset m_currentLoopOffset;
++
++ bool m_pitchCompensation = true;
++ QPlaybackOptions m_options;
++ PlaybackEngineObjectID m_currentID{ 1, 1 };
++ };
++
++ template<typename T, typename... Args>
++ PlaybackEngine::ObjectPtr<T> PlaybackEngine::createPlaybackEngineObject(Args &&...args)
++ {
++ ++m_currentID.objectID;
++ auto result = ObjectPtr<T>(new T(m_currentID, std::forward<Args>(args)...), { this });
++ registerObject(*result);
++ return result;
++ }
++ } // namespace QFFmpeg
++
++ QT_END_NAMESPACE
++
++ #endif // QFFMPEGPLAYBACKENGINE_P_H