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
34GST_DEBUG_CATEGORY(webkit_capturer_debug);
35#define GST_CAT_DEFAULT webkit_capturer_debug
36
37namespace WebCore {
38
39static 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
49GStreamerCapturer::GStreamerCapturer(GStreamerCaptureDevice device, GRefPtr<GstCaps> caps)
50 : m_device(device.device())
51 , m_caps(caps)
52 , m_sourceFactory(nullptr)
53{
54 initializeGStreamerAndDebug();
55}
56
57GStreamerCapturer::GStreamerCapturer(const char* sourceFactory, GRefPtr<GstCaps> caps)
58 : m_device(nullptr)
59 , m_caps(caps)
60 , m_sourceFactory(sourceFactory)
61{
62 initializeGStreamerAndDebug();
63}
64
65GStreamerCapturer::~GStreamerCapturer()
66{
67 if (m_pipeline)
68 disconnectSimpleBusMessageCallback(pipeline());
69}
70
71GstElement* 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
90GstCaps* 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
103void 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
128GstElement* 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
138void 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
164void 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
173void 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)
191