Unreviewed, rolling out r234489.
[WebKit-https.git] / Source / WebCore / platform / mediastream / gstreamer / GStreamerMediaStreamSource.cpp
1 /*
2  * Copyright (C) 2018 Metrological Group B.V.
3  * Author: Thibault Saunier <tsaunier@igalia.com>
4  * Author: Alejandro G. Castro <alex@igalia.com>
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
24 #if ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC) && USE(GSTREAMER)
25 #include "GStreamerMediaStreamSource.h"
26
27 #include "AudioTrackPrivate.h"
28 #include "GStreamerAudioData.h"
29 #include "GStreamerCommon.h"
30 #include "GStreamerVideoCaptureSource.h"
31 #include "MediaSampleGStreamer.h"
32 #include "VideoTrackPrivate.h"
33
34 #include <gst/app/gstappsrc.h>
35 #include <gst/base/gstflowcombiner.h>
36
37 #if GST_CHECK_VERSION(1, 10, 0)
38
39 namespace WebCore {
40
41 static void webkitMediaStreamSrcPushVideoSample(WebKitMediaStreamSrc* self, GstSample* gstsample);
42 static void webkitMediaStreamSrcPushAudioSample(WebKitMediaStreamSrc* self, GstSample* gstsample);
43 static void webkitMediaStreamSrcTrackEnded(WebKitMediaStreamSrc* self, MediaStreamTrackPrivate&);
44
45 static GstStaticPadTemplate videoSrcTemplate = GST_STATIC_PAD_TEMPLATE("video_src",
46     GST_PAD_SRC,
47     GST_PAD_SOMETIMES,
48     GST_STATIC_CAPS("video/x-raw;video/x-h264;video/x-vp8"));
49
50 static GstStaticPadTemplate audioSrcTemplate = GST_STATIC_PAD_TEMPLATE("audio_src",
51     GST_PAD_SRC,
52     GST_PAD_SOMETIMES,
53     GST_STATIC_CAPS("audio/x-raw(ANY);"));
54
55 static GstTagList* mediaStreamTrackPrivateGetTags(MediaStreamTrackPrivate* track)
56 {
57     auto taglist = gst_tag_list_new_empty();
58
59     if (!track->label().isEmpty()) {
60         gst_tag_list_add(taglist, GST_TAG_MERGE_APPEND,
61             GST_TAG_TITLE, track->label().utf8().data(), nullptr);
62     }
63
64     if (track->type() == RealtimeMediaSource::Type::Audio) {
65         gst_tag_list_add(taglist, GST_TAG_MERGE_APPEND, WEBKIT_MEDIA_TRACK_TAG_KIND,
66             static_cast<int>(AudioTrackPrivate::Kind::Main), nullptr);
67     } else if (track->type() == RealtimeMediaSource::Type::Video) {
68         gst_tag_list_add(taglist, GST_TAG_MERGE_APPEND, WEBKIT_MEDIA_TRACK_TAG_KIND,
69             static_cast<int>(VideoTrackPrivate::Kind::Main), nullptr);
70
71         if (track->isCaptureTrack()) {
72             GStreamerVideoCaptureSource& source = static_cast<GStreamerVideoCaptureSource&>(
73                 track->source());
74
75             gst_tag_list_add(taglist, GST_TAG_MERGE_APPEND,
76                 WEBKIT_MEDIA_TRACK_TAG_WIDTH, source.size().width(),
77                 WEBKIT_MEDIA_TRACK_TAG_HEIGHT, source.size().height(), nullptr);
78         }
79     }
80
81     return taglist;
82 }
83
84 GstStream* webkitMediaStreamNew(MediaStreamTrackPrivate* track)
85 {
86     GRefPtr<GstCaps> caps;
87     GstStreamType type;
88
89     if (track->type() == RealtimeMediaSource::Type::Audio) {
90         caps = adoptGRef(gst_static_pad_template_get_caps(&audioSrcTemplate));
91         type = GST_STREAM_TYPE_AUDIO;
92     } else if (track->type() == RealtimeMediaSource::Type::Video) {
93         caps = adoptGRef(gst_static_pad_template_get_caps(&videoSrcTemplate));
94         type = GST_STREAM_TYPE_VIDEO;
95     } else {
96         GST_FIXME("Handle %d type", (gint) track->type());
97
98         return nullptr;
99     }
100
101     auto gststream = (GstStream*)gst_stream_new(track->id().utf8().data(),
102         caps.get(), type, GST_STREAM_FLAG_SELECT);
103     auto tags = adoptGRef(mediaStreamTrackPrivateGetTags(track));
104     gst_stream_set_tags(gststream, tags.get());
105
106     return gststream;
107 }
108
109 class WebKitMediaStreamTrackObserver
110     : public MediaStreamTrackPrivate::Observer {
111 public:
112     virtual ~WebKitMediaStreamTrackObserver() { };
113     WebKitMediaStreamTrackObserver(WebKitMediaStreamSrc* src)
114         : m_mediaStreamSrc(src) { }
115     void trackStarted(MediaStreamTrackPrivate&) final { };
116
117     void trackEnded(MediaStreamTrackPrivate& track) final
118     {
119         webkitMediaStreamSrcTrackEnded(m_mediaStreamSrc, track);
120     }
121
122     void trackMutedChanged(MediaStreamTrackPrivate&) final { };
123     void trackSettingsChanged(MediaStreamTrackPrivate&) final { };
124     void trackEnabledChanged(MediaStreamTrackPrivate&) final { };
125     void readyStateChanged(MediaStreamTrackPrivate&) final { };
126
127     void sampleBufferUpdated(MediaStreamTrackPrivate&, MediaSample& sample) final
128     {
129         auto gstsample = static_cast<MediaSampleGStreamer*>(&sample)->platformSample().sample.gstSample;
130
131         webkitMediaStreamSrcPushVideoSample(m_mediaStreamSrc, gstsample);
132     }
133
134     void audioSamplesAvailable(MediaStreamTrackPrivate&, const MediaTime&, const PlatformAudioData& audioData, const AudioStreamDescription&, size_t) final
135     {
136         auto audiodata = static_cast<const GStreamerAudioData&>(audioData);
137
138         webkitMediaStreamSrcPushAudioSample(m_mediaStreamSrc, audiodata.getSample());
139     }
140
141 private:
142     WebKitMediaStreamSrc* m_mediaStreamSrc;
143 };
144
145 typedef struct _WebKitMediaStreamSrcClass WebKitMediaStreamSrcClass;
146 struct _WebKitMediaStreamSrc {
147     GstBin parent_instance;
148
149     gchar* uri;
150
151     GstElement* audioSrc;
152     GstClockTime firstAudioBufferPts;
153     GstElement* videoSrc;
154     GstClockTime firstFramePts;
155
156     std::unique_ptr<WebKitMediaStreamTrackObserver> observer;
157     String videoTrackID;
158     volatile gint npads;
159     gulong probeid;
160     RefPtr<MediaStreamPrivate> stream;
161
162     GstFlowCombiner* flowCombiner;
163     GRefPtr<GstStreamCollection> streamCollection;
164 };
165
166 struct _WebKitMediaStreamSrcClass {
167     GstBinClass parent_class;
168 };
169
170 enum {
171     PROP_0,
172     PROP_IS_LIVE,
173     PROP_LAST
174 };
175
176 static GstURIType webkit_media_stream_src_uri_get_type(GType)
177 {
178     return GST_URI_SRC;
179 }
180
181 static const gchar* const* webkit_media_stream_src_uri_get_protocols(GType)
182 {
183     static const gchar* protocols[] = { "mediastream", nullptr };
184
185     return protocols;
186 }
187
188 static gchar* webkit_media_stream_src_uri_get_uri(GstURIHandler* handler)
189 {
190     WebKitMediaStreamSrc* self = WEBKIT_MEDIA_STREAM_SRC(handler);
191
192     /* FIXME: make thread-safe */
193     return g_strdup(self->uri);
194 }
195
196 static gboolean webkitMediaStreamSrcUriSetUri(GstURIHandler* handler, const gchar* uri,
197     GError**)
198 {
199     WebKitMediaStreamSrc* self = WEBKIT_MEDIA_STREAM_SRC(handler);
200     self->uri = g_strdup(uri);
201
202     return TRUE;
203 }
204
205 static void webkitMediaStreamSrcUriHandlerInit(gpointer g_iface, gpointer)
206 {
207     GstURIHandlerInterface* iface = (GstURIHandlerInterface*)g_iface;
208
209     iface->get_type = webkit_media_stream_src_uri_get_type;
210     iface->get_protocols = webkit_media_stream_src_uri_get_protocols;
211     iface->get_uri = webkit_media_stream_src_uri_get_uri;
212     iface->set_uri = webkitMediaStreamSrcUriSetUri;
213 }
214
215 GST_DEBUG_CATEGORY_STATIC(webkitMediaStreamSrcDebug);
216 #define GST_CAT_DEFAULT webkitMediaStreamSrcDebug
217
218 #define doInit                                                                                                                                                              \
219     G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webkitMediaStreamSrcUriHandlerInit);                                                                                    \
220     GST_DEBUG_CATEGORY_INIT(webkitMediaStreamSrcDebug, "webkitwebmediastreamsrc", 0, "mediastreamsrc element");                                                           \
221     gst_tag_register_static(WEBKIT_MEDIA_TRACK_TAG_WIDTH, GST_TAG_FLAG_META, G_TYPE_INT, "Webkit MediaStream width", "Webkit MediaStream width", gst_tag_merge_use_first);    \
222     gst_tag_register_static(WEBKIT_MEDIA_TRACK_TAG_HEIGHT, GST_TAG_FLAG_META, G_TYPE_INT, "Webkit MediaStream height", "Webkit MediaStream height", gst_tag_merge_use_first); \
223     gst_tag_register_static(WEBKIT_MEDIA_TRACK_TAG_KIND, GST_TAG_FLAG_META, G_TYPE_INT, "Webkit MediaStream Kind", "Webkit MediaStream Kind", gst_tag_merge_use_first);
224
225 G_DEFINE_TYPE_WITH_CODE(WebKitMediaStreamSrc, webkit_media_stream_src, GST_TYPE_BIN, doInit);
226
227 static void webkitMediaStreamSrcSetProperty(GObject* object, guint prop_id,
228     const GValue*, GParamSpec* pspec)
229 {
230     switch (prop_id) {
231     default:
232         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
233         break;
234     }
235 }
236
237 static void webkitMediaStreamSrcGetProperty(GObject* object, guint prop_id, GValue* value,
238     GParamSpec* pspec)
239 {
240     switch (prop_id) {
241     case PROP_IS_LIVE:
242         g_value_set_boolean(value, TRUE);
243         break;
244     default:
245         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
246         break;
247     }
248 }
249
250 static void webkitMediaStreamSrcDispose(GObject* object)
251 {
252     WebKitMediaStreamSrc* self = WEBKIT_MEDIA_STREAM_SRC(object);
253
254     if (self->audioSrc) {
255         gst_bin_remove(GST_BIN(self), self->audioSrc);
256         self->audioSrc = nullptr;
257     }
258
259     if (self->videoSrc) {
260         gst_bin_remove(GST_BIN(self), self->videoSrc);
261         self->videoSrc = nullptr;
262     }
263 }
264
265 static void webkitMediaStreamSrcFinalize(GObject* object)
266 {
267     WebKitMediaStreamSrc* self = WEBKIT_MEDIA_STREAM_SRC(object);
268
269     GST_OBJECT_LOCK(self);
270     for (auto& track : self->stream->tracks())
271         track->removeObserver(*self->observer.get());
272     GST_OBJECT_UNLOCK(self);
273
274     g_clear_pointer(&self->uri, g_free);
275     gst_flow_combiner_free(self->flowCombiner);
276 }
277
278 static GstStateChangeReturn webkitMediaStreamSrcChangeState(GstElement* element, GstStateChange transition)
279 {
280     GstStateChangeReturn result;
281     auto* self = WEBKIT_MEDIA_STREAM_SRC(element);
282
283     if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) {
284
285         GST_OBJECT_LOCK(self);
286         for (auto& track : self->stream->tracks())
287             track->removeObserver(*self->observer.get());
288         GST_OBJECT_UNLOCK(self);
289     }
290
291     result = GST_ELEMENT_CLASS(webkit_media_stream_src_parent_class)->change_state(element, transition);
292
293     if (transition == GST_STATE_CHANGE_READY_TO_PAUSED)
294         result = GST_STATE_CHANGE_NO_PREROLL;
295
296     return result;
297 }
298
299 static void webkit_media_stream_src_class_init(WebKitMediaStreamSrcClass* klass)
300 {
301     GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
302     GstElementClass* gstelement_klass = GST_ELEMENT_CLASS(klass);
303
304     gobject_class->finalize = webkitMediaStreamSrcFinalize;
305     gobject_class->dispose = webkitMediaStreamSrcDispose;
306     gobject_class->get_property = webkitMediaStreamSrcGetProperty;
307     gobject_class->set_property = webkitMediaStreamSrcSetProperty;
308
309     g_object_class_install_property(gobject_class, PROP_IS_LIVE,
310         g_param_spec_boolean("is-live", "Is Live",
311             "Let playbin3 know we are a live source.",
312             TRUE, (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
313
314     gstelement_klass->change_state = webkitMediaStreamSrcChangeState;
315     gst_element_class_add_pad_template(gstelement_klass,
316         gst_static_pad_template_get(&videoSrcTemplate));
317     gst_element_class_add_pad_template(gstelement_klass,
318         gst_static_pad_template_get(&audioSrcTemplate));
319 }
320
321 static void webkit_media_stream_src_init(WebKitMediaStreamSrc* self)
322 {
323     self->observer = std::make_unique<WebKitMediaStreamTrackObserver>(self);
324     self->flowCombiner = gst_flow_combiner_new();
325     self->firstAudioBufferPts = GST_CLOCK_TIME_NONE;
326     self->firstFramePts = GST_CLOCK_TIME_NONE;
327 }
328
329 typedef struct {
330     WebKitMediaStreamSrc* self;
331     RefPtr<MediaStreamTrackPrivate> track;
332     GstStaticPadTemplate* pad_template;
333 } ProbeData;
334
335 static GstFlowReturn webkitMediaStreamSrcChain(GstPad* pad, GstObject* parent, GstBuffer* buffer)
336 {
337     GstFlowReturn result;
338     GRefPtr<WebKitMediaStreamSrc> self = adoptGRef(WEBKIT_MEDIA_STREAM_SRC(gst_object_get_parent(parent)));
339
340     result = gst_flow_combiner_update_pad_flow(self.get()->flowCombiner, pad,
341         gst_proxy_pad_chain_default(pad, GST_OBJECT(self.get()), buffer));
342
343     return result;
344 }
345
346 static void webkitMediaStreamSrcAddPad(WebKitMediaStreamSrc* self, GstPad* target, GstStaticPadTemplate* pad_template)
347 {
348     auto padname = String::format("src_%u", g_atomic_int_add(&(self->npads), 1));
349     auto ghostpad = gst_ghost_pad_new_from_template(padname.utf8().data(), target,
350         gst_static_pad_template_get(pad_template));
351
352     GST_DEBUG_OBJECT(self, "%s Ghosting %" GST_PTR_FORMAT,
353         gst_object_get_path_string(GST_OBJECT_CAST(self)),
354         target);
355
356     auto proxypad = adoptGRef(GST_PAD(gst_proxy_pad_get_internal(GST_PROXY_PAD(ghostpad))));
357     gst_pad_set_active(ghostpad, TRUE);
358     if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(ghostpad))) {
359         GST_ERROR_OBJECT(self, "Could not add pad %s:%s", GST_DEBUG_PAD_NAME(ghostpad));
360         ASSERT_NOT_REACHED();
361
362         return;
363     }
364
365     gst_flow_combiner_add_pad(self->flowCombiner, proxypad.get());
366     gst_pad_set_chain_function(proxypad.get(),
367         static_cast<GstPadChainFunction>(webkitMediaStreamSrcChain));
368 }
369
370 static GstPadProbeReturn webkitMediaStreamSrcPadProbeCb(GstPad* pad, GstPadProbeInfo* info, ProbeData* data)
371 {
372     GstEvent* event = GST_PAD_PROBE_INFO_EVENT(info);
373     WebKitMediaStreamSrc* self = data->self;
374
375     switch (GST_EVENT_TYPE(event)) {
376     case GST_EVENT_STREAM_START: {
377         const gchar* stream_id;
378         GRefPtr<GstStream> stream = nullptr;
379
380         gst_event_parse_stream_start(event, &stream_id);
381         if (!g_strcmp0(stream_id, data->track->id().utf8().data())) {
382             GST_INFO_OBJECT(pad, "Event has been sticked already");
383             return GST_PAD_PROBE_OK;
384         }
385
386         auto stream_start = gst_event_new_stream_start(data->track->id().utf8().data());
387         gst_event_set_group_id(stream_start, 1);
388         gst_event_unref(event);
389
390         gst_pad_push_event(pad, stream_start);
391         gst_pad_push_event(pad, gst_event_new_tag(mediaStreamTrackPrivateGetTags(data->track.get())));
392
393         webkitMediaStreamSrcAddPad(self, pad, data->pad_template);
394
395         return GST_PAD_PROBE_HANDLED;
396     }
397     default:
398         break;
399     }
400
401     return GST_PAD_PROBE_OK;
402 }
403
404 static gboolean webkitMediaStreamSrcSetupSrc(WebKitMediaStreamSrc* self,
405     MediaStreamTrackPrivate* track, GstElement* element,
406     GstStaticPadTemplate* pad_template, gboolean observe_track)
407 {
408     auto pad = adoptGRef(gst_element_get_static_pad(element, "src"));
409
410     gst_bin_add(GST_BIN(self), element);
411
412     ProbeData* data = new ProbeData;
413     data->self = WEBKIT_MEDIA_STREAM_SRC(self);
414     data->pad_template = pad_template;
415     data->track = track;
416
417     self->probeid = gst_pad_add_probe(pad.get(), (GstPadProbeType)GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
418         (GstPadProbeCallback)webkitMediaStreamSrcPadProbeCb, data,
419         [](gpointer data) {
420             delete (ProbeData*)data;
421         });
422
423     if (observe_track)
424         track->addObserver(*self->observer.get());
425
426     gst_element_sync_state_with_parent(element);
427     return TRUE;
428 }
429
430 static gboolean webkitMediaStreamSrcSetupAppSrc(WebKitMediaStreamSrc* self,
431     MediaStreamTrackPrivate* track, GstElement** element,
432     GstStaticPadTemplate* pad_template)
433 {
434     *element = gst_element_factory_make("appsrc", nullptr);
435     g_object_set(*element, "is-live", true, "format", GST_FORMAT_TIME, nullptr);
436
437     return webkitMediaStreamSrcSetupSrc(self, track, *element, pad_template, TRUE);
438 }
439
440 static void webkitMediaStreamSrcPostStreamCollection(WebKitMediaStreamSrc* self, MediaStreamPrivate* stream)
441 {
442     GST_OBJECT_LOCK(self);
443     self->streamCollection = adoptGRef(gst_stream_collection_new(stream->id().utf8().data()));
444     for (auto& track : stream->tracks()) {
445         auto gststream = webkitMediaStreamNew(track.get());
446
447         gst_stream_collection_add_stream(self->streamCollection.get(), gststream);
448     }
449     GST_OBJECT_UNLOCK(self);
450
451     gst_element_post_message(GST_ELEMENT(self),
452         gst_message_new_stream_collection(GST_OBJECT(self), self->streamCollection.get()));
453 }
454
455 gboolean webkitMediaStreamSrcSetStream(WebKitMediaStreamSrc* self, MediaStreamPrivate* stream)
456 {
457     g_return_val_if_fail(WEBKIT_IS_MEDIA_STREAM_SRC(self), FALSE);
458
459     if (self->audioSrc) {
460         gst_element_set_state(self->audioSrc, GST_STATE_NULL);
461         gst_bin_remove(GST_BIN(self), self->audioSrc);
462         self->audioSrc = nullptr;
463     }
464
465     if (self->videoSrc) {
466         gst_element_set_state(self->videoSrc, GST_STATE_NULL);
467         gst_bin_remove(GST_BIN(self), self->videoSrc);
468         self->videoSrc = nullptr;
469     }
470
471     webkitMediaStreamSrcPostStreamCollection(self, stream);
472
473     self->stream = stream;
474     for (auto& track : stream->tracks()) {
475         if (track->type() == RealtimeMediaSource::Type::Audio) {
476             webkitMediaStreamSrcSetupAppSrc(self, track.get(), &self->audioSrc,
477                 &audioSrcTemplate);
478         } else if (track->type() == RealtimeMediaSource::Type::Video) {
479             webkitMediaStreamSrcSetupAppSrc(self, track.get(), &self->videoSrc,
480                 &videoSrcTemplate);
481         } else {
482             GST_INFO("Unsuported track type: %d", (gint) track->type());
483             continue;
484         }
485     }
486
487     return TRUE;
488 }
489
490 static void webkitMediaStreamSrcPushVideoSample(WebKitMediaStreamSrc* self, GstSample* gstsample)
491 {
492     if (self->videoSrc) {
493         if (!GST_CLOCK_TIME_IS_VALID(self->firstFramePts)) {
494             auto buffer = gst_sample_get_buffer(gstsample);
495
496             self->firstFramePts = GST_BUFFER_PTS(buffer);
497             auto pad = adoptGRef(gst_element_get_static_pad(self->videoSrc, "src"));
498             gst_pad_set_offset(pad.get(), -self->firstFramePts);
499         }
500
501         gst_app_src_push_sample(GST_APP_SRC(self->videoSrc), gstsample);
502     }
503 }
504
505 static void webkitMediaStreamSrcPushAudioSample(WebKitMediaStreamSrc* self, GstSample* gstsample)
506 {
507     if (self->audioSrc) {
508         if (!GST_CLOCK_TIME_IS_VALID(self->firstAudioBufferPts)) {
509             auto buffer = gst_sample_get_buffer(gstsample);
510
511             self->firstAudioBufferPts = GST_BUFFER_PTS(buffer);
512             auto pad = adoptGRef(gst_element_get_static_pad(self->audioSrc, "src"));
513             gst_pad_set_offset(pad.get(), -self->firstAudioBufferPts);
514         }
515         gst_app_src_push_sample(GST_APP_SRC(self->audioSrc), gstsample);
516     }
517 }
518
519 static void webkitMediaStreamSrcTrackEnded(WebKitMediaStreamSrc* self,
520     MediaStreamTrackPrivate& track)
521 {
522     GRefPtr<GstPad> pad = nullptr;
523
524     GST_OBJECT_LOCK(self);
525     for (auto tmp = GST_ELEMENT(self)->srcpads; tmp; tmp = tmp->next) {
526         GstPad* tmppad = GST_PAD(tmp->data);
527         const gchar* stream_id;
528
529         GstEvent* stream_start = gst_pad_get_sticky_event(tmppad, GST_EVENT_STREAM_START, 0);
530         if (!stream_start)
531             continue;
532
533         gst_event_parse_stream_start(stream_start, &stream_id);
534         if (String(stream_id) == track.id()) {
535             pad = tmppad;
536             break;
537         }
538     }
539     GST_OBJECT_UNLOCK(self);
540
541     if (!pad) {
542         GST_ERROR_OBJECT(self, "No pad found for %s", track.id().utf8().data());
543
544         return;
545     }
546
547     // Make sure that the video.videoWidth is reset to 0
548     webkitMediaStreamSrcPostStreamCollection(self, self->stream.get());
549     auto tags = mediaStreamTrackPrivateGetTags(&track);
550     gst_pad_push_event(pad.get(), gst_event_new_tag(tags));
551     gst_pad_push_event(pad.get(), gst_event_new_eos());
552 }
553
554 } // WebCore
555 #endif // GST_CHECK_VERSION(1, 10, 0)
556 #endif // ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC)