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 <QtCore/qiodevice.h>
26#include <QtCore/qloggingcategory.h>
27#include <QtCore/qthread.h>
28#include <QtCore/qurl.h>
29#include <QtCore/private/quniquehandle_p.h>
39 using TrackType = QGstreamerMediaPlayer::TrackType;
41 QByteArrayView type = caps
.at(0
).name();
43 if (type.startsWith(
"video/x-raw"))
44 return TrackType::VideoStream;
45 if (type.startsWith(
"audio/x-raw"))
46 return TrackType::AudioStream;
47 if (type.startsWith(
"text"))
48 return TrackType::SubtitleStream;
57 return QtConcurrent::run([url = std::move(url)] {
58 QGst::QGstDiscoverer discoverer;
59 return discoverer.discover(url);
66 using namespace Qt::Literals;
67 using namespace std::chrono;
68 using namespace std::chrono_literals;
70 if (discoveryResult) {
72 gst_play_set_uri(m_gstPlay.get(), url.toEncoded().constData());
74 m_trackMetaData.fill({});
75 seekableChanged(discoveryResult->isSeekable);
76 if (discoveryResult->duration)
77 m_duration = round<milliseconds>(*discoveryResult->duration);
80 durationChanged(m_duration);
82 m_metaData = QGst::toContainerMetadata(*discoveryResult);
84 videoAvailableChanged(!discoveryResult->videoStreams.empty());
85 audioAvailableChanged(!discoveryResult->audioStreams.empty());
88 for (
const auto &videoInfo : discoveryResult->videoStreams) {
89 m_trackMetaData[0].emplace_back(QGst::toStreamMetadata(videoInfo));
90 QSize nativeSize = QGstUtils::qCalculateFrameSizeGStreamer(videoInfo.size,
91 videoInfo.pixelAspectRatio);
92 m_nativeSize.emplace_back(nativeSize);
94 for (
const auto &audioInfo : discoveryResult->audioStreams)
95 m_trackMetaData[1].emplace_back(QGst::toStreamMetadata(audioInfo));
96 for (
const auto &subtitleInfo : discoveryResult->subtitleStreams)
97 m_trackMetaData[2].emplace_back(QGst::toStreamMetadata(subtitleInfo));
99 using Key = QMediaMetaData::Key;
100 auto copyKeysToRootMetadata = [&](
const QMediaMetaData &reference, QSpan<
const Key> keys) {
101 for (QMediaMetaData::Key key : keys) {
102 QVariant referenceValue = reference.value(key);
103 if (referenceValue.isValid())
104 m_metaData.insert(key, referenceValue);
110 if (!m_trackMetaData[0].empty())
111 copyKeysToRootMetadata(m_trackMetaData[0].front(),
121 if (!m_trackMetaData[1].empty())
122 copyKeysToRootMetadata(m_trackMetaData[1].front(),
128 if (!m_url.isEmpty())
129 m_metaData.insert(QMediaMetaData::Key::Url, m_url);
131 qCDebug(qLcMediaPlayer) <<
"metadata:" << m_metaData;
132 qCDebug(qLcMediaPlayer) <<
"video metadata:" << m_trackMetaData[0];
133 qCDebug(qLcMediaPlayer) <<
"audio metadata:" << m_trackMetaData[1];
134 qCDebug(qLcMediaPlayer) <<
"subtitle metadata:" << m_trackMetaData[2];
139 isVideoAvailable() ? 0 : -1,
140 isAudioAvailable() ? 0 : -1,
143 updateVideoTrackEnabled();
144 updateAudioTrackEnabled();
145 updateNativeSizeOnVideoOutput();
146 positionChanged(0ms);
149 m_hasPendingMedia =
false;
150 if (m_requestedPlaybackState) {
151 switch (*m_requestedPlaybackState) {
152 case QMediaPlayer::PlayingState:
155 case QMediaPlayer::PausedState:
164 qCDebug(qLcMediaPlayer) <<
"Discovery error:" << discoveryResult.error();
165 m_resourceErrorState = ResourceErrorState::ErrorOccurred;
166 error(QMediaPlayer::Error::ResourceError, u"Resource cannot be discovered"_s);
167 m_hasPendingMedia =
false;
168 mediaStatusChanged(QMediaPlayer::InvalidMedia);
169 resetStateForEmptyOrInvalidMedia();
179 qCDebug(qLcMediaPlayer) <<
"Added pad" << pad.name() <<
"from" << src.name();
183 std::optional<QGstreamerMediaPlayer::TrackType> type = toTrackType(caps);
187 customPipelinePads[*type] = pad;
195 customPipeline.add(sink);
197 customPipelineSinks[VideoStream] = sink;
204 customPipeline.add(sink);
206 customPipelineSinks[AudioStream] = sink;
210 case SubtitleStream: {
214 customPipeline.add(sink);
216 customPipelineSinks[SubtitleStream] = sink;
233 Q_ASSERT(thread()->isCurrentThread());
235 qCDebug(qLcMediaPlayer) <<
"Removed pad" << pad.name() <<
"from" << src.name() <<
"for stream"
238 auto found =
std::find(customPipelinePads.begin(), customPipelinePads.end(), pad);
239 if (found == customPipelinePads.end())
242 TrackType type = TrackType(
std::distance(customPipelinePads.begin(), found));
247 case SubtitleStream: {
248 if (customPipelineSinks[VideoStream]) {
249 customPipeline.stopAndRemoveElements(customPipelineSinks[VideoStream]);
250 customPipelineSinks[VideoStream] = {};
262 using namespace std::chrono_literals;
263 m_nativeSize.clear();
265 bool metadataNeedsSignal = !m_metaData.isEmpty();
266 bool tracksNeedsSignal =
267 std::any_of(m_trackMetaData.begin(), m_trackMetaData.end(), [](
const auto &container) {
268 return !container.empty();
272 m_trackMetaData.fill({});
274 seekableChanged(
false);
276 videoAvailableChanged(
false);
277 audioAvailableChanged(
false);
279 m_activeTrack.fill(-1);
281 if (metadataNeedsSignal)
283 if (tracksNeedsSignal)
289 int activeVideoTrack = activeTrack(TrackType::VideoStream);
290 bool hasVideoTrack = activeVideoTrack != -1;
292 QSize nativeSize = hasVideoTrack ? m_nativeSize[activeTrack(TrackType::VideoStream)] : QSize{};
294 QVariant orientation = hasVideoTrack
295 ? m_trackMetaData[TrackType::VideoStream][activeTrack(TrackType::VideoStream)].value(
296 QMediaMetaData::Key::Orientation)
299 if (orientation.isValid()) {
300 auto rotation = orientation.value<QtVideo::Rotation>();
301 gstVideoOutput->setRotation(rotation);
308 gst_play_seek(m_gstPlay.get(), gst_play_get_position(m_gstPlay.get()));
313 bool hasTrack = m_activeTrack[TrackType::VideoStream] != -1;
317 gst_play_set_video_track_enabled(m_gstPlay.get(), hasTrack && hasSink);
322 bool hasTrack = m_activeTrack[TrackType::AudioStream] != -1;
323 bool hasAudioOut = gstAudioOutput;
325 gst_play_set_audio_track_enabled(m_gstPlay.get(), hasTrack && hasAudioOut);
330 if (qFuzzyIsNull(newProgress - m_bufferProgress))
333 m_bufferProgress = newProgress;
334 bufferProgressChanged(m_bufferProgress);
339 auto handlers = std::initializer_list<QGObjectHandlerScopedConnection *>{ &sourceSetup };
340 for (QGObjectHandlerScopedConnection *handler : handlers)
341 handler->disconnect();
346 auto videoOutput = QGstreamerVideoOutput::create();
348 return q23::unexpected{ videoOutput.error() };
356 gst_play_config_set_seek_accurate(config, accurate);
360 QMediaPlayer *parent)
362 QPlatformMediaPlayer(parent),
363 gstVideoOutput(videoOutput),
365 gst_play_new(
nullptr),
366 QGstPlayHandle::HasRef,
369 GST_PIPELINE_CAST(gst_play_get_pipeline(m_gstPlay.get())),
370 QGstPipeline::HasRef,
373 QGstBusHandle{ gst_play_get_message_bus(m_gstPlay.get()), QGstBusHandle::HasRef },
382 constexpr bool useNxpWorkaround = std::is_same_v<
decltype(&gst_play_config_set_seek_accurate),
383 void (*)(GstPlay *, gboolean)>;
385 QUniqueGstStructureHandle config{
386 gst_play_get_config(m_gstPlay.get()),
389 if constexpr (useNxpWorkaround)
390 setSeekAccurate(m_gstPlay.get(),
true);
392 setSeekAccurate(config.get(),
true);
394 gst_play_set_config(m_gstPlay.get(), config.release());
396 QUniqueGstStructureHandle config{
397 gst_play_get_config(m_gstPlay.get()),
399 gst_play_config_set_seek_accurate(config.get(),
true);
400 gst_play_set_config(m_gstPlay.get(), config.release());
403 gstVideoOutput->setParent(
this);
407 m_gstVideoSink =
new QGstreamerRelayVideoSink(
this);
408 m_gstVideoSink->setRhi(qEnsureThreadLocalRhi());
411 m_playbin.set(
"video-sink", gstVideoOutput->gstElement());
412 m_playbin.set(
"text-sink", gstVideoOutput->gstSubtitleElement());
413 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
415 m_gstPlayBus.installMessageFilter(
this);
418 gst_play_set_subtitle_track_enabled(m_gstPlay.get(),
false);
420 sourceSetup = m_playbin.connect(
"source-setup", GCallback(sourceSetupCallback),
this);
422 m_activeTrack.fill(-1);
430 cleanupCustomPipeline();
432 m_gstPlayBus.removeMessageFilter(
static_cast<QGstreamerBusMessageFilter *>(
this));
433 gst_bus_set_flushing(m_gstPlayBus.get(), TRUE);
434 gst_play_stop(m_gstPlay.get());
437 m_playbin.setStateSync(GST_STATE_NULL);
439 m_playbin.set(
"video-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
440 m_playbin.set(
"text-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
441 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
446 using namespace std::chrono;
448 positionChanged(round<milliseconds>(nanoseconds{
449 gst_play_get_position(m_gstPlay.get()),
455 if (isCustomSource()) {
456 constexpr bool traceBusMessages =
true;
457 if (traceBusMessages)
458 qCDebug(qLcMediaPlayer) <<
"received bus message:" << message;
460 switch (message.type()) {
461 case GST_MESSAGE_WARNING:
462 qWarning() <<
"received bus message:" << message;
465 case GST_MESSAGE_INFO:
466 qInfo() <<
"received bus message:" << message;
469 case GST_MESSAGE_ERROR:
470 qWarning() <<
"received bus message:" << message;
471 customPipeline.dumpPipelineGraph(
"GST_MESSAGE_ERROR");
474 case GST_MESSAGE_LATENCY:
475 customPipeline.recalculateLatency();
484 switch (message.type()) {
485 case GST_MESSAGE_APPLICATION:
486 if (gst_play_is_play_message(message.message()))
487 return processBusMessageApplication(message);
491 qCDebug(qLcMediaPlayer) << message;
501 using namespace std::chrono;
503 gst_play_message_parse_type(message.message(), &type);
507 case GST_PLAY_MESSAGE_URI_LOADED: {
508 mediaStatusChanged(QMediaPlayer::LoadedMedia);
512 case GST_PLAY_MESSAGE_POSITION_UPDATED: {
513 if (state() == QMediaPlayer::PlaybackState::PlayingState) {
515 constexpr bool usePayload =
false;
516 if constexpr (usePayload) {
517 GstClockTime position;
518 gst_play_message_parse_position_updated(message.message(), &position);
519 positionChanged(round<milliseconds>(nanoseconds{ position }));
521 GstClockTime position = gst_play_get_position(m_gstPlay.get());
522 positionChanged(round<milliseconds>(nanoseconds{ position }));
527 case GST_PLAY_MESSAGE_DURATION_CHANGED: {
528 GstClockTime duration;
529 gst_play_message_parse_duration_updated(message.message(), &duration);
530 milliseconds durationInMs = round<milliseconds>(nanoseconds{ duration });
531 durationChanged(durationInMs);
533 m_metaData.insert(QMediaMetaData::Duration,
int(durationInMs.count()));
538 case GST_PLAY_MESSAGE_BUFFERING: {
540 gst_play_message_parse_buffering_percent(message.message(), &percent);
541 updateBufferProgress(percent * 0.01f);
544 case GST_PLAY_MESSAGE_STATE_CHANGED: {
546 gst_play_message_parse_state_changed(message.message(), &state);
549 case GstPlayState::GST_PLAY_STATE_STOPPED:
550 if (stateChangeToSkip) {
551 qCDebug(qLcMediaPlayer) <<
" skipping StoppedState transition";
553 stateChangeToSkip -= 1;
556 stateChanged(QMediaPlayer::StoppedState);
557 updateBufferProgress(0);
560 case GstPlayState::GST_PLAY_STATE_PAUSED:
561 stateChanged(QMediaPlayer::PausedState);
562 mediaStatusChanged(QMediaPlayer::BufferedMedia);
564 updateBufferProgress(1);
566 case GstPlayState::GST_PLAY_STATE_BUFFERING:
567 mediaStatusChanged(QMediaPlayer::BufferingMedia);
569 case GstPlayState::GST_PLAY_STATE_PLAYING:
570 stateChanged(QMediaPlayer::PlayingState);
571 mediaStatusChanged(QMediaPlayer::BufferedMedia);
573 updateBufferProgress(1);
580 case GST_PLAY_MESSAGE_MEDIA_INFO_UPDATED: {
583 QUniqueGstPlayMediaInfoHandle info{};
584 gst_play_message_parse_media_info_updated(message.message(), &info);
586 seekableChanged(gst_play_media_info_is_seekable(info.get()));
588 const gchar *title = gst_play_media_info_get_title(info.get());
589 m_metaData.insert(QMediaMetaData::Title, QString::fromUtf8(title));
596 case GST_PLAY_MESSAGE_END_OF_STREAM: {
598 positionChanged(m_duration);
599 qCDebug(qLcMediaPlayer) <<
"EOS: restarting loop";
600 gst_play_play(m_gstPlay.get());
601 positionChanged(0ms);
605 stateChangeToSkip += 1;
607 qCDebug(qLcMediaPlayer) <<
"EOS: done";
608 positionChanged(m_duration);
609 mediaStatusChanged(QMediaPlayer::EndOfMedia);
610 stateChanged(QMediaPlayer::StoppedState);
616 case GST_PLAY_MESSAGE_ERROR:
617 case GST_PLAY_MESSAGE_WARNING:
618 case GST_PLAY_MESSAGE_VIDEO_DIMENSIONS_CHANGED:
619 case GST_PLAY_MESSAGE_VOLUME_CHANGED:
620 case GST_PLAY_MESSAGE_MUTE_CHANGED:
621 case GST_PLAY_MESSAGE_SEEK_DONE:
625 Q_UNREACHABLE_RETURN(
false);
631 return m_duration.count();
636 return !m_url.isEmpty() || m_stream;
644 switch (mediaStatus()) {
645 case QMediaPlayer::MediaStatus::NoMedia:
646 case QMediaPlayer::MediaStatus::InvalidMedia:
656 return m_bufferProgress;
661 return QMediaTimeRange();
666 return gst_play_get_rate(m_gstPlay.get());
671 if (isCustomSource()) {
672 static std::once_flag flag;
673 std::call_once(flag, [] {
676 <<
"setPlaybackRate with custom gstreamer pipelines can cause pipeline hangs. "
680 customPipeline.setPlaybackRate(rate);
684 if (rate == playbackRate())
687 qCDebug(qLcMediaPlayer) <<
"gst_play_set_rate" << rate;
688 gst_play_set_rate(m_gstPlay.get(), rate);
689 playbackRateChanged(rate);
694 std::chrono::milliseconds posInMs{ pos };
696 setPosition(posInMs);
701 using namespace std::chrono;
703 if (isCustomSource()) {
704 static std::once_flag flag;
705 std::call_once(flag, [] {
707 qWarning() <<
"setPosition with custom gstreamer pipelines can cause pipeline hangs. "
711 customPipeline.setPosition(pos);
715 if (m_hasPendingMedia) {
719 qCDebug(qLcMediaPlayer) <<
"gst_play_seek" << pos;
720 gst_play_seek(m_gstPlay.get(), nanoseconds(pos).count());
722 if (mediaStatus() == QMediaPlayer::EndOfMedia)
723 mediaStatusChanged(QMediaPlayer::LoadedMedia);
724 positionChanged(pos);
729 if (isCustomSource()) {
731 customPipeline.setState(GST_STATE_PLAYING);
732 stateChanged(QMediaPlayer::PlayingState);
736 if (m_hasPendingMedia) {
739 m_requestedPlaybackState = QMediaPlayer::PlayingState;
743 QMediaPlayer::PlaybackState currentState = state();
744 if (currentState == QMediaPlayer::PlayingState || !hasValidMedia())
747 if (currentState != QMediaPlayer::PausedState)
750 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
752 mediaStatusChanged(QMediaPlayer::LoadedMedia);
756 gst_play_seek(m_gstPlay.get(), m_pendingSeek->count());
757 m_pendingSeek = std::nullopt;
760 qCDebug(qLcMediaPlayer) <<
"gst_play_play";
762 gst_play_play(m_gstPlay.get());
763 stateChanged(QMediaPlayer::PlayingState);
768 if (isCustomSource()) {
770 customPipeline.setState(GST_STATE_PAUSED);
771 stateChanged(QMediaPlayer::PausedState);
775 if (m_hasPendingMedia) {
776 m_requestedPlaybackState = QMediaPlayer::PausedState;
780 if (state() == QMediaPlayer::PausedState || !hasMedia()
781 || m_resourceErrorState != ResourceErrorState::NoError)
786 qCDebug(qLcMediaPlayer) <<
"gst_play_pause";
787 gst_play_pause(m_gstPlay.get());
789 mediaStatusChanged(QMediaPlayer::BufferedMedia);
790 stateChanged(QMediaPlayer::PausedState);
795 if (isCustomSource()) {
796 customPipeline.setState(GST_STATE_READY);
797 stateChanged(QMediaPlayer::StoppedState);
802 if (m_hasPendingMedia) {
803 m_requestedPlaybackState = QMediaPlayer::StoppedState;
807 using namespace std::chrono_literals;
808 if (state() == QMediaPlayer::StoppedState) {
809 if (position() != 0) {
811 positionChanged(0ms);
812 mediaStatusChanged(QMediaPlayer::LoadedMedia);
817 qCDebug(qLcMediaPlayer) <<
"gst_play_stop";
819 gst_play_stop(m_gstPlay.get());
821 stateChanged(QMediaPlayer::StoppedState);
823 mediaStatusChanged(QMediaPlayer::LoadedMedia);
824 positionChanged(0ms);
829 if (isCustomSource())
830 return customPipeline;
848 return PitchCompensationAvailability::AlwaysOn;
866 const gchar *typeName = g_type_name_from_instance((GTypeInstance *)source);
867 qCDebug(qLcMediaPlayer) <<
"Setting up source:" << typeName;
869 if (typeName == std::string_view(
"GstRTSPSrc")) {
873 int v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_LATENCY", &ok);
876 qCDebug(qLcMediaPlayer) <<
" -> setting source latency to:" << latency <<
"ms";
877 s.set(
"latency", latency);
880 v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_DROP_ON_LATENCY", &ok);
883 qCDebug(qLcMediaPlayer) <<
" -> setting drop-on-latency to:" << drop;
884 s.set(
"drop-on-latency", drop);
887 v = qEnvironmentVariableIntValue(
"QT_MEDIA_RTSP_DO_RETRANSMISSION", &ok);
890 qCDebug(qLcMediaPlayer) <<
" -> setting do-retransmission to:" << retrans;
891 s.set(
"do-retransmission", retrans);
898 cleanupCustomPipeline();
900 m_resourceErrorState = ResourceErrorState::NoError;
905 m_discoveryHandler.cancel();
906 m_discoverFuture.cancel();
910 streamURL = qGstRegisterQIODevice(stream);
912 if (content.isEmpty() && !stream) {
913 mediaStatusChanged(QMediaPlayer::NoMedia);
914 resetStateForEmptyOrInvalidMedia();
918 if (isCustomSource()) {
919 setMediaCustomSource(content);
921 mediaStatusChanged(QMediaPlayer::LoadingMedia);
922 m_hasPendingMedia =
true;
923 m_requestedPlaybackState = std::nullopt;
925 const QUrl &playUrl = stream ? streamURL : content;
926 m_discoverFuture = discover(playUrl);
929 m_discoverFuture.then(
this,[
this, playUrl](
const DiscoverResult &result) {
930 handleDiscoverResult(result, playUrl);
937 using namespace Qt::Literals;
938 using namespace std::chrono;
939 using namespace std::chrono_literals;
944 m_playbin.set(
"video-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
945 m_playbin.set(
"text-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
946 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
949 if (QGstElement sink = gstVideoOutput->gstreamerVideoSink()->gstSink())
954 customPipeline = QGstPipeline::create(
"customPipeline");
955 customPipeline.installMessageFilter(
this);
956 positionUpdateTimer = std::make_unique<QTimer>();
958 QObject::connect(positionUpdateTimer.get(), &QTimer::timeout,
this, [
this] {
959 Q_ASSERT(customPipeline);
960 auto position = customPipeline.position();
962 positionChanged(round<milliseconds>(position));
965 positionUpdateTimer->start(100ms);
967 QByteArray gstLaunchString =
968 content.toString(QUrl::RemoveScheme | QUrl::PrettyDecoded).toLatin1();
969 qCDebug(qLcMediaPlayer) <<
"generating" << gstLaunchString;
972 emit error(QMediaPlayer::ResourceError, u"Could not create custom pipeline"_s);
977 customPipeline.add(decoder);
980 qGstSafeCast<GstBin>(element.element()),
984 elementBin.addUnlinkedGhostPads(GstPadDirection::GST_PAD_SRC);
987 padAdded = decoder.onPadAdded<&QGstreamerMediaPlayer::decoderPadAddedCustomSource>(
this);
988 padRemoved = decoder.onPadRemoved<&QGstreamerMediaPlayer::decoderPadRemovedCustomSource>(
this);
990 customPipeline.setStateSync(GstState::GST_STATE_PAUSED);
992 auto srcPadVisitor = [](GstElement *element, GstPad *pad,
void *self) -> gboolean {
993 reinterpret_cast<QGstreamerMediaPlayer *>(self)->decoderPadAddedCustomSource(
994 QGstElement{ element, QGstElement::NeedsRef }, QGstPad{ pad, QGstPad::NeedsRef });
998 gst_element_foreach_pad(element.element(), srcPadVisitor,
this);
1000 mediaStatusChanged(QMediaPlayer::LoadedMedia);
1002 customPipeline.dumpGraph(
"setMediaCustomPipeline");
1007 customPipeline.setStateSync(GST_STATE_NULL);
1008 customPipeline.removeMessageFilter(
this);
1010 for (QGstElement &sink : customPipelineSinks)
1012 customPipeline.remove(sink);
1014 positionUpdateTimer = {};
1015 customPipeline = {};
1020 if (isCustomSource()) {
1021 qWarning() <<
"QMediaPlayer::setAudioOutput not supported when using custom sources";
1025 if (gstAudioOutput == output)
1030 gstOutput->setAsync(
true);
1034 m_playbin.set(
"audio-sink", gstAudioOutput->gstElement());
1036 m_playbin.set(
"audio-sink", QGstElement::createFromPipelineDescription(
"fakesink"));
1037 updateAudioTrackEnabled();
1041 if (!qmediaplayerDestructorCalled)
1042 m_playbin.finishStateChange();
1060 Q_ASSERT(pluggableSink);
1066 QSpan<
const QMediaMetaData> tracks = m_trackMetaData[type];
1067 return tracks.size();
1072 QSpan<
const QMediaMetaData> tracks = m_trackMetaData[type];
1073 if (index < tracks.size())
1074 return tracks[index];
1080 return m_activeTrack[type];
1085 if (m_activeTrack[type] == index)
1088 int formerTrack = m_activeTrack[type];
1089 m_activeTrack[type] = index;
1092 case TrackType::VideoStream: {
1094 gst_play_set_video_track(m_gstPlay.get(), index);
1095 updateVideoTrackEnabled();
1096 updateNativeSizeOnVideoOutput();
1099 case TrackType::AudioStream: {
1101 gst_play_set_audio_track(m_gstPlay.get(), index);
1102 updateAudioTrackEnabled();
1105 case TrackType::SubtitleStream: {
1107 gst_play_set_subtitle_track(m_gstPlay.get(), index);
1108 gst_play_set_subtitle_track_enabled(m_gstPlay.get(), index != -1);
1115 if (formerTrack != -1 && index != -1)
1118 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)