7#include "private/qplatformaudiooutput_p.h"
8#include "private/qplatformvideosink_p.h"
26template<
typename Array>
29 using T =
typename Array::value_type;
30 return { T{ {}, {} }, T{ {}, {} }, T{ {}, {} } };
47 qCDebug(qLcPlaybackEngine) <<
"Create PlaybackEngine";
48 qRegisterMetaType<QFFmpeg::Packet>();
49 qRegisterMetaType<QFFmpeg::Frame>();
53 qCDebug(qLcPlaybackEngine) <<
"Delete PlaybackEngine";
56 forEachExistingObject([](
auto &
object) {
object.reset(); });
60void PlaybackEngine::onRendererFinished()
62 auto isAtEnd = [
this](
auto trackType) {
63 return !m_renderers[trackType] || m_renderers[trackType]->isAtEnd();
82 qCDebug(qLcPlaybackEngine) <<
"Playback engine end of stream";
92 if (loopIndex > m_currentLoopOffset.
index) {
93 m_currentLoopOffset = {
offset, loopIndex };
95 }
else if (loopIndex == m_currentLoopOffset.
index &&
offset != m_currentLoopOffset.
pos) {
96 qWarning() <<
"Unexpected offset for loop" << loopIndex <<
":" <<
offset <<
"vs"
97 << m_currentLoopOffset.
pos;
102void PlaybackEngine::onRendererSynchronized(
quint64 id, std::chrono::steady_clock::time_point tp,
105 if (!hasRenderer(
id))
111 m_timeController.
sync(tp,
pos);
113 forEachExistingObject<Renderer>([&](
auto &
renderer) {
123 if (
state == m_state)
126 const auto prevState = std::exchange(m_state,
state);
137 triggerStepIfNeeded();
139 updateObjectsPausedState();
142void PlaybackEngine::updateObjectsPausedState()
147 forEachExistingObject([&](
auto &
object) {
148 bool objectPaused =
false;
150 if constexpr (std::is_same_v<
decltype(*object),
Renderer &>)
151 objectPaused = paused;
153 auto streamPaused = [](
bool p,
auto &
r) {
154 const auto needMoreFrames =
r &&
r->stepInProgress();
155 return p && !needMoreFrames;
158 if constexpr (std::is_same_v<
decltype(*object), StreamDecoder &>)
159 objectPaused = streamPaused(paused,
renderer(
object->trackType()));
161 objectPaused = std::accumulate(m_renderers.begin(), m_renderers.end(), paused,
165 object->setPaused(objectPaused);
172 if (!std::exchange(
engine->m_threadsDirty,
true))
182 auto threadName = objectThreadName(
object);
183 auto &
thread = m_threads[threadName];
185 thread = std::make_unique<QThread>();
191 object.moveToThread(
thread.get());
200 ? createPlaybackEngineObject<VideoRenderer>(m_timeController, m_videoSink, m_media.
rotation())
204 ? createPlaybackEngineObject<AudioRenderer>(m_timeController, m_audioOutput)
208 ? createPlaybackEngineObject<SubtitleRenderer>(m_timeController, m_videoSink)
215template<
typename C,
typename Action>
216void PlaybackEngine::forEachExistingObject(Action &&action)
218 auto handleNotNullObject = [&](
auto &
object) {
219 if constexpr (std::is_base_of_v<C, std::remove_reference_t<
decltype(*object)>>)
224 handleNotNullObject(m_demuxer);
225 std::for_each(m_streams.begin(), m_streams.end(), handleNotNullObject);
226 std::for_each(m_renderers.begin(), m_renderers.end(), handleNotNullObject);
229template<
typename Action>
230void PlaybackEngine::forEachExistingObject(Action &&action)
232 forEachExistingObject<PlaybackEngineObject>(std::forward<Action>(action));
240 m_timeController.
sync(m_currentLoopOffset.
pos +
pos);
248 qWarning() <<
"Cannot set loops for non-seekable source";
252 if (std::exchange(m_loops, loops) == loops)
255 qCDebug(qLcPlaybackEngine) <<
"set playback engine loops:" << loops <<
"prev loops:" << m_loops
256 <<
"index:" << m_currentLoopOffset.
index;
259 m_demuxer->setLoops(loops);
262void PlaybackEngine::triggerStepIfNeeded()
275QString PlaybackEngine::objectThreadName(
const PlaybackEngineObject &
object)
278 if (
auto stream = qobject_cast<const StreamDecoder *>(&
object))
296void PlaybackEngine::recreateObjects()
300 forEachExistingObject([](
auto &
object) {
object.reset(); });
302 createObjectsIfNeeded();
305void PlaybackEngine::createObjectsIfNeeded()
316void PlaybackEngine::forceUpdate()
319 triggerStepIfNeeded();
320 updateObjectsPausedState();
325 auto codec = codecForTrack(trackType);
327 auto &
renderer = m_renderers[trackType];
339 &PlaybackEngine::onRendererSynchronized);
342 &PlaybackEngine::onRendererLoopChanged);
346 &PlaybackEngine::updateObjectsPausedState);
349 &PlaybackEngine::onRendererFinished);
352 auto &
stream = m_streams[trackType] =
353 createPlaybackEngineObject<StreamDecoder>(*
codec,
renderer->seekPosition());
370 auto &
result = m_codecs[trackType];
374 <<
"Create codec for stream:" << streamIndex <<
"trackType:" << trackType;
380 "Cannot create codec," + maybeCodec.error());
384 result = maybeCodec.value();
390bool PlaybackEngine::hasMediaStream()
const
396void PlaybackEngine::createDemuxer()
398 std::array<int, QPlatformMediaPlayer::NTrackTypes> streamIndexes = { -1, -1, -1 };
400 bool hasStreams =
false;
401 forEachExistingObject<StreamDecoder>([&](
auto &
stream) {
403 const auto trackType =
stream->trackType();
410 const PositionWithOffset positionWithOffset{
currentPosition(
false), m_currentLoopOffset };
412 m_demuxer = createPlaybackEngineObject<Demuxer>(m_media.
avContext(), positionWithOffset,
413 streamIndexes, m_loops);
417 forEachExistingObject<StreamDecoder>([&](
auto &
stream) {
428 forEachExistingObject([&](
auto &
object) {
429 using Type = std::remove_reference_t<
decltype(*object)>;
430 if constexpr (!std::is_same_v<Type, Demuxer>)
432 &Type::setInitialPosition);
436 m_timeController.
sync(tp,
pos);
443void PlaybackEngine::deleteFreeThreads() {
444 m_threadsDirty =
false;
445 auto freeThreads = std::move(m_threads);
447 forEachExistingObject([&](
auto &
object) {
448 m_threads.insert(freeThreads.extract(objectThreadName(*
object)));
451 for (
auto &[
name, thr] : freeThreads)
454 for (
auto &[
name, thr] : freeThreads)
464 m_media = std::move(media);
465 updateVideoSinkSize();
470 auto prev = std::exchange(m_videoSink,
sink);
474 updateVideoSinkSize(prev);
477 if (!
sink || !prev) {
489 auto prev = std::exchange(m_audioOutput,
output);
502 std::optional<qint64>
pos;
504 for (
size_t i = 0;
i < m_renderers.size(); ++
i) {
513 const auto rendererPos =
renderer->lastPosition();
515 : topPos ? std::max(*
pos, rendererPos)
516 : std::min(*
pos, rendererPos);
522 return boundPosition(*
pos - m_currentLoopOffset.
pos);
532const QList<MediaDataHolder::StreamInfo> &
553 m_codecs[trackType] = {};
555 m_renderers[trackType].reset();
556 m_streams = defaultObjectsArray<decltype(m_streams)>();
559 updateVideoSinkSize();
560 createObjectsIfNeeded();
561 updateObjectsPausedState();
564void PlaybackEngine::finilizeTime(
qint64 pos)
570 m_currentLoopOffset = {};
573void PlaybackEngine::finalizeOutputs()
579bool PlaybackEngine::hasRenderer(
quint64 id)
const
581 return std::any_of(m_renderers.begin(), m_renderers.end(),
582 [
id](
auto &
renderer) { return renderer && renderer->id() == id; });
594 if (
auto renderer = qobject_cast<SubtitleRenderer *>(
602void PlaybackEngine::updateVideoSinkSize(
QVideoSink *prevSink)
604 auto platformVideoSink = m_videoSink ? m_videoSink->
platformVideoSink() :
nullptr;
605 if (!platformVideoSink)
608 if (prevSink && prevSink->platformVideoSink())
609 platformVideoSink->
setNativeSize(prevSink->platformVideoSink()->nativeSize());
612 if (streamIndex >= 0) {
615 const AVRational pixelAspectRatio =
620 { pixelAspectRatio.num, pixelAspectRatio.den });
636#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)
virtual RendererPtr createRenderer(QPlatformMediaPlayer::TrackType trackType)
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 updateActiveAudioOutput(QAudioOutput *output)
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 Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
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