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/qgstutils_p.h>
10#include <common/qgst_discoverer_p.h>
11#include <common/qgst_play_p.h>
12#include <common/qgstpipeline_p.h>
13#include <common/qgstreameraudiooutput_p.h>
14#include <common/qgstreamermessage_p.h>
15#include <common/qgstreamermetadata_p.h>
16#include <common/qgstreamervideooutput_p.h>
17#include <common/qgstreamervideosink_p.h>
18#include <uri_handler/qgstreamer_qiodevice_handler_p.h>
19#include <qgstreamerformatinfo_p.h>
21#include <QtMultimedia/private/qthreadlocalrhi_p.h>
22#include <QtMultimedia/qaudiodevice.h>
23#include <QtConcurrent/qtconcurrentrun.h>
24#include <QtCore/qdebug.h>
25#include <QtMultimedia/private/qmultimedia_ranges_p.h>
26#include <QtCore/qiodevice.h>
27#include <QtCore/qloggingcategory.h>
28#include <QtCore/qthread.h>
29#include <QtCore/qurl.h>
30#include <QtCore/private/quniquehandle_p.h>
35namespace ranges = QtMultimediaPrivate::ranges;
41 using TrackType = QGstreamerMediaPlayer::TrackType;
43 QByteArrayView type = caps
.at(0
).name();
45 if (type.startsWith(
"video/x-raw"))
46 return TrackType::VideoStream;
47 if (type.startsWith(
"audio/x-raw"))
48 return TrackType::AudioStream;
49 if (type.startsWith(
"text"))
50 return TrackType::SubtitleStream;
59 return QtConcurrent::run([url = std::move(url)] {
60 QGst::QGstDiscoverer discoverer;
61 return discoverer.discover(url);
68 using namespace Qt::Literals;
69 using namespace std::chrono;
70 using namespace std::chrono_literals;
72 if (discoveryResult) {
74 gst_play_set_uri(m_gstPlay.get(), url.toEncoded().constData());
76 m_trackMetaData.fill({});
77 seekableChanged(discoveryResult->isSeekable);
78 if (discoveryResult->duration)
79 m_duration = round<milliseconds>(*discoveryResult->duration);
82 durationChanged(m_duration);
84 m_metaData = QGst::toContainerMetadata(*discoveryResult);
86 videoAvailableChanged(!discoveryResult->videoStreams.empty());
87 audioAvailableChanged(!discoveryResult->audioStreams.empty());
90 for (
const auto &videoInfo : discoveryResult->videoStreams) {
91 m_trackMetaData[0].emplace_back(QGst::toStreamMetadata(videoInfo));
92 QSize nativeSize = QGstUtils::qCalculateFrameSizeGStreamer(videoInfo.size,
93 videoInfo.pixelAspectRatio);
94 m_nativeSize.emplace_back(nativeSize);
96 for (
const auto &audioInfo : discoveryResult->audioStreams)
97 m_trackMetaData[1].emplace_back(QGst::toStreamMetadata(audioInfo));
98 for (
const auto &subtitleInfo : discoveryResult->subtitleStreams)
99 m_trackMetaData[2].emplace_back(QGst::toStreamMetadata(subtitleInfo));
101 using Key = QMediaMetaData::Key;
102 auto copyKeysToRootMetadata = [&](
const QMediaMetaData &reference, QSpan<
const Key> keys) {
103 for (QMediaMetaData::Key key : keys) {
104 QVariant referenceValue = reference.value(key);
105 if (referenceValue.isValid())
106 m_metaData.insert(key, referenceValue);
112 if (!m_trackMetaData[0].empty())
113 copyKeysToRootMetadata(m_trackMetaData[0].front(),
123 if (!m_trackMetaData[1].empty())
124 copyKeysToRootMetadata(m_trackMetaData[1].front(),
130 if (!m_url.isEmpty())
131 m_metaData.insert(QMediaMetaData::Key::Url, m_url);
133 qCDebug(qLcMediaPlayer) <<
"metadata:" << m_metaData;
134 qCDebug(qLcMediaPlayer) <<
"video metadata:" << m_trackMetaData[0];
135 qCDebug(qLcMediaPlayer) <<
"audio metadata:" << m_trackMetaData[1];
136 qCDebug(qLcMediaPlayer) <<
"subtitle metadata:" << m_trackMetaData[2];
141 isVideoAvailable() ? 0 : -1,
142 isAudioAvailable() ? 0 : -1,
145 updateVideoTrackEnabled();
146 updateAudioTrackEnabled();
147 updateNativeSizeOnVideoOutput();
148 positionChanged(0ms);
151 m_hasPendingMedia =
false;
152 if (m_requestedPlaybackState) {
153 switch (*m_requestedPlaybackState) {
154 case QMediaPlayer::PlayingState:
157 case QMediaPlayer::PausedState:
166 qCDebug(qLcMediaPlayer) <<
"Discovery error:" << discoveryResult.error();
167 m_resourceErrorState = ResourceErrorState::ErrorOccurred;
168 setInvalidMediaWithError(QMediaPlayer::Error::ResourceError,
169 u"Resource cannot be discovered"_s);
170 m_hasPendingMedia =
false;
171 resetStateForEmptyOrInvalidMedia();
181 qCDebug(qLcMediaPlayer) <<
"Added pad" << pad.name() <<
"from" << src.name();
185 std::optional<QGstreamerMediaPlayer::TrackType> type = toTrackType(caps);
189 customPipelinePads[*type] = pad;
197 customPipeline.add(sink);
199 customPipelineSinks[VideoStream] = sink;
206 customPipeline.add(sink);
208 customPipelineSinks[AudioStream] = sink;
212 case SubtitleStream: {
216 customPipeline.add(sink);
218 customPipelineSinks[SubtitleStream] = sink;
235 Q_ASSERT(thread()->isCurrentThread());
237 qCDebug(qLcMediaPlayer) <<
"Removed pad" << pad.name() <<
"from" << src.name() <<
"for stream"
240 auto found = ranges::find(customPipelinePads, pad);
241 if (found == customPipelinePads.end())
244 TrackType type = TrackType(found - customPipelinePads.cbegin());
249 case SubtitleStream: {
250 if (customPipelineSinks[VideoStream]) {
251 customPipeline.stopAndRemoveElements(customPipelineSinks[VideoStream]);
252 customPipelineSinks[VideoStream] = {};
264 using namespace std::chrono_literals;
265 m_nativeSize.clear();
267 bool metadataNeedsSignal = !m_metaData.isEmpty();
268 bool tracksNeedsSignal = ranges::any_of(m_trackMetaData, [](
const auto &container) {
269 return !container.empty();
273 m_trackMetaData.fill({});
275 seekableChanged(
false);
277 videoAvailableChanged(
false);
278 audioAvailableChanged(
false);
280 m_activeTrack.fill(-1);
282 if (metadataNeedsSignal)
284 if (tracksNeedsSignal)
290 int activeVideoTrack = activeTrack(TrackType::VideoStream);
291 bool hasVideoTrack = activeVideoTrack != -1;
293 QSize nativeSize = hasVideoTrack ? m_nativeSize[activeTrack(TrackType::VideoStream)] : QSize{};
295 QVariant orientation = hasVideoTrack
296 ? m_trackMetaData[TrackType::VideoStream][activeTrack(TrackType::VideoStream)].value(
297 QMediaMetaData::Key::Orientation)
300 if (orientation.isValid()) {
301 auto rotation = orientation.value<QtVideo::Rotation>();
302 gstVideoOutput->setRotation(rotation);
309 gst_play_seek(m_gstPlay.get(), gst_play_get_position(m_gstPlay.get()));
314 bool hasTrack = m_activeTrack[TrackType::VideoStream] != -1;
318 gst_play_set_video_track_enabled(m_gstPlay.get(), hasTrack && hasSink);
323 bool hasTrack = m_activeTrack[TrackType::AudioStream] != -1;
324 bool hasAudioOut = gstAudioOutput;
326 gst_play_set_audio_track_enabled(m_gstPlay.get(), hasTrack && hasAudioOut);
331 if (qFuzzyIsNull(newProgress - m_bufferProgress))
334 m_bufferProgress = newProgress;
335 bufferProgressChanged(m_bufferProgress);
340 auto handlers = std::initializer_list<QGObjectHandlerScopedConnection *>{ &sourceSetup };
341 for (QGObjectHandlerScopedConnection *handler : handlers)
342 handler->disconnect();
347 auto videoOutput = QGstreamerVideoOutput::create();
349 return q23::unexpected{ videoOutput.error() };
357 gst_play_config_set_seek_accurate(config, accurate);
361 QMediaPlayer *parent)
363 QPlatformMediaPlayer(parent),
364 gstVideoOutput(videoOutput),
366 gst_play_new(
nullptr),
367 QGstPlayHandle::HasRef,
370 GST_PIPELINE_CAST(gst_play_get_pipeline(m_gstPlay.get())),
371 QGstPipeline::HasRef,
374 QGstBusHandle{ gst_play_get_message_bus(m_gstPlay.get()), QGstBusHandle::HasRef },
388 constexpr bool useNxpWorkaround = std::is_same_v<
decltype(&gst_play_config_set_seek_accurate),
389 void (*)(GstPlay *, gboolean)>;
391 QUniqueGstStructureHandle config{
392 gst_play_get_config(m_gstPlay.get()),
395 if constexpr (useNxpWorkaround)
396 setSeekAccurate(m_gstPlay.get(),
true);
398 setSeekAccurate(config.get(),
true);
400 gst_play_set_config(m_gstPlay.get(), config.release());
402 QUniqueGstStructureHandle config{
403 gst_play_get_config(m_gstPlay.get()),
405 gst_play_config_set_seek_accurate(config.get(),
true);
406 gst_play_set_config(m_gstPlay.get(), config.release());
409 gstVideoOutput->setParent(
this);
413 m_gstVideoSink =
new QGstreamerRelayVideoSink(
this);
416 m_playbin.set(
"video-sink", gstVideoOutput->gstElement());
417 m_playbin.set(
"text-sink", gstVideoOutput->gstSubtitleElement());
418 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
420 m_gstPlayBus.installMessageFilter(
this);
423 gst_play_set_subtitle_track_enabled(m_gstPlay.get(),
false);
425 sourceSetup = m_playbin.connect(
"source-setup", GCallback(sourceSetupCallback),
this);
427 m_activeTrack.fill(-1);
435 cleanupCustomPipeline();
437 m_gstPlayBus.removeMessageFilter(
static_cast<QGstreamerBusMessageFilter *>(
this));
438 gst_bus_set_flushing(m_gstPlayBus.get(), TRUE);
439 gst_play_stop(m_gstPlay.get());
442 m_playbin.setStateSync(GST_STATE_NULL);
444 m_playbin.set(
"video-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
445 m_playbin.set(
"text-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
446 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
451 using namespace std::chrono;
453 positionChanged(round<milliseconds>(nanoseconds{
454 gst_play_get_position(m_gstPlay.get()),
460 if (isCustomSource()) {
461 constexpr bool traceBusMessages =
true;
462 if (traceBusMessages)
463 qCDebug(qLcMediaPlayer) <<
"received bus message:" << message;
465 switch (message.type()) {
466 case GST_MESSAGE_WARNING:
467 qWarning() <<
"received bus message:" << message;
470 case GST_MESSAGE_INFO:
471 qInfo() <<
"received bus message:" << message;
474 case GST_MESSAGE_ERROR:
475 qWarning() <<
"received bus message:" << message;
476 customPipeline.dumpPipelineGraph(
"GST_MESSAGE_ERROR");
479 case GST_MESSAGE_LATENCY:
480 customPipeline.recalculateLatency();
489 switch (message.type()) {
490 case GST_MESSAGE_APPLICATION:
491 if (gst_play_is_play_message(message.message()))
492 return processBusMessageApplication(message);
496 qCDebug(qLcMediaPlayer) << message;
506 using namespace std::chrono;
508 gst_play_message_parse_type(message.message(), &type);
512 case GST_PLAY_MESSAGE_URI_LOADED: {
513 mediaStatusChanged(QMediaPlayer::LoadedMedia);
517 case GST_PLAY_MESSAGE_POSITION_UPDATED: {
518 if (state() == QMediaPlayer::PlaybackState::PlayingState) {
520 constexpr bool usePayload =
false;
521 if constexpr (usePayload) {
522 GstClockTime position;
523 gst_play_message_parse_position_updated(message.message(), &position);
524 positionChanged(round<milliseconds>(nanoseconds{ position }));
526 GstClockTime position = gst_play_get_position(m_gstPlay.get());
527 positionChanged(round<milliseconds>(nanoseconds{ position }));
532 case GST_PLAY_MESSAGE_DURATION_CHANGED: {
533 GstClockTime duration;
534 gst_play_message_parse_duration_updated(message.message(), &duration);
535 milliseconds durationInMs = round<milliseconds>(nanoseconds{ duration });
536 durationChanged(durationInMs);
538 m_metaData.insert(QMediaMetaData::Duration,
int(durationInMs.count()));
543 case GST_PLAY_MESSAGE_BUFFERING: {
545 gst_play_message_parse_buffering_percent(message.message(), &percent);
546 updateBufferProgress(percent * 0.01f);
549 case GST_PLAY_MESSAGE_STATE_CHANGED: {
551 gst_play_message_parse_state_changed(message.message(), &state);
554 case GstPlayState::GST_PLAY_STATE_STOPPED:
555 if (stateChangeToSkip) {
556 qCDebug(qLcMediaPlayer) <<
" skipping StoppedState transition";
558 stateChangeToSkip -= 1;
561 stateChanged(QMediaPlayer::StoppedState);
562 updateBufferProgress(0);
565 case GstPlayState::GST_PLAY_STATE_PAUSED:
566 stateChanged(QMediaPlayer::PausedState);
567 mediaStatusChanged(QMediaPlayer::BufferedMedia);
569 updateBufferProgress(1);
571 case GstPlayState::GST_PLAY_STATE_BUFFERING:
572 mediaStatusChanged(QMediaPlayer::BufferingMedia);
574 case GstPlayState::GST_PLAY_STATE_PLAYING:
575 stateChanged(QMediaPlayer::PlayingState);
576 mediaStatusChanged(QMediaPlayer::BufferedMedia);
578 updateBufferProgress(1);
585 case GST_PLAY_MESSAGE_MEDIA_INFO_UPDATED: {
588 QUniqueGstPlayMediaInfoHandle info{};
589 gst_play_message_parse_media_info_updated(message.message(), &info);
591 seekableChanged(gst_play_media_info_is_seekable(info.get()));
593 const gchar *title = gst_play_media_info_get_title(info.get());
594 m_metaData.insert(QMediaMetaData::Title, QString::fromUtf8(title));
601 case GST_PLAY_MESSAGE_END_OF_STREAM: {
603 positionChanged(m_duration);
604 qCDebug(qLcMediaPlayer) <<
"EOS: restarting loop";
605 gst_play_play(m_gstPlay.get());
606 positionChanged(0ms);
610 stateChangeToSkip += 1;
612 qCDebug(qLcMediaPlayer) <<
"EOS: done";
613 positionChanged(m_duration);
614 mediaStatusChanged(QMediaPlayer::EndOfMedia);
615 stateChanged(QMediaPlayer::StoppedState);
621 case GST_PLAY_MESSAGE_ERROR:
622 case GST_PLAY_MESSAGE_WARNING:
623 case GST_PLAY_MESSAGE_VIDEO_DIMENSIONS_CHANGED:
624 case GST_PLAY_MESSAGE_VOLUME_CHANGED:
625 case GST_PLAY_MESSAGE_MUTE_CHANGED:
626 case GST_PLAY_MESSAGE_SEEK_DONE:
630 Q_UNREACHABLE_RETURN(
false);
636 return m_duration.count();
641 return !m_url.isEmpty() || m_stream;
649 switch (mediaStatus()) {
650 case QMediaPlayer::MediaStatus::NoMedia:
651 case QMediaPlayer::MediaStatus::InvalidMedia:
661 return m_bufferProgress;
666 return QMediaTimeRange();
671 return gst_play_get_rate(m_gstPlay.get());
676 if (isCustomSource()) {
677 static std::once_flag flag;
678 std::call_once(flag, [] {
681 <<
"setPlaybackRate with custom gstreamer pipelines can cause pipeline hangs. "
685 customPipeline.setPlaybackRate(rate);
689 if (rate == playbackRate())
692 qCDebug(qLcMediaPlayer) <<
"gst_play_set_rate" << rate;
693 gst_play_set_rate(m_gstPlay.get(), rate);
694 playbackRateChanged(rate);
699 std::chrono::milliseconds posInMs{ pos };
701 setPosition(posInMs);
706 using namespace std::chrono;
708 if (isCustomSource()) {
709 static std::once_flag flag;
710 std::call_once(flag, [] {
712 qWarning() <<
"setPosition with custom gstreamer pipelines can cause pipeline hangs. "
716 customPipeline.setPosition(pos);
720 if (m_hasPendingMedia) {
724 qCDebug(qLcMediaPlayer) <<
"gst_play_seek" << pos;
725 gst_play_seek(m_gstPlay.get(), nanoseconds(pos).count());
727 if (mediaStatus() == QMediaPlayer::EndOfMedia)
728 mediaStatusChanged(QMediaPlayer::LoadedMedia);
729 positionChanged(pos);
734 if (isCustomSource()) {
736 customPipeline.setState(GST_STATE_PLAYING);
737 stateChanged(QMediaPlayer::PlayingState);
741 if (m_hasPendingMedia) {
744 m_requestedPlaybackState = QMediaPlayer::PlayingState;
748 QMediaPlayer::PlaybackState currentState = state();
749 if (currentState == QMediaPlayer::PlayingState || !hasValidMedia())
752 if (currentState != QMediaPlayer::PausedState)
755 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
757 mediaStatusChanged(QMediaPlayer::LoadedMedia);
761 gst_play_seek(m_gstPlay.get(), m_pendingSeek->count());
762 m_pendingSeek = std::nullopt;
765 qCDebug(qLcMediaPlayer) <<
"gst_play_play";
767 gst_play_play(m_gstPlay.get());
768 stateChanged(QMediaPlayer::PlayingState);
773 if (isCustomSource()) {
775 customPipeline.setState(GST_STATE_PAUSED);
776 stateChanged(QMediaPlayer::PausedState);
780 if (m_hasPendingMedia) {
781 m_requestedPlaybackState = QMediaPlayer::PausedState;
785 if (state() == QMediaPlayer::PausedState || !hasMedia()
786 || m_resourceErrorState != ResourceErrorState::NoError)
791 qCDebug(qLcMediaPlayer) <<
"gst_play_pause";
792 gst_play_pause(m_gstPlay.get());
794 mediaStatusChanged(QMediaPlayer::BufferedMedia);
795 stateChanged(QMediaPlayer::PausedState);
800 if (isCustomSource()) {
801 customPipeline.setState(GST_STATE_READY);
802 stateChanged(QMediaPlayer::StoppedState);
807 if (m_hasPendingMedia) {
808 m_requestedPlaybackState = QMediaPlayer::StoppedState;
812 using namespace std::chrono_literals;
813 if (state() == QMediaPlayer::StoppedState) {
814 if (position() != 0) {
816 positionChanged(0ms);
817 mediaStatusChanged(QMediaPlayer::LoadedMedia);
822 qCDebug(qLcMediaPlayer) <<
"gst_play_stop";
824 gst_play_stop(m_gstPlay.get());
826 stateChanged(QMediaPlayer::StoppedState);
828 mediaStatusChanged(QMediaPlayer::LoadedMedia);
829 positionChanged(0ms);
834 if (isCustomSource())
835 return customPipeline;
853 return PitchCompensationAvailability::AlwaysOn;
871 const gchar *typeName = g_type_name_from_instance((GTypeInstance *)source);
872 qCDebug(qLcMediaPlayer) <<
"Setting up source:" << typeName;
874 if (typeName == std::string_view(
"GstRTSPSrc")) {
878 int v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_LATENCY", &ok);
881 qCDebug(qLcMediaPlayer) <<
" -> setting source latency to:" << latency <<
"ms";
882 s.set(
"latency", latency);
885 v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_DROP_ON_LATENCY", &ok);
888 qCDebug(qLcMediaPlayer) <<
" -> setting drop-on-latency to:" << drop;
889 s.set(
"drop-on-latency", drop);
892 v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_DO_RETRANSMISSION", &ok);
895 qCDebug(qLcMediaPlayer) <<
" -> setting do-retransmission to:" << retrans;
896 s.set(
"do-retransmission", retrans);
903 cleanupCustomPipeline();
905 m_resourceErrorState = ResourceErrorState::NoError;
910 m_discoveryHandler.cancel();
911 m_discoverFuture.cancel();
915 streamURL = qGstRegisterQIODevice(stream);
917 if (content.isEmpty() && !stream) {
918 mediaStatusChanged(QMediaPlayer::NoMedia);
919 resetStateForEmptyOrInvalidMedia();
923 if (isCustomSource()) {
924 setMediaCustomSource(content);
926 mediaStatusChanged(QMediaPlayer::LoadingMedia);
927 m_hasPendingMedia =
true;
928 m_requestedPlaybackState = std::nullopt;
930 const QUrl &playUrl = stream ? streamURL : content;
931 m_discoverFuture = discover(playUrl);
934 m_discoverFuture.then(
this,[
this, playUrl](
const DiscoverResult &result) {
935 handleDiscoverResult(result, playUrl);
942 using namespace Qt::Literals;
943 using namespace std::chrono;
944 using namespace std::chrono_literals;
949 m_playbin.set(
"video-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
950 m_playbin.set(
"text-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
951 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
954 if (QGstElement sink = gstVideoOutput->gstreamerVideoSink()->gstSink())
959 customPipeline = QGstPipeline::create(
"customPipeline");
960 customPipeline.installMessageFilter(
this);
961 positionUpdateTimer = std::make_unique<QTimer>();
963 QObject::connect(positionUpdateTimer.get(), &QTimer::timeout,
this, [
this] {
964 Q_ASSERT(customPipeline);
965 auto position = customPipeline.position();
967 positionChanged(round<milliseconds>(position));
970 positionUpdateTimer->start(100ms);
972 QByteArray gstLaunchString =
973 content.toString(QUrl::RemoveScheme | QUrl::PrettyDecoded).toLatin1();
974 qCDebug(qLcMediaPlayer) <<
"generating" << gstLaunchString;
977 emit error(QMediaPlayer::ResourceError, u"Could not create custom pipeline"_s);
982 customPipeline.add(decoder);
985 qGstSafeCast<GstBin>(element.element()),
989 elementBin.addUnlinkedGhostPads(GstPadDirection::GST_PAD_SRC);
992 padAdded = decoder.onPadAdded<&QGstreamerMediaPlayer::decoderPadAddedCustomSource>(
this);
993 padRemoved = decoder.onPadRemoved<&QGstreamerMediaPlayer::decoderPadRemovedCustomSource>(
this);
995 customPipeline.setStateSync(GstState::GST_STATE_PAUSED);
997 auto srcPadVisitor = [](GstElement *element, GstPad *pad,
void *self) -> gboolean {
998 reinterpret_cast<QGstreamerMediaPlayer *>(self)->decoderPadAddedCustomSource(
999 QGstElement{ element, QGstElement::NeedsRef }, QGstPad{ pad, QGstPad::NeedsRef });
1003 gst_element_foreach_pad(element.element(), srcPadVisitor,
this);
1005 mediaStatusChanged(QMediaPlayer::LoadedMedia);
1007 customPipeline.dumpGraph(
"setMediaCustomPipeline");
1012 customPipeline.setStateSync(GST_STATE_NULL);
1013 customPipeline.removeMessageFilter(
this);
1015 for (QGstElement &sink : customPipelineSinks)
1017 customPipeline.remove(sink);
1019 positionUpdateTimer = {};
1020 customPipeline = {};
1025 if (isCustomSource()) {
1026 qWarning() <<
"QMediaPlayer::setAudioOutput not supported when using custom sources";
1030 if (gstAudioOutput == output)
1035 gstOutput->setAsync(
true);
1039 m_playbin.set(
"audio-sink", gstAudioOutput->gstElement());
1041 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
1042 updateAudioTrackEnabled();
1046 if (!qmediaplayerDestructorCalled)
1047 m_playbin.finishStateChange();
1065 Q_ASSERT(pluggableSink);
1071 QSpan<
const QMediaMetaData> tracks = m_trackMetaData[type];
1072 return tracks.size();
1077 QSpan<
const QMediaMetaData> tracks = m_trackMetaData[type];
1078 if (index < tracks.size())
1079 return tracks[index];
1085 return m_activeTrack[type];
1090 if (m_activeTrack[type] == index)
1093 int formerTrack = m_activeTrack[type];
1094 m_activeTrack[type] = index;
1097 case TrackType::VideoStream: {
1099 gst_play_set_video_track(m_gstPlay.get(), index);
1100 updateVideoTrackEnabled();
1101 updateNativeSizeOnVideoOutput();
1104 case TrackType::AudioStream: {
1106 gst_play_set_audio_track(m_gstPlay.get(), index);
1107 updateAudioTrackEnabled();
1110 case TrackType::SubtitleStream: {
1112 gst_play_set_subtitle_track(m_gstPlay.get(), index);
1113 gst_play_set_subtitle_track_enabled(m_gstPlay.get(), index != -1);
1120 if (formerTrack != -1 && index != -1)
1123 seekToCurrentPosition();
QGstStructureView at(int index) const
bool syncStateWithParent()
static QGstElement createFromPipelineDescription(const char *)
bool link(const QGstPad &sink) const
QGstCaps queryCaps() const
QGString streamId() 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)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
QGstPlayMessageAdaptor(const QGstreamerMessage &m)