Revision control
Copy as Markdown
// 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