Source/WebCore/ChangeLog

112016-10-04 Enrique Ocaña González <eocanha@igalia.com>
22
 3 [GStreamer][MSE] Playback pipeline
 4 https://bugs.webkit.org/show_bug.cgi?id=162901
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Encapsulate the responsibility to interact with the GStreamer playback pipeline and the
 9 different streams managed by the WebKitMediaSrc element into a new PlaybackPipeline class.
 10
 11 * platform/graphics/gstreamer/mse/PlaybackPipeline.cpp: Added.
 12 (getStreamByTrackId):
 13 (getStreamBySourceBufferPrivate):
 14 (pushSample):
 15 (WebCore::PlaybackPipeline::create):
 16 (WebCore::PlaybackPipeline::PlaybackPipeline):
 17 (WebCore::PlaybackPipeline::~PlaybackPipeline):
 18 (WebCore::PlaybackPipeline::setWebKitMediaSrc):
 19 (WebCore::PlaybackPipeline::webKitMediaSrc):
 20 (WebCore::PlaybackPipeline::addSourceBuffer):
 21 (WebCore::PlaybackPipeline::removeSourceBuffer):
 22 (WebCore::PlaybackPipeline::attachTrack):
 23 (WebCore::PlaybackPipeline::reattachTrack):
 24 (WebCore::PlaybackPipeline::notifyDurationChanged):
 25 (WebCore::PlaybackPipeline::markEndOfStream):
 26 (WebCore::PlaybackPipeline::flushAndEnqueueNonDisplayingSamples):
 27 (WebCore::PlaybackPipeline::enqueueSample):
 28 (WebCore::PlaybackPipeline::pipeline):
 29 * platform/graphics/gstreamer/mse/PlaybackPipeline.h: Added.
 30
 312016-10-04 Enrique Ocaña González <eocanha@igalia.com>
 32
333 [GStreamer][MSE][EME] MSE specialization of Media Player Private for GStreamer
434 https://bugs.webkit.org/show_bug.cgi?id=162900
535

Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp

 1/*
 2 * Copyright (C) 2014, 2015 Sebastian Dröge <sebastian@centricular.com>
 3 * Copyright (C) 2016 Metrological Group B.V.
 4 * Copyright (C) 2016 Igalia S.L
 5 *
 6 * This library is free software; you can redistribute it and/or
 7 * modify it under the terms of the GNU Library General Public
 8 * License as published by the Free Software Foundation; either
 9 * version 2 of the License, or (at your option) any later version.
 10 *
 11 * This library is distributed in the hope that it will be useful,
 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 14 * Library General Public License for more details.
 15 *
 16 * You should have received a copy of the GNU Library General Public License
 17 * aint with this library; see the file COPYING.LIB. If not, write to
 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 19 * Boston, MA 02110-1301, USA.
 20 */
 21
 22#include "config.h"
 23#include "PlaybackPipeline.h"
 24
 25#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
 26
 27#include "AudioTrackPrivateGStreamer.h"
 28#include "GStreamerMediaSample.h"
 29#include "GStreamerUtilities.h"
 30#include "MediaSample.h"
 31#include "SourceBufferPrivateGStreamer.h"
 32#include "VideoTrackPrivateGStreamer.h"
 33
 34#include <gst/app/gstappsrc.h>
 35#include <gst/gst.h>
 36#include <wtf/MainThread.h>
 37#include <wtf/RefCounted.h>
 38#include <wtf/glib/GMutexLocker.h>
 39#include <wtf/glib/GRefPtr.h>
 40#include <wtf/glib/GUniquePtr.h>
 41#include <wtf/text/AtomicString.h>
 42
 43static Stream* getStreamByTrackId(WebKitMediaSrc*, AtomicString);
 44static Stream* getStreamBySourceBufferPrivate(WebKitMediaSrc*, WebCore::SourceBufferPrivateGStreamer*);
 45
 46static Stream* getStreamByTrackId(WebKitMediaSrc* source, AtomicString trackIdString)
 47{
 48 // WebKitMediaSrc should be locked at this point.
 49 for (Stream* stream : source->priv->streams) {
 50 if (stream->type != WebCore::Invalid
 51 && ((stream->audioTrack && stream->audioTrack->id() == trackIdString)
 52 || (stream->videoTrack && stream->videoTrack->id() == trackIdString) ) ) {
 53 return stream;
 54 }
 55 }
 56 return nullptr;
 57}
 58
 59static Stream* getStreamBySourceBufferPrivate(WebKitMediaSrc* source, WebCore::SourceBufferPrivateGStreamer* sourceBufferPrivate)
 60{
 61 for (Stream* stream : source->priv->streams) {
 62 if (stream->sourceBuffer == sourceBufferPrivate)
 63 return stream;
 64 }
 65 return nullptr;
 66}
 67
 68// FIXME: Use gst_app_src_push_sample() instead when we switch to the appropriate GStreamer version.
 69static GstFlowReturn pushSample(GstAppSrc* appsrc, GstSample* sample)
 70{
 71 g_return_val_if_fail(GST_IS_SAMPLE(sample), GST_FLOW_ERROR);
 72
 73 GstCaps* caps = gst_sample_get_caps(sample);
 74 if (caps)
 75 gst_app_src_set_caps(appsrc, caps);
 76 else
 77 GST_WARNING_OBJECT(appsrc, "received sample without caps");
 78
 79 GstBuffer* buffer = gst_sample_get_buffer(sample);
 80 if (UNLIKELY(!buffer)) {
 81 GST_WARNING_OBJECT(appsrc, "received sample without buffer");
 82 return GST_FLOW_OK;
 83 }
 84
 85 // gst_app_src_push_buffer() steals the reference, we need an additional one.
 86 return gst_app_src_push_buffer(appsrc, gst_buffer_ref(buffer));
 87}
 88
 89namespace WebCore {
 90
 91PassRefPtr<PlaybackPipeline> PlaybackPipeline::create()
 92{
 93 return adoptRef(new PlaybackPipeline());
 94}
 95
 96PlaybackPipeline::PlaybackPipeline()
 97 : RefCounted<PlaybackPipeline>()
 98{
 99}
 100
 101PlaybackPipeline::~PlaybackPipeline()
 102{
 103}
 104
 105void PlaybackPipeline::setWebKitMediaSrc(WebKitMediaSrc* webKitMediaSrc)
 106{
 107 GST_DEBUG("webKitMediaSrc=%p", webKitMediaSrc);
 108 m_webKitMediaSrc = webKitMediaSrc;
 109}
 110
 111WebKitMediaSrc* PlaybackPipeline::webKitMediaSrc()
 112{
 113 return m_webKitMediaSrc.get();
 114}
 115
 116MediaSourcePrivate::AddStatus PlaybackPipeline::addSourceBuffer(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate)
 117{
 118 WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv;
 119
 120 if (priv->allTracksConfigured) {
 121 GST_ERROR_OBJECT(m_webKitMediaSrc.get(), "Adding new source buffers after first data not supported yet");
 122 return MediaSourcePrivate::NotSupported;
 123 }
 124
 125 GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "State %d", int(GST_STATE(m_webKitMediaSrc.get())));
 126
 127 Stream* stream = new Stream { 0 };
 128 stream->parent = m_webKitMediaSrc.get();
 129 stream->appsrc = gst_element_factory_make("appsrc", nullptr);
 130 stream->appsrcNeedDataFlag = false;
 131 stream->sourceBuffer = sourceBufferPrivate.get();
 132
 133 // No track has been attached yet.
 134 stream->type = Invalid;
 135 stream->parser = nullptr;
 136 stream->caps = nullptr;
 137#if ENABLE(VIDEO_TRACK)
 138 stream->audioTrack = nullptr;
 139 stream->videoTrack = nullptr;
 140#endif
 141 stream->presentationSize = WebCore::FloatSize();
 142 stream->lastEnqueuedTime = MediaTime::invalidTime();
 143
 144 gst_app_src_set_callbacks(GST_APP_SRC(stream->appsrc), &enabledAppsrcCallbacks, stream->parent, nullptr);
 145 gst_app_src_set_emit_signals(GST_APP_SRC(stream->appsrc), FALSE);
 146 gst_app_src_set_stream_type(GST_APP_SRC(stream->appsrc), GST_APP_STREAM_TYPE_SEEKABLE);
 147
 148 gst_app_src_set_max_bytes(GST_APP_SRC(stream->appsrc), 2 * WTF::MB);
 149 g_object_set(G_OBJECT(stream->appsrc), "block", FALSE, "min-percent", 20, nullptr);
 150
 151 GST_OBJECT_LOCK(m_webKitMediaSrc.get());
 152 priv->streams.prepend(stream);
 153 GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
 154
 155 gst_bin_add(GST_BIN(m_webKitMediaSrc.get()), stream->appsrc);
 156 gst_element_sync_state_with_parent(stream->appsrc);
 157
 158 return MediaSourcePrivate::Ok;
 159}
 160
 161void PlaybackPipeline::removeSourceBuffer(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate)
 162{
 163 ASSERT(WTF::isMainThread());
 164
 165 GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Element removed from MediaSource");
 166 GST_OBJECT_LOCK(m_webKitMediaSrc.get());
 167 WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv;
 168 Stream* stream = nullptr;
 169 Deque<Stream*>::iterator streamPosition = priv->streams.begin();
 170
 171 for (; streamPosition != priv->streams.end(); ++streamPosition) {
 172 if ((*streamPosition)->sourceBuffer == sourceBufferPrivate.get()) {
 173 stream = *streamPosition;
 174 break;
 175 }
 176 }
 177 if (stream)
 178 priv->streams.remove(streamPosition);
 179 GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
 180
 181 if (stream)
 182 webKitMediaSrcFreeStream(m_webKitMediaSrc.get(), stream);
 183}
 184
 185void PlaybackPipeline::attachTrack(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, RefPtr<TrackPrivateBase> trackPrivate, GstStructure* structure, GstCaps* caps)
 186{
 187 WebKitMediaSrc* webKitMediaSrc = m_webKitMediaSrc.get();
 188
 189 GST_OBJECT_LOCK(webKitMediaSrc);
 190 Stream* stream = getStreamBySourceBufferPrivate(webKitMediaSrc, sourceBufferPrivate.get());
 191 GST_OBJECT_UNLOCK(webKitMediaSrc);
 192
 193 ASSERT(stream);
 194
 195 GST_OBJECT_LOCK(webKitMediaSrc);
 196 unsigned padId = stream->parent->priv->numberOfPads;
 197 stream->parent->priv->numberOfPads++;
 198 GST_OBJECT_UNLOCK(webKitMediaSrc);
 199
 200 const gchar* mediaType = gst_structure_get_name(structure);
 201
 202 GST_DEBUG_OBJECT(webKitMediaSrc, "Configured track %s: appsrc=%s, padId=%u, mediaType=%s", trackPrivate->id().string().utf8().data(), GST_ELEMENT_NAME(stream->appsrc), padId, mediaType);
 203
 204 GUniquePtr<gchar> parserBinName(g_strdup_printf("streamparser%u", padId));
 205
 206 if (!g_strcmp0(mediaType, "video/x-h264")) {
 207 GRefPtr<GstCaps> filterCaps = adoptGRef(gst_caps_new_simple("video/x-h264", "alignment", G_TYPE_STRING, "au", nullptr));
 208 GstElement* capsfilter = gst_element_factory_make("capsfilter", nullptr);
 209 g_object_set(capsfilter, "caps", filterCaps.get(), nullptr);
 210
 211 stream->parser = gst_bin_new(parserBinName.get());
 212
 213 GstElement* parser = gst_element_factory_make("h264parse", nullptr);
 214 gst_bin_add_many(GST_BIN(stream->parser), parser, capsfilter, nullptr);
 215 gst_element_link_pads(parser, "src", capsfilter, "sink");
 216
 217 GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink"));
 218 gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get()));
 219
 220 pad = adoptGRef(gst_element_get_static_pad(capsfilter, "src"));
 221 gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get()));
 222 } else if (!g_strcmp0(mediaType, "video/x-h265")) {
 223 GRefPtr<GstCaps> filterCaps = adoptGRef(gst_caps_new_simple("video/x-h265", "alignment", G_TYPE_STRING, "au", nullptr));
 224 GstElement* capsfilter = gst_element_factory_make("capsfilter", nullptr);
 225 g_object_set(capsfilter, "caps", filterCaps.get(), nullptr);
 226
 227 stream->parser = gst_bin_new(parserBinName.get());
 228
 229 GstElement* parser = gst_element_factory_make("h265parse", nullptr);
 230 gst_bin_add_many(GST_BIN(stream->parser), parser, capsfilter, nullptr);
 231 gst_element_link_pads(parser, "src", capsfilter, "sink");
 232
 233 GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink"));
 234 gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get()));
 235
 236 pad = adoptGRef(gst_element_get_static_pad(capsfilter, "src"));
 237 gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get()));
 238 } else if (!g_strcmp0(mediaType, "audio/mpeg")) {
 239 gint mpegversion = -1;
 240 gst_structure_get_int(structure, "mpegversion", &mpegversion);
 241
 242 GstElement* parser;
 243 if (mpegversion == 1)
 244 parser = gst_element_factory_make("mpegaudioparse", nullptr);
 245 else if (mpegversion == 2 || mpegversion == 4)
 246 parser = gst_element_factory_make("aacparse", nullptr);
 247 else
 248 ASSERT_NOT_REACHED();
 249
 250 stream->parser = gst_bin_new(parserBinName.get());
 251 gst_bin_add(GST_BIN(stream->parser), parser);
 252
 253 GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink"));
 254 gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get()));
 255
 256 pad = adoptGRef(gst_element_get_static_pad(parser, "src"));
 257 gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get()));
 258 } else if (!g_strcmp0(mediaType, "video/x-vp9"))
 259 stream->parser = nullptr;
 260 else {
 261 GST_ERROR_OBJECT(stream->parent, "Unsupported media format: %s", mediaType);
 262 return;
 263 }
 264
 265 GST_OBJECT_LOCK(webKitMediaSrc);
 266 stream->type = Unknown;
 267 GST_OBJECT_UNLOCK(webKitMediaSrc);
 268
 269 GRefPtr<GstPad> sourcePad;
 270 if (stream->parser) {
 271 gst_bin_add(GST_BIN(stream->parent), stream->parser);
 272 gst_element_sync_state_with_parent(stream->parser);
 273
 274 GRefPtr<GstPad> sinkPad = adoptGRef(gst_element_get_static_pad(stream->parser, "sink"));
 275 sourcePad = adoptGRef(gst_element_get_static_pad(stream->appsrc, "src"));
 276 gst_pad_link(sourcePad.get(), sinkPad.get());
 277 sourcePad = adoptGRef(gst_element_get_static_pad(stream->parser, "src"));
 278 } else {
 279 GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Stream of type %s doesn't require a parser bin", mediaType);
 280 sourcePad = adoptGRef(gst_element_get_static_pad(stream->appsrc, "src"));
 281 }
 282 ASSERT(sourcePad);
 283
 284 // FIXME: Is padId the best way to identify the Stream? What about trackId?
 285 g_object_set_data(G_OBJECT(sourcePad.get()), "padId", GINT_TO_POINTER(padId));
 286 webKitMediaSrcLinkParser(sourcePad.get(), caps, stream);
 287
 288 ASSERT(stream->parent->priv->mediaPlayerPrivate);
 289 int signal = -1;
 290 if (g_str_has_prefix(mediaType, "audio")) {
 291 GST_OBJECT_LOCK(webKitMediaSrc);
 292 stream->type = Audio;
 293 stream->parent->priv->numberOfAudioStreams++;
 294 GST_OBJECT_UNLOCK(webKitMediaSrc);
 295 signal = SIGNAL_AUDIO_CHANGED;
 296
 297 stream->audioTrack = RefPtr<WebCore::AudioTrackPrivateGStreamer>(static_cast<WebCore::AudioTrackPrivateGStreamer*>(trackPrivate.get()));
 298 } else if (g_str_has_prefix(mediaType, "video")) {
 299 GST_OBJECT_LOCK(webKitMediaSrc);
 300 stream->type = Video;
 301 stream->parent->priv->numberOfVideoStreams++;
 302 GST_OBJECT_UNLOCK(webKitMediaSrc);
 303 signal = SIGNAL_VIDEO_CHANGED;
 304
 305 stream->videoTrack = RefPtr<WebCore::VideoTrackPrivateGStreamer>(static_cast<WebCore::VideoTrackPrivateGStreamer*>(trackPrivate.get()));
 306 } else if (g_str_has_prefix(mediaType, "text")) {
 307 GST_OBJECT_LOCK(webKitMediaSrc);
 308 stream->type = Text;
 309 stream->parent->priv->numberOfTextStreams++;
 310 GST_OBJECT_UNLOCK(webKitMediaSrc);
 311 signal = SIGNAL_TEXT_CHANGED;
 312
 313 // FIXME: Support text tracks.
 314 }
 315
 316 if (signal != -1)
 317 g_signal_emit(G_OBJECT(stream->parent), webKitMediaSrcSignals[signal], 0, nullptr);
 318}
 319
 320void PlaybackPipeline::reattachTrack(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, RefPtr<TrackPrivateBase> trackPrivate)
 321{
 322 GST_DEBUG("Re-attaching track");
 323
 324 // FIXME: Maybe remove this method. Now the caps change is managed by gst_appsrc_push_sample() in enqueueSample()
 325 // and flushAndEnqueueNonDisplayingSamples().
 326
 327 WebKitMediaSrc* webKitMediaSrc = m_webKitMediaSrc.get();
 328
 329 GST_OBJECT_LOCK(webKitMediaSrc);
 330 Stream* stream = getStreamBySourceBufferPrivate(webKitMediaSrc, sourceBufferPrivate.get());
 331 GST_OBJECT_UNLOCK(webKitMediaSrc);
 332
 333 ASSERT(stream && stream->type != Invalid);
 334
 335 GRefPtr<GstCaps> oldAppsrcCaps = adoptGRef(gst_app_src_get_caps(GST_APP_SRC(stream->appsrc)));
 336 // The caps change is managed by gst_appsrc_push_sample() in enqueueSample() and
 337 // flushAndEnqueueNonDisplayingSamples(), so the caps aren't set from here.
 338 GRefPtr<GstCaps> appsrcCaps = adoptGRef(gst_app_src_get_caps(GST_APP_SRC(stream->appsrc)));
 339 const gchar* mediaType = gst_structure_get_name(gst_caps_get_structure(appsrcCaps.get(), 0));
 340
 341 if (!gst_caps_is_equal(oldAppsrcCaps.get(), appsrcCaps.get())) {
 342 GST_DEBUG("Caps have changed, but reconstructing the sequence of elements is not supported yet");
 343
 344#ifndef GST_DISABLE_GST_DEBUG
 345 GUniquePtr<gchar> stroldcaps(gst_caps_to_string(oldAppsrcCaps.get()));
 346 GUniquePtr<gchar> strnewcaps(gst_caps_to_string(appsrcCaps.get()));
 347 GST_DEBUG("oldcaps: %s", stroldcaps.get());
 348 GST_DEBUG("newcaps: %s", strnewcaps.get());
 349#endif
 350 }
 351
 352 int signal = -1;
 353
 354 GST_OBJECT_LOCK(webKitMediaSrc);
 355 if (g_str_has_prefix(mediaType, "audio")) {
 356 ASSERT(stream->type == Audio);
 357 signal = SIGNAL_AUDIO_CHANGED;
 358 stream->audioTrack = RefPtr<WebCore::AudioTrackPrivateGStreamer>(static_cast<WebCore::AudioTrackPrivateGStreamer*>(trackPrivate.get()));
 359 } else if (g_str_has_prefix(mediaType, "video")) {
 360 ASSERT(stream->type == Video);
 361 signal = SIGNAL_VIDEO_CHANGED;
 362 stream->videoTrack = RefPtr<WebCore::VideoTrackPrivateGStreamer>(static_cast<WebCore::VideoTrackPrivateGStreamer*>(trackPrivate.get()));
 363 } else if (g_str_has_prefix(mediaType, "text")) {
 364 ASSERT(stream->type == Text);
 365 signal = SIGNAL_TEXT_CHANGED;
 366
 367 // FIXME: Support text tracks.
 368 }
 369 GST_OBJECT_UNLOCK(webKitMediaSrc);
 370
 371 if (signal != -1)
 372 g_signal_emit(G_OBJECT(stream->parent), webKitMediaSrcSignals[signal], 0, nullptr);
 373}
 374
 375void PlaybackPipeline::notifyDurationChanged()
 376{
 377 gst_element_post_message(GST_ELEMENT(m_webKitMediaSrc.get()), gst_message_new_duration_changed(GST_OBJECT(m_webKitMediaSrc.get())));
 378 // WebKitMediaSrc will ask MediaPlayerPrivateGStreamerMSE for the new duration later, when somebody asks for it.
 379}
 380
 381void PlaybackPipeline::markEndOfStream(MediaSourcePrivate::EndOfStreamStatus)
 382{
 383 WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv;
 384
 385 GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Have EOS");
 386
 387 GST_OBJECT_LOCK(m_webKitMediaSrc.get());
 388 bool allTracksConfigured = priv->allTracksConfigured;
 389 if (!allTracksConfigured)
 390 priv->allTracksConfigured = true;
 391 GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
 392
 393 if (!allTracksConfigured) {
 394 gst_element_no_more_pads(GST_ELEMENT(m_webKitMediaSrc.get()));
 395 webKitMediaSrcDoAsyncDone(m_webKitMediaSrc.get());
 396 }
 397
 398 Vector<GstAppSrc*> appsrcs;
 399
 400 GST_OBJECT_LOCK(m_webKitMediaSrc.get());
 401 for (Stream* stream : priv->streams) {
 402 if (stream->appsrc)
 403 appsrcs.append(GST_APP_SRC(stream->appsrc));
 404 }
 405 GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
 406
 407 for (GstAppSrc* appsrc : appsrcs)
 408 gst_app_src_end_of_stream(appsrc);
 409}
 410
 411void PlaybackPipeline::flushAndEnqueueNonDisplayingSamples(Vector<RefPtr<MediaSample>> samples)
 412{
 413 ASSERT(WTF::isMainThread());
 414
 415 if (!samples.size()) {
 416 GST_DEBUG("No samples, trackId unknown");
 417 return;
 418 }
 419
 420 AtomicString trackId = samples[0]->trackID();
 421 GST_DEBUG("flushAndEnqueueNonDisplayingSamples: trackId=%s PTS[0]=%f ... PTS[n]=%f", trackId.string().utf8().data(), samples[0]->presentationTime().toFloat(), samples[samples.size()-1]->presentationTime().toFloat());
 422
 423 GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Flushing and re-enqueing %zu samples for stream %s", samples.size(), trackId.string().utf8().data());
 424
 425 GST_OBJECT_LOCK(m_webKitMediaSrc.get());
 426 Stream* stream = getStreamByTrackId(m_webKitMediaSrc.get(), trackId);
 427
 428 if (!stream) {
 429 GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
 430 return;
 431 }
 432
 433 if (!stream->sourceBuffer->isReadyForMoreSamples(trackId)) {
 434 GST_DEBUG("flushAndEnqueueNonDisplayingSamples: skip adding new sample for trackId=%s, SB is not ready yet", trackId.string().utf8().data());
 435 GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
 436 return;
 437 }
 438
 439 GstElement* appsrc = stream->appsrc;
 440 MediaTime lastEnqueuedTime = stream->lastEnqueuedTime;
 441 GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
 442
 443 if (!m_webKitMediaSrc->priv->mediaPlayerPrivate->seeking())
 444 GST_DEBUG("flushAndEnqueueNonDisplayingSamples: trackId=%s pipeline needs flushing.", trackId.string().utf8().data());
 445
 446 for (const RefPtr<MediaSample>& sample : samples) {
 447 GStreamerMediaSample* gStreamerMediaSample = static_cast<GStreamerMediaSample*>(sample.get());
 448 GstBuffer* buffer = nullptr;
 449 if (gStreamerMediaSample->sample())
 450 buffer = gst_sample_get_buffer(gStreamerMediaSample->sample());
 451 if (buffer) {
 452 GstSample* gstSample = gst_sample_ref(gStreamerMediaSample->sample());
 453 lastEnqueuedTime = gStreamerMediaSample->presentationTime();
 454
 455 GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_DECODE_ONLY);
 456 pushSample(GST_APP_SRC(appsrc), gstSample);
 457 // gst_app_src_push_sample() uses transfer-none for gstSample.
 458
 459 gst_sample_unref(gstSample);
 460 }
 461 }
 462 GST_OBJECT_LOCK(m_webKitMediaSrc.get());
 463 stream->lastEnqueuedTime = lastEnqueuedTime;
 464 GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
 465}
 466
 467void PlaybackPipeline::enqueueSample(PassRefPtr<MediaSample> prSample)
 468{
 469 ASSERT(WTF::isMainThread());
 470
 471 RefPtr<MediaSample> protectedPrSample = prSample;
 472 AtomicString trackId = protectedPrSample->trackID();
 473
 474 GST_TRACE("enqueing sample trackId=%s PTS=%f presentationSize=%.0fx%.0f at %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT,
 475 trackId.string().utf8().data(), protectedPrSample->presentationTime().toFloat(),
 476 protectedPrSample->presentationSize().width(), protectedPrSample->presentationSize().height(),
 477 GST_TIME_ARGS(WebCore::toGstClockTime(protectedPrSample->presentationTime().toDouble())),
 478 GST_TIME_ARGS(WebCore::toGstClockTime(protectedPrSample->duration().toDouble())));
 479
 480 Stream* stream = getStreamByTrackId(m_webKitMediaSrc.get(), trackId);
 481
 482 if (!stream) {
 483 GST_WARNING("No stream!");
 484 return;
 485 }
 486
 487 if (!stream->sourceBuffer->isReadyForMoreSamples(trackId)) {
 488 GST_DEBUG("enqueueSample: skip adding new sample for trackId=%s, SB is not ready yet", trackId.string().utf8().data());
 489 return;
 490 }
 491
 492 GstElement* appsrc = stream->appsrc;
 493 MediaTime lastEnqueuedTime = stream->lastEnqueuedTime;
 494
 495 GStreamerMediaSample* sample = static_cast<GStreamerMediaSample*>(protectedPrSample.get());
 496 if (sample->sample() && gst_sample_get_buffer(sample->sample())) {
 497 GRefPtr<GstSample> gstSample = sample->sample();
 498 GstBuffer* buffer = gst_sample_get_buffer(gstSample.get());
 499 lastEnqueuedTime = sample->presentationTime();
 500
 501 GST_BUFFER_FLAG_UNSET(buffer, GST_BUFFER_FLAG_DECODE_ONLY);
 502 pushSample(GST_APP_SRC(appsrc), gstSample.get());
 503 // gst_app_src_push_sample() uses transfer-none for gstSample.
 504
 505 stream->lastEnqueuedTime = lastEnqueuedTime;
 506 }
 507}
 508
 509GstElement* PlaybackPipeline::pipeline()
 510{
 511 if (!m_webKitMediaSrc || !GST_ELEMENT_PARENT(GST_ELEMENT(m_webKitMediaSrc.get())))
 512 return nullptr;
 513
 514 return GST_ELEMENT_PARENT(GST_ELEMENT_PARENT(GST_ELEMENT(m_webKitMediaSrc.get())));
 515}
 516
 517} // namespace WebCore.
 518
 519#endif // USE(GSTREAMER)

Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h

 1/*
 2 * Copyright (C) 2016 Metrological Group B.V.
 3 * Copyright (C) 2016 Igalia S.L
 4 *
 5 * This library is free software; you can redistribute it and/or
 6 * modify it under the terms of the GNU Library General Public
 7 * License as published by the Free Software Foundation; either
 8 * version 2 of the License, or (at your option) any later version.
 9 *
 10 * This library is distributed in the hope that it will be useful,
 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 13 * Library General Public License for more details.
 14 *
 15 * You should have received a copy of the GNU Library General Public License
 16 * aint with this library; see the file COPYING.LIB. If not, write to
 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 18 * Boston, MA 02110-1301, USA.
 19 */
 20
 21#pragma once
 22
 23#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
 24
 25// PlaybackPipeline is (sort of) a friend class of WebKitMediaSourceGStreamer.
 26
 27#include "WebKitMediaSourceGStreamer.h"
 28#include "WebKitMediaSourceGStreamerPrivate.h"
 29
 30#include <gst/gst.h>
 31#include <wtf/Condition.h>
 32#include <wtf/glib/GRefPtr.h>
 33
 34namespace WTF {
 35template<> GRefPtr<WebKitMediaSrc> adoptGRef(WebKitMediaSrc*);
 36template<> WebKitMediaSrc* refGPtr<WebKitMediaSrc>(WebKitMediaSrc*);
 37template<> void derefGPtr<WebKitMediaSrc>(WebKitMediaSrc*);
 38};
 39
 40namespace WebCore {
 41
 42class ContentType;
 43class SourceBufferPrivateGStreamer;
 44class MediaSourceGStreamer;
 45
 46class PlaybackPipeline: public RefCounted<PlaybackPipeline> {
 47public:
 48 static PassRefPtr<PlaybackPipeline> create();
 49 virtual ~PlaybackPipeline();
 50
 51 void setWebKitMediaSrc(WebKitMediaSrc*);
 52 WebKitMediaSrc* webKitMediaSrc();
 53
 54 MediaSourcePrivate::AddStatus addSourceBuffer(RefPtr<SourceBufferPrivateGStreamer>);
 55 void removeSourceBuffer(RefPtr<SourceBufferPrivateGStreamer>);
 56 void attachTrack(RefPtr<SourceBufferPrivateGStreamer>, RefPtr<TrackPrivateBase>, GstStructure*, GstCaps*);
 57 void reattachTrack(RefPtr<SourceBufferPrivateGStreamer>, RefPtr<TrackPrivateBase>);
 58 void notifyDurationChanged();
 59
 60 // From MediaSourceGStreamer.
 61 void markEndOfStream(MediaSourcePrivate::EndOfStreamStatus);
 62
 63 // From SourceBufferPrivateGStreamer.
 64 void flushAndEnqueueNonDisplayingSamples(Vector<RefPtr<MediaSample>>);
 65 void enqueueSample(PassRefPtr<MediaSample>);
 66
 67 GstElement* pipeline();
 68private:
 69 PlaybackPipeline();
 70 GRefPtr<WebKitMediaSrc> m_webKitMediaSrc;
 71};
 72
 73} // namespace WebCore.
 74
 75#endif // USE(GSTREAMER)