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/private/qthreadlocalrhi_p.h>
21#include <QtMultimedia/qaudiodevice.h>
22#include <QtCore/qdebug.h>
23#include <QtCore/qiodevice.h>
24#include <QtCore/qloggingcategory.h>
25#include <QtCore/qthread.h>
26#include <QtCore/qurl.h>
27#include <QtCore/private/quniquehandle_p.h>
37 using TrackType = QGstreamerMediaPlayer::TrackType;
39 QByteArrayView type = caps
.at(0
).name();
41 if (type.startsWith(
"video/x-raw"))
42 return TrackType::VideoStream;
43 if (type.startsWith(
"audio/x-raw"))
44 return TrackType::AudioStream;
45 if (type.startsWith(
"text"))
46 return TrackType::SubtitleStream;
57 using namespace std::chrono;
58 using namespace std::chrono_literals;
60 auto discoveryResult = discoverer.discover(url);
61 if (discoveryResult) {
63 gst_play_set_uri(m_gstPlay.get(), url.toEncoded().constData());
65 m_trackMetaData.fill({});
66 seekableChanged(discoveryResult->isSeekable);
67 if (discoveryResult->duration)
68 m_duration = round<milliseconds>(*discoveryResult->duration);
71 durationChanged(m_duration);
73 m_metaData = QGst::toContainerMetadata(*discoveryResult);
75 videoAvailableChanged(!discoveryResult->videoStreams.empty());
76 audioAvailableChanged(!discoveryResult->audioStreams.empty());
79 for (
const auto &videoInfo : discoveryResult->videoStreams) {
80 m_trackMetaData[0].emplace_back(QGst::toStreamMetadata(videoInfo));
81 QGstStructureView structure = videoInfo.caps.at(0);
82 m_nativeSize.emplace_back(structure.nativeSize());
84 for (
const auto &audioInfo : discoveryResult->audioStreams)
85 m_trackMetaData[1].emplace_back(QGst::toStreamMetadata(audioInfo));
86 for (
const auto &subtitleInfo : discoveryResult->subtitleStreams)
87 m_trackMetaData[2].emplace_back(QGst::toStreamMetadata(subtitleInfo));
89 using Key = QMediaMetaData::Key;
90 auto copyKeysToRootMetadata = [&](
const QMediaMetaData &reference, QSpan<
const Key> keys) {
91 for (QMediaMetaData::Key key : keys) {
92 QVariant referenceValue = reference.value(key);
93 if (referenceValue.isValid())
94 m_metaData.insert(key, referenceValue);
100 if (!m_trackMetaData[0].empty())
101 copyKeysToRootMetadata(m_trackMetaData[0].front(),
111 if (!m_trackMetaData[1].empty())
112 copyKeysToRootMetadata(m_trackMetaData[1].front(),
118 if (!m_url.isEmpty())
119 m_metaData.insert(QMediaMetaData::Key::Url, m_url);
121 qCDebug(qLcMediaPlayer) <<
"metadata:" << m_metaData;
122 qCDebug(qLcMediaPlayer) <<
"video metadata:" << m_trackMetaData[0];
123 qCDebug(qLcMediaPlayer) <<
"audio metadata:" << m_trackMetaData[1];
124 qCDebug(qLcMediaPlayer) <<
"subtitle metadata:" << m_trackMetaData[2];
129 isVideoAvailable() ? 0 : -1,
130 isAudioAvailable() ? 0 : -1,
133 updateVideoTrackEnabled();
134 updateAudioTrackEnabled();
135 updateNativeSizeOnVideoOutput();
138 return bool(discoveryResult);
147 qCDebug(qLcMediaPlayer) <<
"Added pad" << pad.name() <<
"from" << src.name();
151 std::optional<QGstreamerMediaPlayer::TrackType> type = toTrackType(caps);
155 customPipelinePads[*type] = pad;
163 customPipeline.add(sink);
165 customPipelineSinks[VideoStream] = sink;
172 customPipeline.add(sink);
174 customPipelineSinks[AudioStream] = sink;
178 case SubtitleStream: {
182 customPipeline.add(sink);
184 customPipelineSinks[SubtitleStream] = sink;
201 Q_ASSERT(thread()->isCurrentThread());
203 qCDebug(qLcMediaPlayer) <<
"Removed pad" << pad.name() <<
"from" << src.name() <<
"for stream"
206 auto found = std::find(customPipelinePads.begin(), customPipelinePads.end(), pad);
207 if (found == customPipelinePads.end())
210 TrackType type = TrackType(std::distance(customPipelinePads.begin(), found));
215 case SubtitleStream: {
216 if (customPipelineSinks[VideoStream]) {
217 customPipeline.stopAndRemoveElements(customPipelineSinks[VideoStream]);
218 customPipelineSinks[VideoStream] = {};
230 using namespace std::chrono_literals;
231 m_nativeSize.clear();
233 bool metadataNeedsSignal = !m_metaData.isEmpty();
234 bool tracksNeedsSignal =
235 std::any_of(m_trackMetaData.begin(), m_trackMetaData.end(), [](
const auto &container) {
236 return !container.empty();
240 m_trackMetaData.fill({});
242 seekableChanged(
false);
244 videoAvailableChanged(
false);
245 audioAvailableChanged(
false);
247 m_activeTrack.fill(-1);
249 if (metadataNeedsSignal)
251 if (tracksNeedsSignal)
257 int activeVideoTrack = activeTrack(TrackType::VideoStream);
258 bool hasVideoTrack = activeVideoTrack != -1;
260 QSize nativeSize = hasVideoTrack ? m_nativeSize[activeTrack(TrackType::VideoStream)] : QSize{};
262 QVariant orientation = hasVideoTrack
263 ? m_trackMetaData[TrackType::VideoStream][activeTrack(TrackType::VideoStream)].value(
264 QMediaMetaData::Key::Orientation)
267 if (orientation.isValid()) {
268 auto rotation = orientation.value<QtVideo::Rotation>();
269 gstVideoOutput->setRotation(rotation);
276 gst_play_seek(m_gstPlay.get(), gst_play_get_position(m_gstPlay.get()));
281 bool hasTrack = m_activeTrack[TrackType::VideoStream] != -1;
285 gst_play_set_video_track_enabled(m_gstPlay.get(), hasTrack && hasSink);
290 bool hasTrack = m_activeTrack[TrackType::AudioStream] != -1;
291 bool hasAudioOut = gstAudioOutput;
293 gst_play_set_audio_track_enabled(m_gstPlay.get(), hasTrack && hasAudioOut);
298 if (qFuzzyIsNull(newProgress - m_bufferProgress))
301 m_bufferProgress = newProgress;
302 bufferProgressChanged(m_bufferProgress);
307 auto handlers = std::initializer_list<QGObjectHandlerScopedConnection *>{ &sourceSetup };
308 for (QGObjectHandlerScopedConnection *handler : handlers)
309 handler->disconnect();
314 auto videoOutput = QGstreamerVideoOutput::create();
316 return q23::unexpected{ videoOutput.error() };
324 gst_play_config_set_seek_accurate(config, accurate);
328 QMediaPlayer *parent)
330 QPlatformMediaPlayer(parent),
331 gstVideoOutput(videoOutput),
333 gst_play_new(
nullptr),
334 QGstPlayHandle::HasRef,
337 GST_PIPELINE_CAST(gst_play_get_pipeline(m_gstPlay.get())),
338 QGstPipeline::HasRef,
341 QGstBusHandle{ gst_play_get_message_bus(m_gstPlay.get()), QGstBusHandle::HasRef },
350 constexpr bool useNxpWorkaround = std::is_same_v<
decltype(&gst_play_config_set_seek_accurate),
351 void (*)(GstPlay *, gboolean)>;
353 QUniqueGstStructureHandle config{
354 gst_play_get_config(m_gstPlay.get()),
357 if constexpr (useNxpWorkaround)
358 setSeekAccurate(m_gstPlay.get(),
true);
360 setSeekAccurate(config.get(),
true);
362 gst_play_set_config(m_gstPlay.get(), config.release());
364 QUniqueGstStructureHandle config{
365 gst_play_get_config(m_gstPlay.get()),
367 gst_play_config_set_seek_accurate(config.get(),
true);
368 gst_play_set_config(m_gstPlay.get(), config.release());
371 gstVideoOutput->setParent(
this);
375 m_gstVideoSink =
new QGstreamerRelayVideoSink(
this);
376 m_gstVideoSink->setRhi(qEnsureThreadLocalRhi());
379 m_playbin.set(
"video-sink", gstVideoOutput->gstElement());
380 m_playbin.set(
"text-sink", gstVideoOutput->gstSubtitleElement());
381 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
383 m_gstPlayBus.installMessageFilter(
this);
386 gst_play_set_subtitle_track_enabled(m_gstPlay.get(),
false);
388 sourceSetup = m_playbin.connect(
"source-setup", GCallback(sourceSetupCallback),
this);
390 m_activeTrack.fill(-1);
398 cleanupCustomPipeline();
400 m_gstPlayBus.removeMessageFilter(
static_cast<QGstreamerBusMessageFilter *>(
this));
401 gst_bus_set_flushing(m_gstPlayBus.get(), TRUE);
402 gst_play_stop(m_gstPlay.get());
405 m_playbin.setStateSync(GST_STATE_NULL);
407 m_playbin.set(
"video-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
408 m_playbin.set(
"text-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
409 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
414 using namespace std::chrono;
416 positionChanged(round<milliseconds>(nanoseconds{
417 gst_play_get_position(m_gstPlay.get()),
423 if (isCustomSource()) {
424 constexpr bool traceBusMessages =
true;
425 if (traceBusMessages)
426 qCDebug(qLcMediaPlayer) <<
"received bus message:" << message;
428 switch (message.type()) {
429 case GST_MESSAGE_WARNING:
430 qWarning() <<
"received bus message:" << message;
433 case GST_MESSAGE_INFO:
434 qInfo() <<
"received bus message:" << message;
437 case GST_MESSAGE_ERROR:
438 qWarning() <<
"received bus message:" << message;
439 customPipeline.dumpPipelineGraph(
"GST_MESSAGE_ERROR");
442 case GST_MESSAGE_LATENCY:
443 customPipeline.recalculateLatency();
452 switch (message.type()) {
453 case GST_MESSAGE_APPLICATION:
454 if (gst_play_is_play_message(message.message()))
455 return processBusMessageApplication(message);
459 qCDebug(qLcMediaPlayer) << message;
469 using namespace std::chrono;
471 gst_play_message_parse_type(message.message(), &type);
472 qCDebug(qLcMediaPlayer) << QGstPlayMessageAdaptor{ message };
475 case GST_PLAY_MESSAGE_URI_LOADED: {
476 mediaStatusChanged(QMediaPlayer::LoadedMedia);
480 case GST_PLAY_MESSAGE_POSITION_UPDATED: {
481 if (state() == QMediaPlayer::PlaybackState::PlayingState) {
483 constexpr bool usePayload =
false;
484 if constexpr (usePayload) {
485 GstClockTime position;
486 gst_play_message_parse_position_updated(message.message(), &position);
487 positionChanged(round<milliseconds>(nanoseconds{ position }));
489 GstClockTime position = gst_play_get_position(m_gstPlay.get());
490 positionChanged(round<milliseconds>(nanoseconds{ position }));
495 case GST_PLAY_MESSAGE_DURATION_CHANGED: {
496 GstClockTime duration;
497 gst_play_message_parse_duration_updated(message.message(), &duration);
498 milliseconds durationInMs = round<milliseconds>(nanoseconds{ duration });
499 durationChanged(durationInMs);
501 m_metaData.insert(QMediaMetaData::Duration,
int(durationInMs.count()));
506 case GST_PLAY_MESSAGE_BUFFERING: {
508 gst_play_message_parse_buffering_percent(message.message(), &percent);
509 updateBufferProgress(percent * 0.01f);
512 case GST_PLAY_MESSAGE_STATE_CHANGED: {
514 gst_play_message_parse_state_changed(message.message(), &state);
517 case GstPlayState::GST_PLAY_STATE_STOPPED:
518 if (stateChangeToSkip) {
519 qCDebug(qLcMediaPlayer) <<
" skipping StoppedState transition";
521 stateChangeToSkip -= 1;
524 stateChanged(QMediaPlayer::StoppedState);
525 updateBufferProgress(0);
528 case GstPlayState::GST_PLAY_STATE_PAUSED:
529 stateChanged(QMediaPlayer::PausedState);
530 mediaStatusChanged(QMediaPlayer::BufferedMedia);
532 updateBufferProgress(1);
534 case GstPlayState::GST_PLAY_STATE_BUFFERING:
535 mediaStatusChanged(QMediaPlayer::BufferingMedia);
537 case GstPlayState::GST_PLAY_STATE_PLAYING:
538 stateChanged(QMediaPlayer::PlayingState);
539 mediaStatusChanged(QMediaPlayer::BufferedMedia);
541 updateBufferProgress(1);
548 case GST_PLAY_MESSAGE_MEDIA_INFO_UPDATED: {
551 QUniqueGstPlayMediaInfoHandle info{};
552 gst_play_message_parse_media_info_updated(message.message(), &info);
554 seekableChanged(gst_play_media_info_is_seekable(info.get()));
556 const gchar *title = gst_play_media_info_get_title(info.get());
557 m_metaData.insert(QMediaMetaData::Title, QString::fromUtf8(title));
564 case GST_PLAY_MESSAGE_END_OF_STREAM: {
566 positionChanged(m_duration);
567 qCDebug(qLcMediaPlayer) <<
"EOS: restarting loop";
568 gst_play_play(m_gstPlay.get());
569 positionChanged(0ms);
573 stateChangeToSkip += 1;
575 qCDebug(qLcMediaPlayer) <<
"EOS: done";
576 positionChanged(m_duration);
577 mediaStatusChanged(QMediaPlayer::EndOfMedia);
578 stateChanged(QMediaPlayer::StoppedState);
584 case GST_PLAY_MESSAGE_ERROR:
585 case GST_PLAY_MESSAGE_WARNING:
586 case GST_PLAY_MESSAGE_VIDEO_DIMENSIONS_CHANGED:
587 case GST_PLAY_MESSAGE_VOLUME_CHANGED:
588 case GST_PLAY_MESSAGE_MUTE_CHANGED:
589 case GST_PLAY_MESSAGE_SEEK_DONE:
593 Q_UNREACHABLE_RETURN(
false);
599 return m_duration.count();
604 return !m_url.isEmpty() || m_stream;
612 switch (mediaStatus()) {
613 case QMediaPlayer::MediaStatus::NoMedia:
614 case QMediaPlayer::MediaStatus::InvalidMedia:
624 return m_bufferProgress;
629 return QMediaTimeRange();
634 return gst_play_get_rate(m_gstPlay.get());
639 if (isCustomSource()) {
640 static std::once_flag flag;
641 std::call_once(flag, [] {
644 <<
"setPlaybackRate with custom gstreamer pipelines can cause pipeline hangs. "
648 customPipeline.setPlaybackRate(rate);
652 if (rate == playbackRate())
655 qCDebug(qLcMediaPlayer) <<
"gst_play_set_rate" << rate;
656 gst_play_set_rate(m_gstPlay.get(), rate);
657 playbackRateChanged(rate);
662 std::chrono::milliseconds posInMs{ pos };
664 setPosition(posInMs);
669 using namespace std::chrono;
671 if (isCustomSource()) {
672 static std::once_flag flag;
673 std::call_once(flag, [] {
675 qWarning() <<
"setPosition with custom gstreamer pipelines can cause pipeline hangs. "
679 customPipeline.setPosition(pos);
682 qCDebug(qLcMediaPlayer) <<
"gst_play_seek" << pos;
683 gst_play_seek(m_gstPlay.get(), nanoseconds(pos).count());
685 if (mediaStatus() == QMediaPlayer::EndOfMedia)
686 mediaStatusChanged(QMediaPlayer::LoadedMedia);
688 positionChanged(pos);
693 if (isCustomSource()) {
695 customPipeline.setState(GST_STATE_PLAYING);
696 stateChanged(QMediaPlayer::PlayingState);
700 QMediaPlayer::PlaybackState currentState = state();
701 if (currentState == QMediaPlayer::PlayingState || !hasValidMedia())
704 if (currentState != QMediaPlayer::PausedState)
707 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
709 mediaStatusChanged(QMediaPlayer::LoadedMedia);
713 gst_play_seek(m_gstPlay.get(), m_pendingSeek->count());
714 m_pendingSeek = std::nullopt;
717 qCDebug(qLcMediaPlayer) <<
"gst_play_play";
719 gst_play_play(m_gstPlay.get());
720 stateChanged(QMediaPlayer::PlayingState);
725 if (isCustomSource()) {
727 customPipeline.setState(GST_STATE_PAUSED);
728 stateChanged(QMediaPlayer::PausedState);
732 if (state() == QMediaPlayer::PausedState || !hasMedia()
733 || m_resourceErrorState != ResourceErrorState::NoError)
738 qCDebug(qLcMediaPlayer) <<
"gst_play_pause";
739 gst_play_pause(m_gstPlay.get());
741 mediaStatusChanged(QMediaPlayer::BufferedMedia);
742 stateChanged(QMediaPlayer::PausedState);
747 if (isCustomSource()) {
748 customPipeline.setState(GST_STATE_READY);
749 stateChanged(QMediaPlayer::StoppedState);
754 using namespace std::chrono_literals;
755 if (state() == QMediaPlayer::StoppedState) {
756 if (position() != 0) {
758 positionChanged(0ms);
759 mediaStatusChanged(QMediaPlayer::LoadedMedia);
764 qCDebug(qLcMediaPlayer) <<
"gst_play_stop";
766 gst_play_stop(m_gstPlay.get());
768 stateChanged(QMediaPlayer::StoppedState);
770 mediaStatusChanged(QMediaPlayer::LoadedMedia);
771 positionChanged(0ms);
776 if (isCustomSource())
777 return customPipeline;
795 return PitchCompensationAvailability::AlwaysOn;
813 const gchar *typeName = g_type_name_from_instance((GTypeInstance *)source);
814 qCDebug(qLcMediaPlayer) <<
"Setting up source:" << typeName;
816 if (typeName == std::string_view(
"GstRTSPSrc")) {
820 int v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_LATENCY", &ok);
823 qCDebug(qLcMediaPlayer) <<
" -> setting source latency to:" << latency <<
"ms";
824 s.set(
"latency", latency);
827 v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_DROP_ON_LATENCY", &ok);
830 qCDebug(qLcMediaPlayer) <<
" -> setting drop-on-latency to:" << drop;
831 s.set(
"drop-on-latency", drop);
834 v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_DO_RETRANSMISSION", &ok);
837 qCDebug(qLcMediaPlayer) <<
" -> setting do-retransmission to:" << retrans;
838 s.set(
"do-retransmission", retrans);
844 using namespace Qt::Literals;
845 using namespace std::chrono;
846 using namespace std::chrono_literals;
849 cleanupCustomPipeline();
851 m_resourceErrorState = ResourceErrorState::NoError;
856 streamURL = qGstRegisterQIODevice(stream);
858 if (content.isEmpty() && !stream) {
859 mediaStatusChanged(QMediaPlayer::NoMedia);
860 resetStateForEmptyOrInvalidMedia();
864 if (isCustomSource()) {
865 setMediaCustomSource(content);
867 mediaStatusChanged(QMediaPlayer::LoadingMedia);
868 const QUrl &playUrl = stream ? streamURL : content;
872 bool mediaDiscovered = discover(playUrl);
873 if (!mediaDiscovered) {
874 m_resourceErrorState = ResourceErrorState::ErrorOccurred;
875 error(QMediaPlayer::Error::ResourceError, u"Resource cannot be discovered"_s);
876 mediaStatusChanged(QMediaPlayer::InvalidMedia);
877 resetStateForEmptyOrInvalidMedia();
881 positionChanged(0ms);
887 using namespace Qt::Literals;
888 using namespace std::chrono;
889 using namespace std::chrono_literals;
894 m_playbin.set(
"video-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
895 m_playbin.set(
"text-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
896 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
899 if (QGstElement sink = gstVideoOutput->gstreamerVideoSink()->gstSink())
904 customPipeline = QGstPipeline::create(
"customPipeline");
905 customPipeline.installMessageFilter(
this);
906 positionUpdateTimer = std::make_unique<QTimer>();
908 QObject::connect(positionUpdateTimer.get(), &QTimer::timeout,
this, [
this] {
909 Q_ASSERT(customPipeline);
910 auto position = customPipeline.position();
912 positionChanged(round<milliseconds>(position));
915 positionUpdateTimer->start(100ms);
917 QByteArray gstLaunchString =
918 content.toString(QUrl::RemoveScheme | QUrl::PrettyDecoded).toLatin1();
919 qCDebug(qLcMediaPlayer) <<
"generating" << gstLaunchString;
922 emit error(QMediaPlayer::ResourceError, u"Could not create custom pipeline"_s);
927 customPipeline.add(decoder);
930 qGstSafeCast<GstBin>(element.element()),
934 elementBin.addUnlinkedGhostPads(GstPadDirection::GST_PAD_SRC);
937 padAdded = decoder.onPadAdded<&QGstreamerMediaPlayer::decoderPadAddedCustomSource>(
this);
938 padRemoved = decoder.onPadRemoved<&QGstreamerMediaPlayer::decoderPadRemovedCustomSource>(
this);
940 customPipeline.setStateSync(GstState::GST_STATE_PAUSED);
942 auto srcPadVisitor = [](GstElement *element, GstPad *pad,
void *self) -> gboolean {
943 reinterpret_cast<QGstreamerMediaPlayer *>(self)->decoderPadAddedCustomSource(
944 QGstElement{ element, QGstElement::NeedsRef }, QGstPad{ pad, QGstPad::NeedsRef });
948 gst_element_foreach_pad(element.element(), srcPadVisitor,
this);
950 mediaStatusChanged(QMediaPlayer::LoadedMedia);
952 customPipeline.dumpGraph(
"setMediaCustomPipeline");
957 customPipeline.setStateSync(GST_STATE_NULL);
958 customPipeline.removeMessageFilter(
this);
960 for (QGstElement &sink : customPipelineSinks)
962 customPipeline.remove(sink);
964 positionUpdateTimer = {};
970 if (isCustomSource()) {
971 qWarning() <<
"QMediaPlayer::setAudioOutput not supported when using custom sources";
975 if (gstAudioOutput == output)
980 gstOutput->setAsync(
true);
984 m_playbin.set(
"audio-sink", gstAudioOutput->gstElement());
986 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
987 updateAudioTrackEnabled();
991 if (!qmediaplayerDestructorCalled)
992 m_playbin.finishStateChange();
1010 Q_ASSERT(pluggableSink);
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
void connectPluggableVideoSink(QGstreamerPluggableVideoSink *pluggableSink)
void disconnectPluggableVideoSink()
void setVideoSink(QGstreamerRelayVideoSink *sink)
QGstreamerRelayVideoSink * gstreamerVideoSink() const
void setNativeSize(QSize)
std::optional< QGstreamerMediaPlayer::TrackType > toTrackType(const QGstCaps &caps)
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")