Stop using PassRefPtr in platform/graphics
[WebKit-https.git] / 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 "GUniquePtrGStreamer.h"
31 #include "MediaSample.h"
32 #include "SourceBufferPrivateGStreamer.h"
33 #include "VideoTrackPrivateGStreamer.h"
34
35 #include <gst/app/gstappsrc.h>
36 #include <gst/gst.h>
37 #include <wtf/MainThread.h>
38 #include <wtf/RefCounted.h>
39 #include <wtf/glib/GMutexLocker.h>
40 #include <wtf/glib/GRefPtr.h>
41 #include <wtf/glib/GUniquePtr.h>
42 #include <wtf/text/AtomicString.h>
43
44 GST_DEBUG_CATEGORY_EXTERN(webkit_mse_debug);
45 #define GST_CAT_DEFAULT webkit_mse_debug
46
47 static Stream* getStreamByTrackId(WebKitMediaSrc*, AtomicString);
48 static Stream* getStreamBySourceBufferPrivate(WebKitMediaSrc*, WebCore::SourceBufferPrivateGStreamer*);
49
50 static Stream* getStreamByTrackId(WebKitMediaSrc* source, AtomicString trackIdString)
51 {
52     // WebKitMediaSrc should be locked at this point.
53     for (Stream* stream : source->priv->streams) {
54         if (stream->type != WebCore::Invalid
55             && ((stream->audioTrack && stream->audioTrack->id() == trackIdString)
56                 || (stream->videoTrack && stream->videoTrack->id() == trackIdString) ) ) {
57             return stream;
58         }
59     }
60     return nullptr;
61 }
62
63 static Stream* getStreamBySourceBufferPrivate(WebKitMediaSrc* source, WebCore::SourceBufferPrivateGStreamer* sourceBufferPrivate)
64 {
65     for (Stream* stream : source->priv->streams) {
66         if (stream->sourceBuffer == sourceBufferPrivate)
67             return stream;
68     }
69     return nullptr;
70 }
71
72 // FIXME: Use gst_app_src_push_sample() instead when we switch to the appropriate GStreamer version.
73 static GstFlowReturn pushSample(GstAppSrc* appsrc, GstSample* sample)
74 {
75     g_return_val_if_fail(GST_IS_SAMPLE(sample), GST_FLOW_ERROR);
76
77     GstCaps* caps = gst_sample_get_caps(sample);
78     if (caps)
79         gst_app_src_set_caps(appsrc, caps);
80     else
81         GST_WARNING_OBJECT(appsrc, "received sample without caps");
82
83     GstBuffer* buffer = gst_sample_get_buffer(sample);
84     if (UNLIKELY(!buffer)) {
85         GST_WARNING_OBJECT(appsrc, "received sample without buffer");
86         return GST_FLOW_OK;
87     }
88
89     // gst_app_src_push_buffer() steals the reference, we need an additional one.
90     return gst_app_src_push_buffer(appsrc, gst_buffer_ref(buffer));
91 }
92
93 namespace WebCore {
94
95 void PlaybackPipeline::setWebKitMediaSrc(WebKitMediaSrc* webKitMediaSrc)
96 {
97     GST_DEBUG("webKitMediaSrc=%p", webKitMediaSrc);
98     m_webKitMediaSrc = webKitMediaSrc;
99 }
100
101 WebKitMediaSrc* PlaybackPipeline::webKitMediaSrc()
102 {
103     return m_webKitMediaSrc.get();
104 }
105
106 MediaSourcePrivate::AddStatus PlaybackPipeline::addSourceBuffer(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate)
107 {
108     WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv;
109
110     if (priv->allTracksConfigured) {
111         GST_ERROR_OBJECT(m_webKitMediaSrc.get(), "Adding new source buffers after first data not supported yet");
112         return MediaSourcePrivate::NotSupported;
113     }
114
115     GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "State %d", int(GST_STATE(m_webKitMediaSrc.get())));
116
117     Stream* stream = new Stream{ };
118     stream->parent = m_webKitMediaSrc.get();
119     stream->appsrc = gst_element_factory_make("appsrc", nullptr);
120     stream->appsrcNeedDataFlag = false;
121     stream->sourceBuffer = sourceBufferPrivate.get();
122
123     // No track has been attached yet.
124     stream->type = Invalid;
125     stream->parser = nullptr;
126     stream->caps = nullptr;
127     stream->audioTrack = nullptr;
128     stream->videoTrack = nullptr;
129     stream->presentationSize = WebCore::FloatSize();
130     stream->lastEnqueuedTime = MediaTime::invalidTime();
131
132     gst_app_src_set_callbacks(GST_APP_SRC(stream->appsrc), &enabledAppsrcCallbacks, stream->parent, nullptr);
133     gst_app_src_set_emit_signals(GST_APP_SRC(stream->appsrc), FALSE);
134     gst_app_src_set_stream_type(GST_APP_SRC(stream->appsrc), GST_APP_STREAM_TYPE_SEEKABLE);
135
136     gst_app_src_set_max_bytes(GST_APP_SRC(stream->appsrc), 2 * WTF::MB);
137     g_object_set(G_OBJECT(stream->appsrc), "block", FALSE, "min-percent", 20, "format", GST_FORMAT_TIME, nullptr);
138
139     GST_OBJECT_LOCK(m_webKitMediaSrc.get());
140     priv->streams.prepend(stream);
141     GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
142
143     gst_bin_add(GST_BIN(m_webKitMediaSrc.get()), stream->appsrc);
144     gst_element_sync_state_with_parent(stream->appsrc);
145
146     return MediaSourcePrivate::Ok;
147 }
148
149 void PlaybackPipeline::removeSourceBuffer(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate)
150 {
151     ASSERT(WTF::isMainThread());
152
153     GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Element removed from MediaSource");
154     GST_OBJECT_LOCK(m_webKitMediaSrc.get());
155     WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv;
156     Stream* stream = nullptr;
157     Deque<Stream*>::iterator streamPosition = priv->streams.begin();
158
159     for (; streamPosition != priv->streams.end(); ++streamPosition) {
160         if ((*streamPosition)->sourceBuffer == sourceBufferPrivate.get()) {
161             stream = *streamPosition;
162             break;
163         }
164     }
165     if (stream)
166         priv->streams.remove(streamPosition);
167     GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
168
169     if (stream)
170         webKitMediaSrcFreeStream(m_webKitMediaSrc.get(), stream);
171 }
172
173 void PlaybackPipeline::attachTrack(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, RefPtr<TrackPrivateBase> trackPrivate, GstStructure* structure, GstCaps* caps)
174 {
175     WebKitMediaSrc* webKitMediaSrc = m_webKitMediaSrc.get();
176
177     GST_OBJECT_LOCK(webKitMediaSrc);
178     Stream* stream = getStreamBySourceBufferPrivate(webKitMediaSrc, sourceBufferPrivate.get());
179     GST_OBJECT_UNLOCK(webKitMediaSrc);
180
181     ASSERT(stream);
182
183     GST_OBJECT_LOCK(webKitMediaSrc);
184     unsigned padId = stream->parent->priv->numberOfPads;
185     stream->parent->priv->numberOfPads++;
186     GST_OBJECT_UNLOCK(webKitMediaSrc);
187
188     const gchar* mediaType = gst_structure_get_name(structure);
189
190     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);
191
192     GUniquePtr<gchar> parserBinName(g_strdup_printf("streamparser%u", padId));
193
194     if (!g_strcmp0(mediaType, "video/x-h264")) {
195         GRefPtr<GstCaps> filterCaps = adoptGRef(gst_caps_new_simple("video/x-h264", "alignment", G_TYPE_STRING, "au", nullptr));
196         GstElement* capsfilter = gst_element_factory_make("capsfilter", nullptr);
197         g_object_set(capsfilter, "caps", filterCaps.get(), nullptr);
198
199         stream->parser = gst_bin_new(parserBinName.get());
200
201         GstElement* parser = gst_element_factory_make("h264parse", nullptr);
202         gst_bin_add_many(GST_BIN(stream->parser), parser, capsfilter, nullptr);
203         gst_element_link_pads(parser, "src", capsfilter, "sink");
204
205         GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink"));
206         gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get()));
207
208         pad = adoptGRef(gst_element_get_static_pad(capsfilter, "src"));
209         gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get()));
210     } else if (!g_strcmp0(mediaType, "video/x-h265")) {
211         GRefPtr<GstCaps> filterCaps = adoptGRef(gst_caps_new_simple("video/x-h265", "alignment", G_TYPE_STRING, "au", nullptr));
212         GstElement* capsfilter = gst_element_factory_make("capsfilter", nullptr);
213         g_object_set(capsfilter, "caps", filterCaps.get(), nullptr);
214
215         stream->parser = gst_bin_new(parserBinName.get());
216
217         GstElement* parser = gst_element_factory_make("h265parse", nullptr);
218         gst_bin_add_many(GST_BIN(stream->parser), parser, capsfilter, nullptr);
219         gst_element_link_pads(parser, "src", capsfilter, "sink");
220
221         GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink"));
222         gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get()));
223
224         pad = adoptGRef(gst_element_get_static_pad(capsfilter, "src"));
225         gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get()));
226     } else if (!g_strcmp0(mediaType, "audio/mpeg")) {
227         gint mpegversion = -1;
228         gst_structure_get_int(structure, "mpegversion", &mpegversion);
229
230         GstElement* parser = nullptr;
231         if (mpegversion == 1)
232             parser = gst_element_factory_make("mpegaudioparse", nullptr);
233         else if (mpegversion == 2 || mpegversion == 4)
234             parser = gst_element_factory_make("aacparse", nullptr);
235         else
236             ASSERT_NOT_REACHED();
237
238         stream->parser = gst_bin_new(parserBinName.get());
239         gst_bin_add(GST_BIN(stream->parser), parser);
240
241         GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink"));
242         gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get()));
243
244         pad = adoptGRef(gst_element_get_static_pad(parser, "src"));
245         gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get()));
246     } else if (!g_strcmp0(mediaType, "video/x-vp9"))
247         stream->parser = nullptr;
248     else {
249         GST_ERROR_OBJECT(stream->parent, "Unsupported media format: %s", mediaType);
250         return;
251     }
252
253     GST_OBJECT_LOCK(webKitMediaSrc);
254     stream->type = Unknown;
255     GST_OBJECT_UNLOCK(webKitMediaSrc);
256
257     GRefPtr<GstPad> sourcePad;
258     if (stream->parser) {
259         gst_bin_add(GST_BIN(stream->parent), stream->parser);
260         gst_element_sync_state_with_parent(stream->parser);
261
262         GRefPtr<GstPad> sinkPad = adoptGRef(gst_element_get_static_pad(stream->parser, "sink"));
263         sourcePad = adoptGRef(gst_element_get_static_pad(stream->appsrc, "src"));
264         gst_pad_link(sourcePad.get(), sinkPad.get());
265         sourcePad = adoptGRef(gst_element_get_static_pad(stream->parser, "src"));
266     } else {
267         GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Stream of type %s doesn't require a parser bin", mediaType);
268         sourcePad = adoptGRef(gst_element_get_static_pad(stream->appsrc, "src"));
269     }
270     ASSERT(sourcePad);
271
272     // FIXME: Is padId the best way to identify the Stream? What about trackId?
273     g_object_set_data(G_OBJECT(sourcePad.get()), "padId", GINT_TO_POINTER(padId));
274     webKitMediaSrcLinkParser(sourcePad.get(), caps, stream);
275
276     ASSERT(stream->parent->priv->mediaPlayerPrivate);
277     int signal = -1;
278
279     GST_OBJECT_LOCK(webKitMediaSrc);
280     if (g_str_has_prefix(mediaType, "audio")) {
281         stream->type = Audio;
282         stream->parent->priv->numberOfAudioStreams++;
283         signal = SIGNAL_AUDIO_CHANGED;
284         stream->audioTrack = RefPtr<WebCore::AudioTrackPrivateGStreamer>(static_cast<WebCore::AudioTrackPrivateGStreamer*>(trackPrivate.get()));
285     } else if (g_str_has_prefix(mediaType, "video")) {
286         stream->type = Video;
287         stream->parent->priv->numberOfVideoStreams++;
288         signal = SIGNAL_VIDEO_CHANGED;
289         stream->videoTrack = RefPtr<WebCore::VideoTrackPrivateGStreamer>(static_cast<WebCore::VideoTrackPrivateGStreamer*>(trackPrivate.get()));
290     } else if (g_str_has_prefix(mediaType, "text")) {
291         stream->type = Text;
292         stream->parent->priv->numberOfTextStreams++;
293         signal = SIGNAL_TEXT_CHANGED;
294
295         // FIXME: Support text tracks.
296     }
297     GST_OBJECT_UNLOCK(webKitMediaSrc);
298
299     if (signal != -1)
300         g_signal_emit(G_OBJECT(stream->parent), webKitMediaSrcSignals[signal], 0, nullptr);
301 }
302
303 void PlaybackPipeline::reattachTrack(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, RefPtr<TrackPrivateBase> trackPrivate)
304 {
305     GST_DEBUG("Re-attaching track");
306
307     // FIXME: Maybe remove this method. Now the caps change is managed by gst_appsrc_push_sample() in enqueueSample()
308     // and flushAndEnqueueNonDisplayingSamples().
309
310     WebKitMediaSrc* webKitMediaSrc = m_webKitMediaSrc.get();
311
312     GST_OBJECT_LOCK(webKitMediaSrc);
313     Stream* stream = getStreamBySourceBufferPrivate(webKitMediaSrc, sourceBufferPrivate.get());
314     GST_OBJECT_UNLOCK(webKitMediaSrc);
315
316     ASSERT(stream && stream->type != Invalid);
317
318     // The caps change is managed by gst_appsrc_push_sample() in enqueueSample() and
319     // flushAndEnqueueNonDisplayingSamples(), so the caps aren't set from here.
320     GRefPtr<GstCaps> appsrcCaps = adoptGRef(gst_app_src_get_caps(GST_APP_SRC(stream->appsrc)));
321     const gchar* mediaType = gst_structure_get_name(gst_caps_get_structure(appsrcCaps.get(), 0));
322     int signal = -1;
323
324     GST_OBJECT_LOCK(webKitMediaSrc);
325     if (g_str_has_prefix(mediaType, "audio")) {
326         ASSERT(stream->type == Audio);
327         signal = SIGNAL_AUDIO_CHANGED;
328         stream->audioTrack = RefPtr<WebCore::AudioTrackPrivateGStreamer>(static_cast<WebCore::AudioTrackPrivateGStreamer*>(trackPrivate.get()));
329     } else if (g_str_has_prefix(mediaType, "video")) {
330         ASSERT(stream->type == Video);
331         signal = SIGNAL_VIDEO_CHANGED;
332         stream->videoTrack = RefPtr<WebCore::VideoTrackPrivateGStreamer>(static_cast<WebCore::VideoTrackPrivateGStreamer*>(trackPrivate.get()));
333     } else if (g_str_has_prefix(mediaType, "text")) {
334         ASSERT(stream->type == Text);
335         signal = SIGNAL_TEXT_CHANGED;
336
337         // FIXME: Support text tracks.
338     }
339     GST_OBJECT_UNLOCK(webKitMediaSrc);
340
341     if (signal != -1)
342         g_signal_emit(G_OBJECT(stream->parent), webKitMediaSrcSignals[signal], 0, nullptr);
343 }
344
345 void PlaybackPipeline::notifyDurationChanged()
346 {
347     gst_element_post_message(GST_ELEMENT(m_webKitMediaSrc.get()), gst_message_new_duration_changed(GST_OBJECT(m_webKitMediaSrc.get())));
348     // WebKitMediaSrc will ask MediaPlayerPrivateGStreamerMSE for the new duration later, when somebody asks for it.
349 }
350
351 void PlaybackPipeline::markEndOfStream(MediaSourcePrivate::EndOfStreamStatus)
352 {
353     WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv;
354
355     GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Have EOS");
356
357     GST_OBJECT_LOCK(m_webKitMediaSrc.get());
358     bool allTracksConfigured = priv->allTracksConfigured;
359     if (!allTracksConfigured)
360         priv->allTracksConfigured = true;
361     GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
362
363     if (!allTracksConfigured) {
364         gst_element_no_more_pads(GST_ELEMENT(m_webKitMediaSrc.get()));
365         webKitMediaSrcDoAsyncDone(m_webKitMediaSrc.get());
366     }
367
368     Vector<GstAppSrc*> appsrcs;
369
370     GST_OBJECT_LOCK(m_webKitMediaSrc.get());
371     for (Stream* stream : priv->streams) {
372         if (stream->appsrc)
373             appsrcs.append(GST_APP_SRC(stream->appsrc));
374     }
375     GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
376
377     for (GstAppSrc* appsrc : appsrcs)
378         gst_app_src_end_of_stream(appsrc);
379 }
380
381 GstPadProbeReturn segmentFixerProbe(GstPad*, GstPadProbeInfo* info, gpointer)
382 {
383     GstEvent* event = GST_EVENT(info->data);
384
385     if (GST_EVENT_TYPE(event) != GST_EVENT_SEGMENT)
386         return GST_PAD_PROBE_OK;
387
388     GstSegment* segment = nullptr;
389     gst_event_parse_segment(event, const_cast<const GstSegment**>(&segment));
390
391     GST_TRACE("Fixed segment base time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
392         GST_TIME_ARGS(segment->base), GST_TIME_ARGS(segment->start));
393
394     segment->base = segment->start;
395     segment->flags = static_cast<GstSegmentFlags>(0);
396
397     return GST_PAD_PROBE_REMOVE;
398 }
399
400 void PlaybackPipeline::flush(AtomicString trackId)
401 {
402     ASSERT(WTF::isMainThread());
403
404     GST_DEBUG("flush: trackId=%s", trackId.string().utf8().data());
405
406     GST_OBJECT_LOCK(m_webKitMediaSrc.get());
407     Stream* stream = getStreamByTrackId(m_webKitMediaSrc.get(), trackId);
408
409     if (!stream) {
410         GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
411         return;
412     }
413
414     stream->lastEnqueuedTime = MediaTime::invalidTime();
415     GstElement* appsrc = stream->appsrc;
416     GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
417
418     if (!appsrc)
419         return;
420
421     gint64 position = GST_CLOCK_TIME_NONE;
422     GRefPtr<GstQuery> query = adoptGRef(gst_query_new_position(GST_FORMAT_TIME));
423     if (gst_element_query(pipeline(), query.get()))
424         gst_query_parse_position(query.get(), 0, &position);
425
426     GST_TRACE("Position: %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
427
428     if (static_cast<guint64>(position) == GST_CLOCK_TIME_NONE) {
429         GST_TRACE("Can't determine position, avoiding flush");
430         return;
431     }
432
433     double rate;
434     GstFormat format;
435     gint64 start = GST_CLOCK_TIME_NONE;
436     gint64 stop = GST_CLOCK_TIME_NONE;
437
438     query = adoptGRef(gst_query_new_segment(GST_FORMAT_TIME));
439     if (gst_element_query(pipeline(), query.get()))
440         gst_query_parse_segment(query.get(), &rate, &format, &start, &stop);
441
442     GST_TRACE("segment: [%" GST_TIME_FORMAT ", %" GST_TIME_FORMAT "], rate: %f",
443         GST_TIME_ARGS(start), GST_TIME_ARGS(stop), rate);
444
445     if (!gst_element_send_event(GST_ELEMENT(appsrc), gst_event_new_flush_start())) {
446         GST_WARNING("Failed to send flush-start event for trackId=%s", trackId.string().utf8().data());
447         return;
448     }
449
450     if (!gst_element_send_event(GST_ELEMENT(appsrc), gst_event_new_flush_stop(false))) {
451         GST_WARNING("Failed to send flush-stop event for trackId=%s", trackId.string().utf8().data());
452         return;
453     }
454
455     if (static_cast<guint64>(position) == GST_CLOCK_TIME_NONE || static_cast<guint64>(start) == GST_CLOCK_TIME_NONE)
456         return;
457
458     GUniquePtr<GstSegment> segment(gst_segment_new());
459     gst_segment_init(segment.get(), GST_FORMAT_TIME);
460     gst_segment_do_seek(segment.get(), rate, GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
461         GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_SET, stop, nullptr);
462
463     GRefPtr<GstPad> sinkPad = gst_element_get_static_pad(appsrc, "src");
464     GRefPtr<GstPad> srcPad = sinkPad ? gst_pad_get_peer(sinkPad.get()) : nullptr;
465     if (srcPad)
466         gst_pad_add_probe(srcPad.get(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, segmentFixerProbe, nullptr, nullptr);
467
468     GST_TRACE("Sending new seamless segment: [%" GST_TIME_FORMAT ", %" GST_TIME_FORMAT "], rate: %f",
469         GST_TIME_ARGS(segment->start), GST_TIME_ARGS(segment->stop), segment->rate);
470
471     if (!gst_base_src_new_seamless_segment(GST_BASE_SRC(appsrc), segment->start, segment->stop, segment->start)) {
472         GST_WARNING("Failed to send seamless segment event for trackId=%s", trackId.string().utf8().data());
473         return;
474     }
475
476     GST_DEBUG("trackId=%s flushed", trackId.string().utf8().data());
477 }
478
479 void PlaybackPipeline::enqueueSample(Ref<MediaSample>&& mediaSample)
480 {
481     ASSERT(WTF::isMainThread());
482
483     AtomicString trackId = mediaSample->trackID();
484
485     GST_TRACE("enqueing sample trackId=%s PTS=%f presentationSize=%.0fx%.0f at %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT,
486         trackId.string().utf8().data(), mediaSample->presentationTime().toFloat(),
487         mediaSample->presentationSize().width(), mediaSample->presentationSize().height(),
488         GST_TIME_ARGS(WebCore::toGstClockTime(mediaSample->presentationTime().toDouble())),
489         GST_TIME_ARGS(WebCore::toGstClockTime(mediaSample->duration().toDouble())));
490
491     Stream* stream = getStreamByTrackId(m_webKitMediaSrc.get(), trackId);
492
493     if (!stream) {
494         GST_WARNING("No stream!");
495         return;
496     }
497
498     if (!stream->sourceBuffer->isReadyForMoreSamples(trackId)) {
499         GST_DEBUG("enqueueSample: skip adding new sample for trackId=%s, SB is not ready yet", trackId.string().utf8().data());
500         return;
501     }
502
503     GstElement* appsrc = stream->appsrc;
504     MediaTime lastEnqueuedTime = stream->lastEnqueuedTime;
505
506     GStreamerMediaSample* sample = static_cast<GStreamerMediaSample*>(mediaSample.ptr());
507     if (sample->sample() && gst_sample_get_buffer(sample->sample())) {
508         GRefPtr<GstSample> gstSample = sample->sample();
509         GstBuffer* buffer = gst_sample_get_buffer(gstSample.get());
510         lastEnqueuedTime = sample->presentationTime();
511
512         GST_BUFFER_FLAG_UNSET(buffer, GST_BUFFER_FLAG_DECODE_ONLY);
513         pushSample(GST_APP_SRC(appsrc), gstSample.get());
514         // gst_app_src_push_sample() uses transfer-none for gstSample.
515
516         stream->lastEnqueuedTime = lastEnqueuedTime;
517     }
518 }
519
520 GstElement* PlaybackPipeline::pipeline()
521 {
522     if (!m_webKitMediaSrc || !GST_ELEMENT_PARENT(GST_ELEMENT(m_webKitMediaSrc.get())))
523         return nullptr;
524
525     return GST_ELEMENT_PARENT(GST_ELEMENT_PARENT(GST_ELEMENT(m_webKitMediaSrc.get())));
526 }
527
528 } // namespace WebCore.
529
530 #endif // USE(GSTREAMER)