2933eea450c365fa3afca488e2c3b512dbeb8f38
[WebKit-https.git] / Source / WebCore / platform / mediastream / gstreamer / GStreamerCapturer.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 "GStreamerCapturer.h"
26
27 #include <gst/app/gstappsink.h>
28 #include <gst/app/gstappsrc.h>
29 #include <mutex>
30 #include <webrtc/api/mediastreaminterface.h>
31
32 #if GST_CHECK_VERSION(1, 10, 0)
33
34 GST_DEBUG_CATEGORY(webkit_capturer_debug);
35 #define GST_CAT_DEFAULT webkit_capturer_debug
36
37 namespace WebCore {
38
39 static void initializeGStreamerAndDebug()
40 {
41     initializeGStreamer();
42
43     static std::once_flag debugRegisteredFlag;
44     std::call_once(debugRegisteredFlag, [] {
45         GST_DEBUG_CATEGORY_INIT(webkit_capturer_debug, "webkitcapturer", 0, "WebKit Capturer");
46     });
47 }
48
49 GStreamerCapturer::GStreamerCapturer(GStreamerCaptureDevice device, GRefPtr<GstCaps> caps)
50     : m_device(device.device())
51     , m_caps(caps)
52     , m_sourceFactory(nullptr)
53 {
54     initializeGStreamerAndDebug();
55 }
56
57 GStreamerCapturer::GStreamerCapturer(const char* sourceFactory, GRefPtr<GstCaps> caps)
58     : m_device(nullptr)
59     , m_caps(caps)
60     , m_sourceFactory(sourceFactory)
61 {
62     initializeGStreamerAndDebug();
63 }
64
65 GStreamerCapturer::~GStreamerCapturer()
66 {
67     if (m_pipeline)
68         disconnectSimpleBusMessageCallback(pipeline());
69 }
70
71 GstElement* GStreamerCapturer::createSource()
72 {
73     if (m_sourceFactory) {
74         m_src = makeElement(m_sourceFactory);
75         if (GST_IS_APP_SRC(m_src.get()))
76             g_object_set(m_src.get(), "is-live", true, "format", GST_FORMAT_TIME, nullptr);
77
78         ASSERT(m_src);
79         return m_src.get();
80     }
81
82     ASSERT(m_device);
83     GUniquePtr<char> sourceName(g_strdup_printf("%s_%p", name(), this));
84     m_src = gst_device_create_element(m_device.get(), sourceName.get());
85     ASSERT(m_src);
86
87     return m_src.get();
88 }
89
90 GstCaps* GStreamerCapturer::caps()
91 {
92     if (m_sourceFactory) {
93         GRefPtr<GstElement> element = makeElement(m_sourceFactory);
94         auto pad = adoptGRef(gst_element_get_static_pad(element.get(), "src"));
95
96         return gst_pad_query_caps(pad.get(), nullptr);
97     }
98
99     ASSERT(m_device);
100     return gst_device_get_caps(m_device.get());
101 }
102
103 void GStreamerCapturer::setupPipeline()
104 {
105     if (m_pipeline)
106         disconnectSimpleBusMessageCallback(pipeline());
107
108     m_pipeline = makeElement("pipeline");
109
110     GRefPtr<GstElement> source = createSource();
111     GRefPtr<GstElement> converter = createConverter();
112
113     m_capsfilter = makeElement("capsfilter");
114     m_tee = makeElement("tee");
115     m_sink = makeElement("appsink");
116
117     gst_app_sink_set_emit_signals(GST_APP_SINK(m_sink.get()), TRUE);
118     g_object_set(m_capsfilter.get(), "caps", m_caps.get(), nullptr);
119
120     gst_bin_add_many(GST_BIN(m_pipeline.get()), source.get(), converter.get(), m_capsfilter.get(), m_tee.get(), nullptr);
121     gst_element_link_many(source.get(), converter.get(), m_capsfilter.get(), m_tee.get(), nullptr);
122
123     addSink(m_sink.get());
124
125     connectSimpleBusMessageCallback(pipeline());
126 }
127
128 GstElement* GStreamerCapturer::makeElement(const char* factoryName)
129 {
130     auto element = gst_element_factory_make(factoryName, nullptr);
131     ASSERT(element);
132     GUniquePtr<char> capturerName(g_strdup_printf("%s_capturer_%s_%p", name(), GST_OBJECT_NAME(element), this));
133     gst_object_set_name(GST_OBJECT(element), capturerName.get());
134
135     return element;
136 }
137
138 void GStreamerCapturer::addSink(GstElement* newSink)
139 {
140     ASSERT(m_pipeline);
141     ASSERT(m_tee);
142
143     auto queue = makeElement("queue");
144     gst_bin_add_many(GST_BIN(pipeline()), queue, newSink, nullptr);
145     gst_element_sync_state_with_parent(queue);
146     gst_element_sync_state_with_parent(newSink);
147
148     if (!gst_element_link_pads(m_tee.get(), "src_%u", queue, "sink")) {
149         ASSERT_NOT_REACHED();
150         return;
151     }
152
153     if (!gst_element_link(queue, newSink)) {
154         ASSERT_NOT_REACHED();
155         return;
156     }
157
158     GST_INFO_OBJECT(pipeline(), "Adding sink: %" GST_PTR_FORMAT, newSink);
159
160     GUniquePtr<char> dumpName(g_strdup_printf("%s_sink_%s_added", GST_OBJECT_NAME(pipeline()), GST_OBJECT_NAME(newSink)));
161     GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline()), GST_DEBUG_GRAPH_SHOW_ALL, dumpName.get());
162 }
163
164 void GStreamerCapturer::play()
165 {
166     ASSERT(m_pipeline);
167
168     GST_INFO_OBJECT(pipeline(), "Going to PLAYING!");
169
170     gst_element_set_state(pipeline(), GST_STATE_PLAYING);
171 }
172
173 void GStreamerCapturer::stop()
174 {
175     ASSERT(m_pipeline);
176
177     GST_INFO_OBJECT(pipeline(), "Tearing down!");
178
179     // Make sure to remove sync handler before tearing down, avoiding
180     // possible deadlocks.
181     GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(pipeline())));
182     gst_bus_set_sync_handler(bus.get(), nullptr, nullptr, nullptr);
183
184     gst_element_set_state(pipeline(), GST_STATE_NULL);
185 }
186
187 } // namespace WebCore
188
189 #endif // GST_CHECK_VERSION(1, 10, 0)
190 #endif // ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC) && USE(GSTREAMER)