4#include <common/qgstreamermediaplayer_p.h>
6#include <audio/qgstreameraudiodevice_p.h>
7#include <common/qglist_helper_p.h>
8#include <common/qgst_debug_p.h>
9#include <common/qgst_discoverer_p.h>
10#include <common/qgst_play_p.h>
11#include <common/qgstpipeline_p.h>
12#include <common/qgstreameraudiooutput_p.h>
13#include <common/qgstreamermessage_p.h>
14#include <common/qgstreamermetadata_p.h>
15#include <common/qgstreamervideooutput_p.h>
16#include <common/qgstreamervideosink_p.h>
17#include <uri_handler/qgstreamer_qiodevice_handler_p.h>
18#include <qgstreamerformatinfo_p.h>
20#include <QtMultimedia/qaudiodevice.h>
21#include <QtCore/qdebug.h>
22#include <QtCore/qiodevice.h>
23#include <QtCore/qloggingcategory.h>
24#include <QtCore/qthread.h>
25#include <QtCore/qurl.h>
26#include <QtCore/private/quniquehandle_p.h>
36 using TrackType = QGstreamerMediaPlayer::TrackType;
38 QByteArrayView type = caps
.at(0
).name();
40 if (type.startsWith(
"video/x-raw"))
41 return TrackType::VideoStream;
42 if (type.startsWith(
"audio/x-raw"))
43 return TrackType::AudioStream;
44 if (type.startsWith(
"text"))
45 return TrackType::SubtitleStream;
56 using namespace std::chrono;
57 using namespace std::chrono_literals;
59 auto discoveryResult = discoverer.discover(url);
60 if (discoveryResult) {
62 gst_play_set_uri(m_gstPlay.get(), url.toEncoded().constData());
64 m_trackMetaData.fill({});
65 seekableChanged(discoveryResult->isSeekable);
66 if (discoveryResult->duration)
67 m_duration = round<milliseconds>(*discoveryResult->duration);
70 durationChanged(m_duration);
72 m_metaData = QGst::toContainerMetadata(*discoveryResult);
74 videoAvailableChanged(!discoveryResult->videoStreams.empty());
75 audioAvailableChanged(!discoveryResult->audioStreams.empty());
78 for (
const auto &videoInfo : discoveryResult->videoStreams) {
79 m_trackMetaData[0].emplace_back(QGst::toStreamMetadata(videoInfo));
80 QGstStructureView structure = videoInfo.caps.at(0);
81 m_nativeSize.emplace_back(structure.nativeSize());
83 for (
const auto &audioInfo : discoveryResult->audioStreams)
84 m_trackMetaData[1].emplace_back(QGst::toStreamMetadata(audioInfo));
85 for (
const auto &subtitleInfo : discoveryResult->subtitleStreams)
86 m_trackMetaData[2].emplace_back(QGst::toStreamMetadata(subtitleInfo));
88 using Key = QMediaMetaData::Key;
89 auto copyKeysToRootMetadata = [&](
const QMediaMetaData &reference, QSpan<
const Key> keys) {
90 for (QMediaMetaData::Key key : keys) {
91 QVariant referenceValue = reference.value(key);
92 if (referenceValue.isValid())
93 m_metaData.insert(key, referenceValue);
99 if (!m_trackMetaData[0].empty())
100 copyKeysToRootMetadata(m_trackMetaData[0].front(),
110 if (!m_trackMetaData[1].empty())
111 copyKeysToRootMetadata(m_trackMetaData[1].front(),
117 if (!m_url.isEmpty())
118 m_metaData.insert(QMediaMetaData::Key::Url, m_url);
120 qCDebug(qLcMediaPlayer) <<
"metadata:" << m_metaData;
121 qCDebug(qLcMediaPlayer) <<
"video metadata:" << m_trackMetaData[0];
122 qCDebug(qLcMediaPlayer) <<
"audio metadata:" << m_trackMetaData[1];
123 qCDebug(qLcMediaPlayer) <<
"subtitle metadata:" << m_trackMetaData[2];
128 isVideoAvailable() ? 0 : -1,
129 isAudioAvailable() ? 0 : -1,
132 updateVideoTrackEnabled();
133 updateAudioTrackEnabled();
134 updateNativeSizeOnVideoOutput();
137 return bool(discoveryResult);
146 qCDebug(qLcMediaPlayer) <<
"Added pad" << pad.name() <<
"from" << src.name();
150 std::optional<QGstreamerMediaPlayer::TrackType> type = toTrackType(caps);
154 customPipelinePads[*type] = pad;
162 customPipeline.add(sink);
164 customPipelineSinks[VideoStream] = sink;
171 customPipeline.add(sink);
173 customPipelineSinks[AudioStream] = sink;
177 case SubtitleStream: {
181 customPipeline.add(sink);
183 customPipelineSinks[SubtitleStream] = sink;
200 Q_ASSERT(thread()->isCurrentThread());
202 qCDebug(qLcMediaPlayer) <<
"Removed pad" << pad.name() <<
"from" << src.name() <<
"for stream"
205 auto found = std::find(customPipelinePads.begin(), customPipelinePads.end(), pad);
206 if (found == customPipelinePads.end())
209 TrackType type = TrackType(std::distance(customPipelinePads.begin(), found));
214 case SubtitleStream: {
215 if (customPipelineSinks[VideoStream]) {
216 customPipeline.stopAndRemoveElements(customPipelineSinks[VideoStream]);
217 customPipelineSinks[VideoStream] = {};
229 using namespace std::chrono_literals;
230 m_nativeSize.clear();
232 bool metadataNeedsSignal = !m_metaData.isEmpty();
233 bool tracksNeedsSignal =
234 std::any_of(m_trackMetaData.begin(), m_trackMetaData.end(), [](
const auto &container) {
235 return !container.empty();
239 m_trackMetaData.fill({});
241 seekableChanged(
false);
243 videoAvailableChanged(
false);
244 audioAvailableChanged(
false);
246 m_activeTrack.fill(-1);
248 if (metadataNeedsSignal)
250 if (tracksNeedsSignal)
256 int activeVideoTrack = activeTrack(TrackType::VideoStream);
257 bool hasVideoTrack = activeVideoTrack != -1;
259 QSize nativeSize = hasVideoTrack ? m_nativeSize[activeTrack(TrackType::VideoStream)] : QSize{};
261 QVariant orientation = hasVideoTrack
262 ? m_trackMetaData[TrackType::VideoStream][activeTrack(TrackType::VideoStream)].value(
263 QMediaMetaData::Key::Orientation)
266 if (orientation.isValid()) {
267 auto rotation = orientation.value<QtVideo::Rotation>();
268 gstVideoOutput->setRotation(rotation);
275 gst_play_seek(m_gstPlay.get(), gst_play_get_position(m_gstPlay.get()));
280 bool hasTrack = m_activeTrack[TrackType::VideoStream] != -1;
284 gst_play_set_video_track_enabled(m_gstPlay.get(), hasTrack && hasSink);
289 bool hasTrack = m_activeTrack[TrackType::AudioStream] != -1;
290 bool hasAudioOut = gstAudioOutput;
292 gst_play_set_audio_track_enabled(m_gstPlay.get(), hasTrack && hasAudioOut);
297 if (qFuzzyIsNull(newProgress - m_bufferProgress))
300 m_bufferProgress = newProgress;
301 bufferProgressChanged(m_bufferProgress);
306 auto handlers = std::initializer_list<QGObjectHandlerScopedConnection *>{ &sourceSetup };
307 for (QGObjectHandlerScopedConnection *handler : handlers)
308 handler->disconnect();
313 auto videoOutput = QGstreamerVideoOutput::create();
315 return q23::unexpected{ videoOutput.error() };
323 gst_play_config_set_seek_accurate(config, accurate);
327 QMediaPlayer *parent)
329 QPlatformMediaPlayer(parent),
330 gstVideoOutput(videoOutput),
332 gst_play_new(
nullptr),
333 QGstPlayHandle::HasRef,
336 GST_PIPELINE_CAST(gst_play_get_pipeline(m_gstPlay.get())),
337 QGstPipeline::HasRef,
340 QGstBusHandle{ gst_play_get_message_bus(m_gstPlay.get()), QGstBusHandle::HasRef },
349 constexpr bool useNxpWorkaround = std::is_same_v<
decltype(&gst_play_config_set_seek_accurate),
350 void (*)(GstPlay *, gboolean)>;
352 QUniqueGstStructureHandle config{
353 gst_play_get_config(m_gstPlay.get()),
356 if constexpr (useNxpWorkaround)
357 setSeekAccurate(m_gstPlay.get(),
true);
359 setSeekAccurate(config.get(),
true);
361 gst_play_set_config(m_gstPlay.get(), config.release());
363 QUniqueGstStructureHandle config{
364 gst_play_get_config(m_gstPlay.get()),
366 gst_play_config_set_seek_accurate(config.get(),
true);
367 gst_play_set_config(m_gstPlay.get(), config.release());
370 gstVideoOutput->setParent(
this);
372 m_playbin.set(
"video-sink", gstVideoOutput->gstElement());
373 m_playbin.set(
"text-sink", gstVideoOutput->gstSubtitleElement());
374 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
376 m_gstPlayBus.installMessageFilter(
this);
379 gst_play_set_subtitle_track_enabled(m_gstPlay.get(),
false);
381 sourceSetup = m_playbin.connect(
"source-setup", GCallback(sourceSetupCallback),
this);
383 m_activeTrack.fill(-1);
391 cleanupCustomPipeline();
393 m_gstPlayBus.removeMessageFilter(
static_cast<QGstreamerBusMessageFilter *>(
this));
394 gst_bus_set_flushing(m_gstPlayBus.get(), TRUE);
395 gst_play_stop(m_gstPlay.get());
398 m_playbin.setStateSync(GST_STATE_NULL);
400 m_playbin.set(
"video-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
401 m_playbin.set(
"text-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
402 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
407 using namespace std::chrono;
409 positionChanged(round<milliseconds>(nanoseconds{
410 gst_play_get_position(m_gstPlay.get()),
416 if (isCustomSource()) {
417 constexpr bool traceBusMessages =
true;
418 if (traceBusMessages)
419 qCDebug(qLcMediaPlayer) <<
"received bus message:" << message;
421 switch (message.type()) {
422 case GST_MESSAGE_WARNING:
423 qWarning() <<
"received bus message:" << message;
426 case GST_MESSAGE_INFO:
427 qInfo() <<
"received bus message:" << message;
430 case GST_MESSAGE_ERROR:
431 qWarning() <<
"received bus message:" << message;
432 customPipeline.dumpPipelineGraph(
"GST_MESSAGE_ERROR");
435 case GST_MESSAGE_LATENCY:
436 customPipeline.recalculateLatency();
445 switch (message.type()) {
446 case GST_MESSAGE_APPLICATION:
447 if (gst_play_is_play_message(message.message()))
448 return processBusMessageApplication(message);
452 qCDebug(qLcMediaPlayer) << message;
462 using namespace std::chrono;
464 gst_play_message_parse_type(message.message(), &type);
465 qCDebug(qLcMediaPlayer) << QGstPlayMessageAdaptor{ message };
468 case GST_PLAY_MESSAGE_URI_LOADED: {
469 mediaStatusChanged(QMediaPlayer::LoadedMedia);
473 case GST_PLAY_MESSAGE_POSITION_UPDATED: {
474 if (state() == QMediaPlayer::PlaybackState::PlayingState) {
476 constexpr bool usePayload =
false;
477 if constexpr (usePayload) {
478 GstClockTime position;
479 gst_play_message_parse_position_updated(message.message(), &position);
480 positionChanged(round<milliseconds>(nanoseconds{ position }));
482 GstClockTime position = gst_play_get_position(m_gstPlay.get());
483 positionChanged(round<milliseconds>(nanoseconds{ position }));
488 case GST_PLAY_MESSAGE_DURATION_CHANGED: {
489 GstClockTime duration;
490 gst_play_message_parse_duration_updated(message.message(), &duration);
491 milliseconds durationInMs = round<milliseconds>(nanoseconds{ duration });
492 durationChanged(durationInMs);
494 m_metaData.insert(QMediaMetaData::Duration,
int(durationInMs.count()));
499 case GST_PLAY_MESSAGE_BUFFERING: {
501 gst_play_message_parse_buffering_percent(message.message(), &percent);
502 updateBufferProgress(percent * 0.01f);
505 case GST_PLAY_MESSAGE_STATE_CHANGED: {
507 gst_play_message_parse_state_changed(message.message(), &state);
510 case GstPlayState::GST_PLAY_STATE_STOPPED:
511 if (stateChangeToSkip) {
512 qCDebug(qLcMediaPlayer) <<
" skipping StoppedState transition";
514 stateChangeToSkip -= 1;
517 stateChanged(QMediaPlayer::StoppedState);
518 updateBufferProgress(0);
521 case GstPlayState::GST_PLAY_STATE_PAUSED:
522 stateChanged(QMediaPlayer::PausedState);
523 mediaStatusChanged(QMediaPlayer::BufferedMedia);
525 updateBufferProgress(1);
527 case GstPlayState::GST_PLAY_STATE_BUFFERING:
528 mediaStatusChanged(QMediaPlayer::BufferingMedia);
530 case GstPlayState::GST_PLAY_STATE_PLAYING:
531 stateChanged(QMediaPlayer::PlayingState);
532 mediaStatusChanged(QMediaPlayer::BufferedMedia);
534 updateBufferProgress(1);
541 case GST_PLAY_MESSAGE_MEDIA_INFO_UPDATED: {
544 QUniqueGstPlayMediaInfoHandle info{};
545 gst_play_message_parse_media_info_updated(message.message(), &info);
547 seekableChanged(gst_play_media_info_is_seekable(info.get()));
549 const gchar *title = gst_play_media_info_get_title(info.get());
550 m_metaData.insert(QMediaMetaData::Title, QString::fromUtf8(title));
557 case GST_PLAY_MESSAGE_END_OF_STREAM: {
559 positionChanged(m_duration);
560 qCDebug(qLcMediaPlayer) <<
"EOS: restarting loop";
561 gst_play_play(m_gstPlay.get());
562 positionChanged(0ms);
566 stateChangeToSkip += 1;
568 qCDebug(qLcMediaPlayer) <<
"EOS: done";
569 positionChanged(m_duration);
570 mediaStatusChanged(QMediaPlayer::EndOfMedia);
571 stateChanged(QMediaPlayer::StoppedState);
577 case GST_PLAY_MESSAGE_ERROR:
578 case GST_PLAY_MESSAGE_WARNING:
579 case GST_PLAY_MESSAGE_VIDEO_DIMENSIONS_CHANGED:
580 case GST_PLAY_MESSAGE_VOLUME_CHANGED:
581 case GST_PLAY_MESSAGE_MUTE_CHANGED:
582 case GST_PLAY_MESSAGE_SEEK_DONE:
586 Q_UNREACHABLE_RETURN(
false);
592 return m_duration.count();
597 return !m_url.isEmpty() || m_stream;
605 switch (mediaStatus()) {
606 case QMediaPlayer::MediaStatus::NoMedia:
607 case QMediaPlayer::MediaStatus::InvalidMedia:
617 return m_bufferProgress;
622 return QMediaTimeRange();
627 return gst_play_get_rate(m_gstPlay.get());
632 if (isCustomSource()) {
633 static std::once_flag flag;
634 std::call_once(flag, [] {
637 <<
"setPlaybackRate with custom gstreamer pipelines can cause pipeline hangs. "
641 customPipeline.setPlaybackRate(rate);
645 if (rate == playbackRate())
648 qCDebug(qLcMediaPlayer) <<
"gst_play_set_rate" << rate;
649 gst_play_set_rate(m_gstPlay.get(), rate);
650 playbackRateChanged(rate);
655 std::chrono::milliseconds posInMs{ pos };
657 setPosition(posInMs);
662 using namespace std::chrono;
664 if (isCustomSource()) {
665 static std::once_flag flag;
666 std::call_once(flag, [] {
668 qWarning() <<
"setPosition with custom gstreamer pipelines can cause pipeline hangs. "
672 customPipeline.setPosition(pos);
675 qCDebug(qLcMediaPlayer) <<
"gst_play_seek" << pos;
676 gst_play_seek(m_gstPlay.get(), nanoseconds(pos).count());
678 if (mediaStatus() == QMediaPlayer::EndOfMedia)
679 mediaStatusChanged(QMediaPlayer::LoadedMedia);
681 positionChanged(pos);
686 if (isCustomSource()) {
688 customPipeline.setState(GST_STATE_PLAYING);
689 stateChanged(QMediaPlayer::PlayingState);
693 QMediaPlayer::PlaybackState currentState = state();
694 if (currentState == QMediaPlayer::PlayingState || !hasValidMedia())
697 if (currentState != QMediaPlayer::PausedState)
700 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
702 mediaStatusChanged(QMediaPlayer::LoadedMedia);
706 gst_play_seek(m_gstPlay.get(), m_pendingSeek->count());
707 m_pendingSeek = std::nullopt;
710 qCDebug(qLcMediaPlayer) <<
"gst_play_play";
712 gst_play_play(m_gstPlay.get());
713 stateChanged(QMediaPlayer::PlayingState);
718 if (isCustomSource()) {
720 customPipeline.setState(GST_STATE_PAUSED);
721 stateChanged(QMediaPlayer::PausedState);
725 if (state() == QMediaPlayer::PausedState || !hasMedia()
726 || m_resourceErrorState != ResourceErrorState::NoError)
731 qCDebug(qLcMediaPlayer) <<
"gst_play_pause";
732 gst_play_pause(m_gstPlay.get());
734 mediaStatusChanged(QMediaPlayer::BufferedMedia);
735 stateChanged(QMediaPlayer::PausedState);
740 if (isCustomSource()) {
741 customPipeline.setState(GST_STATE_READY);
742 stateChanged(QMediaPlayer::StoppedState);
747 using namespace std::chrono_literals;
748 if (state() == QMediaPlayer::StoppedState) {
749 if (position() != 0) {
751 positionChanged(0ms);
752 mediaStatusChanged(QMediaPlayer::LoadedMedia);
757 qCDebug(qLcMediaPlayer) <<
"gst_play_stop";
759 gst_play_stop(m_gstPlay.get());
761 stateChanged(QMediaPlayer::StoppedState);
763 mediaStatusChanged(QMediaPlayer::LoadedMedia);
764 positionChanged(0ms);
769 if (isCustomSource())
770 return customPipeline;
788 return PitchCompensationAvailability::AlwaysOn;
806 const gchar *typeName = g_type_name_from_instance((GTypeInstance *)source);
807 qCDebug(qLcMediaPlayer) <<
"Setting up source:" << typeName;
809 if (typeName == std::string_view(
"GstRTSPSrc")) {
813 int v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_LATENCY", &ok);
816 qCDebug(qLcMediaPlayer) <<
" -> setting source latency to:" << latency <<
"ms";
817 s.set(
"latency", latency);
820 v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_DROP_ON_LATENCY", &ok);
823 qCDebug(qLcMediaPlayer) <<
" -> setting drop-on-latency to:" << drop;
824 s.set(
"drop-on-latency", drop);
827 v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_DO_RETRANSMISSION", &ok);
830 qCDebug(qLcMediaPlayer) <<
" -> setting do-retransmission to:" << retrans;
831 s.set(
"do-retransmission", retrans);
837 using namespace Qt::Literals;
838 using namespace std::chrono;
839 using namespace std::chrono_literals;
842 cleanupCustomPipeline();
844 m_resourceErrorState = ResourceErrorState::NoError;
849 streamURL = qGstRegisterQIODevice(stream);
851 if (content.isEmpty() && !stream) {
852 mediaStatusChanged(QMediaPlayer::NoMedia);
853 resetStateForEmptyOrInvalidMedia();
857 if (isCustomSource()) {
858 setMediaCustomSource(content);
860 mediaStatusChanged(QMediaPlayer::LoadingMedia);
861 const QUrl &playUrl = stream ? streamURL : content;
865 bool mediaDiscovered = discover(playUrl);
866 if (!mediaDiscovered) {
867 m_resourceErrorState = ResourceErrorState::ErrorOccurred;
868 error(QMediaPlayer::Error::ResourceError, u"Resource cannot be discovered"_s);
869 mediaStatusChanged(QMediaPlayer::InvalidMedia);
870 resetStateForEmptyOrInvalidMedia();
874 positionChanged(0ms);
880 using namespace Qt::Literals;
881 using namespace std::chrono;
882 using namespace std::chrono_literals;
887 m_playbin.set(
"video-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
888 m_playbin.set(
"text-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
889 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
892 if (QGstElement sink = gstVideoOutput->gstreamerVideoSink()->gstSink())
897 customPipeline = QGstPipeline::create(
"customPipeline");
898 customPipeline.installMessageFilter(
this);
899 positionUpdateTimer = std::make_unique<QTimer>();
901 QObject::connect(positionUpdateTimer.get(), &QTimer::timeout,
this, [
this] {
902 Q_ASSERT(customPipeline);
903 auto position = customPipeline.position();
905 positionChanged(round<milliseconds>(position));
908 positionUpdateTimer->start(100ms);
910 QByteArray gstLaunchString =
911 content.toString(QUrl::RemoveScheme | QUrl::PrettyDecoded).toLatin1();
912 qCDebug(qLcMediaPlayer) <<
"generating" << gstLaunchString;
915 emit error(QMediaPlayer::ResourceError, u"Could not create custom pipeline"_s);
920 customPipeline.add(decoder);
923 qGstSafeCast<GstBin>(element.element()),
927 elementBin.addUnlinkedGhostPads(GstPadDirection::GST_PAD_SRC);
930 padAdded = decoder.onPadAdded<&QGstreamerMediaPlayer::decoderPadAddedCustomSource>(
this);
931 padRemoved = decoder.onPadRemoved<&QGstreamerMediaPlayer::decoderPadRemovedCustomSource>(
this);
933 customPipeline.setStateSync(GstState::GST_STATE_PAUSED);
935 auto srcPadVisitor = [](GstElement *element, GstPad *pad,
void *self) -> gboolean {
936 reinterpret_cast<QGstreamerMediaPlayer *>(self)->decoderPadAddedCustomSource(
937 QGstElement{ element, QGstElement::NeedsRef }, QGstPad{ pad, QGstPad::NeedsRef });
941 gst_element_foreach_pad(element.element(), srcPadVisitor,
this);
943 mediaStatusChanged(QMediaPlayer::LoadedMedia);
945 customPipeline.dumpGraph(
"setMediaCustomPipeline");
950 customPipeline.setStateSync(GST_STATE_NULL);
951 customPipeline.removeMessageFilter(
this);
953 for (QGstElement &sink : customPipelineSinks)
955 customPipeline.remove(sink);
957 positionUpdateTimer = {};
963 if (isCustomSource()) {
964 qWarning() <<
"QMediaPlayer::setAudioOutput not supported when using custom sources";
968 if (gstAudioOutput == output)
973 gstOutput->setAsync(
true);
977 m_playbin.set(
"audio-sink", gstAudioOutput->gstElement());
979 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
980 updateAudioTrackEnabled();
984 if (!qmediaplayerDestructorCalled)
985 m_playbin.finishStateChange();
995 if (isCustomSource()) {
996 qWarning() <<
"QMediaPlayer::setVideoSink not supported when using custom sources";
1000 auto *gstSink = sink ?
static_cast<
QGstreamerVideoSink *>(sink->platformVideoSink()) :
nullptr;
1002 gstSink->setAsync(
false);
1005 updateVideoTrackEnabled();
1007 if (sink && state() == QMediaPlayer::PausedState) {
1010 seekToCurrentPosition();
1016 QSpan<
const QMediaMetaData> tracks = m_trackMetaData[type];
1017 return tracks.size();
1022 QSpan<
const QMediaMetaData> tracks = m_trackMetaData[type];
1023 if (index < tracks.size())
1024 return tracks[index];
1030 return m_activeTrack[type];
1035 if (m_activeTrack[type] == index)
1038 int formerTrack = m_activeTrack[type];
1039 m_activeTrack[type] = index;
1042 case TrackType::VideoStream: {
1044 gst_play_set_video_track(m_gstPlay.get(), index);
1045 updateVideoTrackEnabled();
1046 updateNativeSizeOnVideoOutput();
1049 case TrackType::AudioStream: {
1051 gst_play_set_audio_track(m_gstPlay.get(), index);
1052 updateAudioTrackEnabled();
1055 case TrackType::SubtitleStream: {
1057 gst_play_set_subtitle_track(m_gstPlay.get(), index);
1058 gst_play_set_subtitle_track_enabled(m_gstPlay.get(), index != -1);
1065 if (formerTrack != -1 && index != -1)
1068 seekToCurrentPosition();
QGstStructureView at(int index) const
bool syncStateWithParent()
static QGstElement createFromPipelineDescription(const char *)
bool link(const QGstPad &sink) const
QGstCaps queryCaps() const
QGstElement gstElement() const
QGstreamerVideoSink * gstreamerVideoSink() const
void setNativeSize(QSize)
void setVideoSink(QVideoSink *sink)
std::optional< QGstreamerMediaPlayer::TrackType > toTrackType(const QGstCaps &caps)
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")