summaryrefslogtreecommitdiffstats
path: root/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp')
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp508
1 files changed, 267 insertions, 241 deletions
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp
index 6e3636441..a564fd62c 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp
@@ -26,7 +26,11 @@
#include <sys/stat.h>
#include <fcntl.h>
-static Q_LOGGING_CATEGORY(qLcMediaPlayer, "qt.multimedia.player")
+#if QT_CONFIG(gstreamer_gl)
+# include <gst/gl/gl.h>
+#endif
+
+Q_STATIC_LOGGING_CATEGORY(qLcMediaPlayer, "qt.multimedia.player");
QT_BEGIN_NAMESPACE
@@ -74,6 +78,23 @@ QGstreamerMediaPlayer::TrackSelector &QGstreamerMediaPlayer::trackSelector(Track
return ts;
}
+void QGstreamerMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status)
+{
+ if (status != QMediaPlayer::StalledMedia)
+ m_stalledMediaNotifier.stop();
+
+ QPlatformMediaPlayer::mediaStatusChanged(status);
+}
+
+void QGstreamerMediaPlayer::updateBufferProgress(float newProgress)
+{
+ if (qFuzzyIsNull(newProgress - m_bufferProgress))
+ return;
+
+ m_bufferProgress = newProgress;
+ bufferProgressChanged(m_bufferProgress);
+}
+
void QGstreamerMediaPlayer::disconnectDecoderHandlers()
{
auto handlers = std::initializer_list<QGObjectHandlerScopedConnection *>{
@@ -92,47 +113,35 @@ QMaybe<QPlatformMediaPlayer *> QGstreamerMediaPlayer::create(QMediaPlayer *paren
if (!videoOutput)
return videoOutput.error();
- QGstElement videoInputSelector =
- QGstElement::createFromFactory("input-selector", "videoInputSelector");
- if (!videoInputSelector)
- return errorMessageCannotFindElement("input-selector");
+ static const auto error =
+ qGstErrorMessageIfElementsNotAvailable("input-selector", "decodebin", "uridecodebin");
+ if (error)
+ return *error;
- QGstElement audioInputSelector =
- QGstElement::createFromFactory("input-selector", "audioInputSelector");
- if (!audioInputSelector)
- return errorMessageCannotFindElement("input-selector");
-
- QGstElement subTitleInputSelector =
- QGstElement::createFromFactory("input-selector", "subTitleInputSelector");
- if (!subTitleInputSelector)
- return errorMessageCannotFindElement("input-selector");
-
- return new QGstreamerMediaPlayer(videoOutput.value(), videoInputSelector, audioInputSelector,
- subTitleInputSelector, parent);
+ return new QGstreamerMediaPlayer(videoOutput.value(), parent);
}
QGstreamerMediaPlayer::QGstreamerMediaPlayer(QGstreamerVideoOutput *videoOutput,
- QGstElement videoInputSelector,
- QGstElement audioInputSelector,
- QGstElement subTitleInputSelector,
QMediaPlayer *parent)
: QObject(parent),
QPlatformMediaPlayer(parent),
- trackSelectors{ { { VideoStream, videoInputSelector },
- { AudioStream, audioInputSelector },
- { SubtitleStream, subTitleInputSelector } } },
+ trackSelectors{ {
+ { VideoStream,
+ QGstElement::createFromFactory("input-selector", "videoInputSelector") },
+ { AudioStream,
+ QGstElement::createFromFactory("input-selector", "audioInputSelector") },
+ { SubtitleStream,
+ QGstElement::createFromFactory("input-selector", "subTitleInputSelector") },
+ } },
playerPipeline(QGstPipeline::create("playerPipeline")),
gstVideoOutput(videoOutput)
{
- playerPipeline.setFlushOnConfigChanges(true);
-
gstVideoOutput->setParent(this);
gstVideoOutput->setPipeline(playerPipeline);
for (auto &ts : trackSelectors)
playerPipeline.add(ts.selector);
- playerPipeline.setState(GST_STATE_NULL);
playerPipeline.installMessageFilter(static_cast<QGstreamerBusMessageFilter *>(this));
playerPipeline.installMessageFilter(static_cast<QGstreamerSyncMessageFilter *>(this));
@@ -143,7 +152,12 @@ QGstreamerMediaPlayer::QGstreamerMediaPlayer(QGstreamerVideoOutput *videoOutput,
gst_pipeline_use_clock(playerPipeline.pipeline(), systemClock.get());
connect(&positionUpdateTimer, &QTimer::timeout, this, [this] {
- updatePosition();
+ updatePositionFromPipeline();
+ });
+
+ m_stalledMediaNotifier.setSingleShot(true);
+ connect(&m_stalledMediaNotifier, &QTimer::timeout, this, [this] {
+ mediaStatusChanged(QMediaPlayer::StalledMedia);
});
}
@@ -152,25 +166,45 @@ QGstreamerMediaPlayer::~QGstreamerMediaPlayer()
playerPipeline.removeMessageFilter(static_cast<QGstreamerBusMessageFilter *>(this));
playerPipeline.removeMessageFilter(static_cast<QGstreamerSyncMessageFilter *>(this));
playerPipeline.setStateSync(GST_STATE_NULL);
- topology.free();
}
-qint64 QGstreamerMediaPlayer::position() const
+std::chrono::nanoseconds QGstreamerMediaPlayer::pipelinePosition() const
+{
+ if (!hasMedia())
+ return {};
+
+ Q_ASSERT(playerPipeline);
+ return playerPipeline.position();
+}
+
+void QGstreamerMediaPlayer::updatePositionFromPipeline()
{
- if (playerPipeline.isNull() || m_url.isEmpty())
- return 0;
+ using namespace std::chrono;
- return playerPipeline.position()/1e6;
+ positionChanged(round<milliseconds>(pipelinePosition()));
+}
+
+void QGstreamerMediaPlayer::updateDurationFromPipeline()
+{
+ std::optional<std::chrono::milliseconds> duration = playerPipeline.durationInMs();
+ if (!duration)
+ duration = std::chrono::milliseconds{ -1 };
+
+ if (duration != m_duration) {
+ qCDebug(qLcMediaPlayer) << "updateDurationFromPipeline" << *duration;
+ m_duration = *duration;
+ durationChanged(m_duration);
+ }
}
qint64 QGstreamerMediaPlayer::duration() const
{
- return m_duration;
+ return m_duration.count();
}
float QGstreamerMediaPlayer::bufferProgress() const
{
- return m_bufferProgress/100.;
+ return m_bufferProgress;
}
QMediaTimeRange QGstreamerMediaPlayer::availablePlaybackRanges() const
@@ -185,19 +219,28 @@ qreal QGstreamerMediaPlayer::playbackRate() const
void QGstreamerMediaPlayer::setPlaybackRate(qreal rate)
{
- bool applyRateToPipeline = state() != QMediaPlayer::StoppedState;
- if (playerPipeline.setPlaybackRate(rate, applyRateToPipeline))
- playbackRateChanged(rate);
+ if (rate == m_rate)
+ return;
+
+ m_rate = rate;
+
+ playerPipeline.setPlaybackRate(rate);
+ playbackRateChanged(rate);
}
void QGstreamerMediaPlayer::setPosition(qint64 pos)
{
- qint64 currentPos = playerPipeline.position()/1e6;
- if (pos == currentPos)
+ std::chrono::milliseconds posInMs{ pos };
+ setPosition(posInMs);
+}
+
+void QGstreamerMediaPlayer::setPosition(std::chrono::milliseconds pos)
+{
+ if (pos == playerPipeline.position())
return;
playerPipeline.finishStateChange();
- playerPipeline.setPosition(pos*1e6);
- qCDebug(qLcMediaPlayer) << Q_FUNC_INFO << pos << playerPipeline.position()/1e6;
+ playerPipeline.setPosition(pos);
+ qCDebug(qLcMediaPlayer) << Q_FUNC_INFO << pos << playerPipeline.positionInMs();
if (mediaStatus() == QMediaPlayer::EndOfMedia)
mediaStatusChanged(QMediaPlayer::LoadedMedia);
positionChanged(pos);
@@ -205,65 +248,74 @@ void QGstreamerMediaPlayer::setPosition(qint64 pos)
void QGstreamerMediaPlayer::play()
{
- if (state() == QMediaPlayer::PlayingState || m_url.isEmpty())
+ QMediaPlayer::PlaybackState currentState = state();
+ if (currentState == QMediaPlayer::PlayingState || !hasMedia())
return;
- resetCurrentLoop();
- playerPipeline.setInStoppedState(false);
+ if (currentState != QMediaPlayer::PausedState)
+ resetCurrentLoop();
+
+ gstVideoOutput->setActive(true);
if (mediaStatus() == QMediaPlayer::EndOfMedia) {
- playerPipeline.setPosition(0);
- updatePosition();
+ playerPipeline.setPosition({});
+ positionChanged(0);
}
qCDebug(qLcMediaPlayer) << "play().";
int ret = playerPipeline.setState(GST_STATE_PLAYING);
if (m_requiresSeekOnPlay) {
- // Flushing the pipeline is required to get track changes
- // immediately, when they happen while paused.
+ // Flushing the pipeline is required to get track changes immediately, when they happen
+ // while paused.
playerPipeline.flush();
m_requiresSeekOnPlay = false;
+ } else {
+ if (currentState == QMediaPlayer::StoppedState) {
+ // we get an assertion failure during instant playback rate changes
+ // https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3545
+ constexpr bool performInstantRateChange = false;
+ playerPipeline.applyPlaybackRate(/*instantRateChange=*/performInstantRateChange);
+ }
}
if (ret == GST_STATE_CHANGE_FAILURE)
qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the playing state.";
positionUpdateTimer.start(100);
- emit stateChanged(QMediaPlayer::PlayingState);
+ stateChanged(QMediaPlayer::PlayingState);
}
void QGstreamerMediaPlayer::pause()
{
- if (state() == QMediaPlayer::PausedState || m_url.isEmpty()
+ if (state() == QMediaPlayer::PausedState || !hasMedia()
|| m_resourceErrorState != ResourceErrorState::NoError)
return;
positionUpdateTimer.stop();
- if (playerPipeline.inStoppedState()) {
- playerPipeline.setInStoppedState(false);
- playerPipeline.flush();
- }
- int ret = playerPipeline.setState(GST_STATE_PAUSED);
+
+ gstVideoOutput->setActive(true);
+ int ret = playerPipeline.setStateSync(GST_STATE_PAUSED);
if (ret == GST_STATE_CHANGE_FAILURE)
qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the paused state.";
if (mediaStatus() == QMediaPlayer::EndOfMedia) {
- playerPipeline.setPosition(0);
+ playerPipeline.setPosition({});
+ positionChanged(0);
+ } else {
+ updatePositionFromPipeline();
}
- updatePosition();
- emit stateChanged(QMediaPlayer::PausedState);
+ stateChanged(QMediaPlayer::PausedState);
if (m_bufferProgress > 0 || !canTrackProgress())
mediaStatusChanged(QMediaPlayer::BufferedMedia);
else
mediaStatusChanged(QMediaPlayer::BufferingMedia);
-
- emit bufferProgressChanged(m_bufferProgress / 100.);
}
void QGstreamerMediaPlayer::stop()
{
+ using namespace std::chrono_literals;
if (state() == QMediaPlayer::StoppedState) {
if (position() != 0) {
- playerPipeline.setPosition(0);
- positionChanged(0);
+ playerPipeline.setPosition({});
+ positionChanged(0ms);
mediaStatusChanged(QMediaPlayer::LoadedMedia);
}
return;
@@ -271,27 +323,63 @@ void QGstreamerMediaPlayer::stop()
stopOrEOS(false);
}
-void *QGstreamerMediaPlayer::nativePipeline()
+const QGstPipeline &QGstreamerMediaPlayer::pipeline() const
{
- return playerPipeline.pipeline();
+ return playerPipeline;
}
void QGstreamerMediaPlayer::stopOrEOS(bool eos)
{
+ using namespace std::chrono_literals;
+
positionUpdateTimer.stop();
- playerPipeline.setInStoppedState(true);
+ gstVideoOutput->setActive(false);
bool ret = playerPipeline.setStateSync(GST_STATE_PAUSED);
if (!ret)
qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the stopped state.";
- if (!eos)
- playerPipeline.setPosition(0);
- updatePosition();
- emit stateChanged(QMediaPlayer::StoppedState);
+ if (!eos) {
+ playerPipeline.setPosition(0ms);
+ positionChanged(0ms);
+ }
+ stateChanged(QMediaPlayer::StoppedState);
if (eos)
mediaStatusChanged(QMediaPlayer::EndOfMedia);
else
mediaStatusChanged(QMediaPlayer::LoadedMedia);
m_initialBufferProgressSent = false;
+ bufferProgressChanged(0.f);
+}
+
+void QGstreamerMediaPlayer::detectPipelineIsSeekable()
+{
+ std::optional<bool> canSeek = playerPipeline.canSeek();
+ if (canSeek) {
+ qCDebug(qLcMediaPlayer) << "detectPipelineIsSeekable: pipeline is seekable:" << *canSeek;
+ seekableChanged(*canSeek);
+ } else {
+ qCWarning(qLcMediaPlayer) << "detectPipelineIsSeekable: query for seekable failed.";
+ seekableChanged(false);
+ }
+}
+
+QGstElement QGstreamerMediaPlayer::getSinkElementForTrackType(TrackType trackType)
+{
+ switch (trackType) {
+ case AudioStream:
+ return gstAudioOutput ? gstAudioOutput->gstElement() : QGstElement{};
+ case VideoStream:
+ return gstVideoOutput ? gstVideoOutput->gstElement() : QGstElement{};
+ case SubtitleStream:
+ return gstVideoOutput ? gstVideoOutput->gstSubtitleElement() : QGstElement{};
+ break;
+ default:
+ Q_UNREACHABLE_RETURN(QGstElement{});
+ }
+}
+
+bool QGstreamerMediaPlayer::hasMedia() const
+{
+ return !m_url.isEmpty() || m_stream;
}
bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
@@ -306,12 +394,11 @@ bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
gst_message_parse_tag(gm, &tagList);
qCDebug(qLcMediaPlayer) << " Got tags: " << tagList.get();
- auto metaData = taglistToMetaData(tagList);
- auto keys = metaData.keys();
- for (auto k : metaData.keys())
- m_metaData.insert(k, metaData.value(k));
- if (!keys.isEmpty())
- emit metaDataChanged();
+
+ QMediaMetaData originalMetaData = m_metaData;
+ extendMetaDataFromTagList(m_metaData, tagList);
+ if (originalMetaData != m_metaData)
+ metaDataChanged();
if (gstVideoOutput) {
QVariant rotation = m_metaData.value(QMediaMetaData::Orientation);
@@ -320,45 +407,40 @@ bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
break;
}
case GST_MESSAGE_DURATION_CHANGED: {
- qint64 d = playerPipeline.duration()/1e6;
- qCDebug(qLcMediaPlayer) << " duration changed message" << d;
- if (d != m_duration) {
- m_duration = d;
- emit durationChanged(duration());
- }
+ if (!prerolling)
+ updateDurationFromPipeline();
+
return false;
}
- case GST_MESSAGE_EOS:
+ case GST_MESSAGE_EOS: {
+ positionChanged(m_duration);
if (doLoop()) {
setPosition(0);
break;
}
stopOrEOS(true);
break;
+ }
case GST_MESSAGE_BUFFERING: {
int progress = 0;
gst_message_parse_buffering(gm, &progress);
- qCDebug(qLcMediaPlayer) << " buffering message: " << progress;
-
if (state() != QMediaPlayer::StoppedState && !prerolling) {
if (!m_initialBufferProgressSent) {
mediaStatusChanged(QMediaPlayer::BufferingMedia);
m_initialBufferProgressSent = true;
}
- if (m_bufferProgress > 0 && progress == 0)
- mediaStatusChanged(QMediaPlayer::StalledMedia);
- else if (progress >= 50)
+ if (m_bufferProgress > 0 && progress == 0) {
+ m_stalledMediaNotifier.start(stalledMediaDebouncePeriod);
+ } else if (progress >= 50)
// QTBUG-124517: rethink buffering
mediaStatusChanged(QMediaPlayer::BufferedMedia);
else
mediaStatusChanged(QMediaPlayer::BufferingMedia);
}
- m_bufferProgress = progress;
-
- emit bufferProgressChanged(m_bufferProgress / 100.);
+ updateBufferProgress(progress * 0.01);
break;
}
case GST_MESSAGE_STATE_CHANGED: {
@@ -381,34 +463,22 @@ bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
case GST_STATE_PAUSED: {
if (prerolling) {
qCDebug(qLcMediaPlayer) << "Preroll done, setting status to Loaded";
+ playerPipeline.dumpGraph("playerPipelinePrerollDone");
+
prerolling = false;
- GST_DEBUG_BIN_TO_DOT_FILE(playerPipeline.bin(), GST_DEBUG_GRAPH_SHOW_ALL,
- "playerPipeline");
-
- qint64 d = playerPipeline.duration() / 1e6;
- if (d != m_duration) {
- m_duration = d;
- qCDebug(qLcMediaPlayer) << " duration changed" << d;
- emit durationChanged(duration());
- }
+ updateDurationFromPipeline();
+
+ m_metaData.insert(QMediaMetaData::Duration, duration());
+ if (!m_url.isEmpty())
+ m_metaData.insert(QMediaMetaData::Url, m_url);
parseStreamsAndMetadata();
+ metaDataChanged();
- emit tracksChanged();
+ tracksChanged();
mediaStatusChanged(QMediaPlayer::LoadedMedia);
- GstQuery *query = gst_query_new_seeking(GST_FORMAT_TIME);
- gboolean canSeek = false;
- if (gst_element_query(playerPipeline.element(), query)) {
- gst_query_parse_seeking(query, nullptr, &canSeek, nullptr, nullptr);
- qCDebug(qLcMediaPlayer) << " pipeline is seekable:" << canSeek;
- } else {
- qCDebug(qLcMediaPlayer) << " query for seekable failed.";
- }
- gst_query_unref(query);
- seekableChanged(canSeek);
-
- if (!playerPipeline.inStoppedState()) {
+ if (state() == QMediaPlayer::PlayingState) {
Q_ASSERT(!m_initialBufferProgressSent);
bool immediatelySendBuffered = !canTrackProgress() || m_bufferProgress > 0;
@@ -456,6 +526,7 @@ bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
error(QMediaPlayer::ResourceError, QString::fromUtf8(err.get()->message));
m_resourceErrorState = ResourceErrorState::ErrorReported;
m_url.clear();
+ m_stream = nullptr;
}
} else {
error(QMediaPlayer::ResourceError, QString::fromUtf8(err.get()->message));
@@ -479,21 +550,28 @@ bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
case GST_MESSAGE_SEGMENT_START: {
qCDebug(qLcMediaPlayer) << " segment start message, updating position";
- QGstStructure structure(gst_message_get_structure(gm));
+ QGstStructureView structure(gst_message_get_structure(gm));
auto p = structure["position"].toInt64();
if (p) {
- qint64 position = (*p)/1000000;
- emit positionChanged(position);
+ std::chrono::milliseconds position{
+ (*p) / 1000000,
+ };
+ positionChanged(position);
}
break;
}
case GST_MESSAGE_ELEMENT: {
- QGstStructure structure(gst_message_get_structure(gm));
+ QGstStructureView structure(gst_message_get_structure(gm));
auto type = structure.name();
- if (type == "stream-topology") {
- topology.free();
- topology = structure.copy();
- }
+ if (type == "stream-topology")
+ topology = structure.clone();
+
+ break;
+ }
+
+ case GST_MESSAGE_ASYNC_DONE: {
+ if (playerPipeline.state() >= GST_STATE_PAUSED)
+ detectPipelineIsSeekable();
break;
}
@@ -573,19 +651,19 @@ void QGstreamerMediaPlayer::decoderPadAdded(const QGstElement &src, const QGstPa
if (streamType == VideoStream) {
connectOutput(ts);
ts.setActiveInputPad(sinkPad);
- emit videoAvailableChanged(true);
+ videoAvailableChanged(true);
}
else if (streamType == AudioStream) {
connectOutput(ts);
ts.setActiveInputPad(sinkPad);
- emit audioAvailableChanged(true);
+ audioAvailableChanged(true);
}
}
if (!prerolling)
- emit tracksChanged();
+ tracksChanged();
- decoderOutputMap.insert(pad.name(), sinkPad);
+ decoderOutputMap.emplace(pad, sinkPad);
}
void QGstreamerMediaPlayer::decoderPadRemoved(const QGstElement &src, const QGstPad &pad)
@@ -594,9 +672,11 @@ void QGstreamerMediaPlayer::decoderPadRemoved(const QGstElement &src, const QGst
return;
qCDebug(qLcMediaPlayer) << "Removed pad" << pad.name() << "from" << src.name();
- auto track = decoderOutputMap.value(pad.name());
- if (track.isNull())
+
+ auto it = decoderOutputMap.find(pad);
+ if (it == decoderOutputMap.end())
return;
+ QGstPad track = it->second;
auto ts = std::find_if(std::begin(trackSelectors), std::end(trackSelectors),
[&](TrackSelector &ts){ return ts.selector == track.parent(); });
@@ -633,27 +713,12 @@ void QGstreamerMediaPlayer::connectOutput(TrackSelector &ts)
if (ts.isConnected)
return;
- QGstElement e;
- switch (ts.type) {
- case AudioStream:
- e = gstAudioOutput ? gstAudioOutput->gstElement() : QGstElement{};
- break;
- case VideoStream:
- e = gstVideoOutput ? gstVideoOutput->gstElement() : QGstElement{};
- break;
- case SubtitleStream:
- if (gstVideoOutput)
- gstVideoOutput->linkSubtitleStream(ts.selector);
- break;
- default:
- return;
- }
-
- if (!e.isNull()) {
+ QGstElement e = getSinkElementForTrackType(ts.type);
+ if (e) {
qCDebug(qLcMediaPlayer) << "connecting output for track type" << ts.type;
playerPipeline.add(e);
qLinkGstElements(ts.selector, e);
- e.setState(GST_STATE_PAUSED);
+ e.syncStateWithParent();
}
ts.isConnected = true;
@@ -664,23 +729,8 @@ void QGstreamerMediaPlayer::removeOutput(TrackSelector &ts)
if (!ts.isConnected)
return;
- QGstElement e;
- switch (ts.type) {
- case AudioStream:
- e = gstAudioOutput ? gstAudioOutput->gstElement() : QGstElement{};
- break;
- case VideoStream:
- e = gstVideoOutput ? gstVideoOutput->gstElement() : QGstElement{};
- break;
- case SubtitleStream:
- if (gstVideoOutput)
- gstVideoOutput->unlinkSubtitleStream();
- break;
- default:
- break;
- }
-
- if (!e.isNull()) {
+ QGstElement e = getSinkElementForTrackType(ts.type);
+ if (e) {
qCDebug(qLcMediaPlayer) << "removing output for track type" << ts.type;
playerPipeline.stopAndRemoveElements(e);
}
@@ -688,6 +738,18 @@ void QGstreamerMediaPlayer::removeOutput(TrackSelector &ts)
ts.isConnected = false;
}
+void QGstreamerMediaPlayer::removeDynamicPipelineElements()
+{
+ for (QGstElement *element : { &src, &decoder }) {
+ if (element->isNull())
+ continue;
+
+ element->setStateSync(GstState::GST_STATE_NULL);
+ playerPipeline.remove(*element);
+ *element = QGstElement{};
+ }
+}
+
void QGstreamerMediaPlayer::uridecodebinElementAddedCallback(GstElement * /*uridecodebin*/,
GstElement *child,
QGstreamerMediaPlayer *)
@@ -696,9 +758,7 @@ void QGstreamerMediaPlayer::uridecodebinElementAddedCallback(GstElement * /*urid
qCDebug(qLcMediaPlayer) << "New element added to uridecodebin:" << c.name();
static const GType decodeBinType = [] {
- QGstElementFactoryHandle factory = QGstElementFactoryHandle{
- gst_element_factory_find("decodebin"),
- };
+ QGstElementFactoryHandle factory = QGstElement::findFactory("decodebin");
return gst_element_factory_get_element_type(factory.get());
}();
@@ -757,16 +817,12 @@ void QGstreamerMediaPlayer::unknownTypeCallback(GstElement *decodebin, GstPad *p
static bool isQueue(const QGstElement &element)
{
static const GType queueType = [] {
- QGstElementFactoryHandle factory = QGstElementFactoryHandle{
- gst_element_factory_find("queue"),
- };
+ QGstElementFactoryHandle factory = QGstElement::findFactory("queue");
return gst_element_factory_get_element_type(factory.get());
}();
static const GType multiQueueType = [] {
- QGstElementFactoryHandle factory = QGstElementFactoryHandle{
- gst_element_factory_find("multiqueue"),
- };
+ QGstElementFactoryHandle factory = QGstElement::findFactory("multiqueue");
return gst_element_factory_get_element_type(factory.get());
}();
@@ -793,9 +849,12 @@ void QGstreamerMediaPlayer::decodebinElementRemovedCallback(GstBin * /*decodebin
void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream)
{
+ using namespace std::chrono_literals;
+
qCDebug(qLcMediaPlayer) << Q_FUNC_INFO << "setting location to" << content;
prerolling = true;
+ m_requiresSeekOnPlay = true;
m_resourceErrorState = ResourceErrorState::NoError;
bool ret = playerPipeline.setStateSync(GST_STATE_NULL);
@@ -805,34 +864,27 @@ void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream)
m_url = content;
m_stream = stream;
- if (!src.isNull())
- playerPipeline.remove(src);
- if (!decoder.isNull())
- playerPipeline.remove(decoder);
- src = QGstElement();
+ removeDynamicPipelineElements();
disconnectDecoderHandlers();
- decoder = QGstElement();
removeAllOutputs();
seekableChanged(false);
- Q_ASSERT(playerPipeline.inStoppedState());
- if (m_duration != 0) {
- m_duration = 0;
- durationChanged(0);
+ if (m_duration != 0ms) {
+ m_duration = 0ms;
+ durationChanged(0ms);
}
stateChanged(QMediaPlayer::StoppedState);
if (position() != 0)
- positionChanged(0);
+ positionChanged(0ms);
if (!m_metaData.isEmpty()) {
m_metaData.clear();
metaDataChanged();
}
- if (content.isEmpty() && !stream)
+ if (content.isEmpty() && !stream) {
mediaStatusChanged(QMediaPlayer::NoMedia);
-
- if (content.isEmpty())
return;
+ }
if (m_stream) {
if (!m_appSrc) {
@@ -847,7 +899,7 @@ void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream)
src = m_appSrc->element();
decoder = QGstElement::createFromFactory("decodebin", "decoder");
if (!decoder) {
- error(QMediaPlayer::ResourceError, errorMessageCannotFindElement("decodebin"));
+ error(QMediaPlayer::ResourceError, qGstErrorMessageCannotFindElement("decodebin"));
return;
}
decoder.set("post-stream-topology", true);
@@ -867,7 +919,7 @@ void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream)
// use uridecodebin
decoder = QGstElement::createFromFactory("uridecodebin", "decoder");
if (!decoder) {
- error(QMediaPlayer::ResourceError, errorMessageCannotFindElement("uridecodebin"));
+ error(QMediaPlayer::ResourceError, qGstErrorMessageCannotFindElement("uridecodebin"));
return;
}
playerPipeline.add(decoder);
@@ -891,10 +943,7 @@ void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream)
constexpr int mb = 1024 * 1024;
decoder.set("ring-buffer-max-size", 2 * mb);
- if (m_bufferProgress != 0) {
- m_bufferProgress = 0;
- emit bufferProgressChanged(0.);
- }
+ updateBufferProgress(0.f);
elementAdded = decoder.connect("deep-element-added",
GCallback(decodebinElementAddedCallback), this);
@@ -905,14 +954,14 @@ void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream)
padRemoved = decoder.onPadRemoved<&QGstreamerMediaPlayer::decoderPadRemoved>(this);
mediaStatusChanged(QMediaPlayer::LoadingMedia);
- if (!playerPipeline.setState(GST_STATE_PAUSED)) {
+ if (!playerPipeline.setStateSync(GST_STATE_PAUSED)) {
qCWarning(qLcMediaPlayer) << "Unable to set the pipeline to the paused state.";
// Note: no further error handling: errors will be delivered via a GstMessage
return;
}
- playerPipeline.setPosition(0);
- positionChanged(0);
+ playerPipeline.setPosition(0ms);
+ positionChanged(0ms);
}
void QGstreamerMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
@@ -942,9 +991,9 @@ void QGstreamerMediaPlayer::setVideoSink(QVideoSink *sink)
gstVideoOutput->setVideoSink(sink);
}
-static QGstStructure endOfChain(const QGstStructure &s)
+static QGstStructureView endOfChain(const QGstStructureView &s)
{
- QGstStructure e = s;
+ QGstStructureView e = s;
while (1) {
auto next = e["next"].toStructure();
if (!next.isNull())
@@ -958,32 +1007,26 @@ static QGstStructure endOfChain(const QGstStructure &s)
void QGstreamerMediaPlayer::parseStreamsAndMetadata()
{
qCDebug(qLcMediaPlayer) << "============== parse topology ============";
- if (topology.isNull()) {
+
+ if (!topology) {
qCDebug(qLcMediaPlayer) << " null topology";
return;
}
- auto caps = topology["caps"].toCaps();
- auto structure = caps.at(0);
- auto fileFormat = QGstreamerFormatInfo::fileFormatForCaps(structure);
- qCDebug(qLcMediaPlayer) << caps << fileFormat;
- m_metaData.insert(QMediaMetaData::FileFormat, QVariant::fromValue(fileFormat));
- m_metaData.insert(QMediaMetaData::Duration, duration());
- m_metaData.insert(QMediaMetaData::Url, m_url);
- QGValue tags = topology["tags"];
- if (!tags.isNull()) {
- QGstTagListHandle tagList;
- gst_structure_get(topology.structure, "tags", GST_TYPE_TAG_LIST, &tagList, nullptr);
- const auto metaData = taglistToMetaData(tagList);
- for (auto k : metaData.keys())
- m_metaData.insert(k, metaData.value(k));
- }
+ QGstStructureView topologyView{ topology };
+
+ QGstCaps caps = topologyView.caps();
+ extendMetaDataFromCaps(m_metaData, caps);
+
+ QGstTagListHandle tagList = QGstStructureView{ topology }.tags();
+ if (tagList)
+ extendMetaDataFromTagList(m_metaData, tagList);
- auto demux = endOfChain(topology);
- auto next = demux["next"];
+ QGstStructureView demux = endOfChain(topologyView);
+ QGValue next = demux["next"];
if (!next.isList()) {
qCDebug(qLcMediaPlayer) << " no additional streams";
- emit metaDataChanged();
+ metaDataChanged();
return;
}
@@ -991,43 +1034,28 @@ void QGstreamerMediaPlayer::parseStreamsAndMetadata()
int size = next.listSize();
for (int i = 0; i < size; ++i) {
auto val = next.at(i);
- caps = val.toStructure()["caps"].toCaps();
- structure = caps.at(0);
- if (structure.name().startsWith("audio/")) {
- auto codec = QGstreamerFormatInfo::audioCodecForCaps(structure);
- m_metaData.insert(QMediaMetaData::AudioCodec, QVariant::fromValue(codec));
- qCDebug(qLcMediaPlayer) << " audio" << caps << (int)codec;
- } else if (structure.name().startsWith("video/")) {
- auto codec = QGstreamerFormatInfo::videoCodecForCaps(structure);
- m_metaData.insert(QMediaMetaData::VideoCodec, QVariant::fromValue(codec));
- qCDebug(qLcMediaPlayer) << " video" << caps << (int)codec;
- auto framerate = structure["framerate"].getFraction();
- if (framerate)
- m_metaData.insert(QMediaMetaData::VideoFrameRate, *framerate);
-
- QSize resolution = structure.resolution();
- if (resolution.isValid())
- m_metaData.insert(QMediaMetaData::Resolution, resolution);
+ caps = val.toStructure().caps();
+
+ extendMetaDataFromCaps(m_metaData, caps);
+ QGstStructureView structure = caps.at(0);
+
+ if (structure.name().startsWith("video/")) {
QSize nativeSize = structure.nativeSize();
gstVideoOutput->setNativeSize(nativeSize);
}
}
auto sinkPad = trackSelector(VideoStream).activeInputPad();
- if (!sinkPad.isNull()) {
- QGstTagListHandle tagList;
-
- g_object_get(sinkPad.object(), "tags", &tagList, nullptr);
+ if (sinkPad) {
+ QGstTagListHandle tagList = sinkPad.tags();
if (tagList)
qCDebug(qLcMediaPlayer) << " tags=" << tagList.get();
else
qCDebug(qLcMediaPlayer) << " tags=(null)";
}
-
qCDebug(qLcMediaPlayer) << "============== end parse topology ============";
- emit metaDataChanged();
playerPipeline.dumpGraph("playback");
}
@@ -1039,12 +1067,10 @@ int QGstreamerMediaPlayer::trackCount(QPlatformMediaPlayer::TrackType type)
QMediaMetaData QGstreamerMediaPlayer::trackMetaData(QPlatformMediaPlayer::TrackType type, int index)
{
auto track = trackSelector(type).inputPad(index);
- if (track.isNull())
+ if (!track)
return {};
- QGstTagListHandle tagList;
- g_object_get(track.object(), "tags", &tagList, nullptr);
-
+ QGstTagListHandle tagList = track.tags();
return taglistToMetaData(tagList);
}