|
44 | 44 | #include <wtf/Condition.h> |
45 | 45 | #include <wtf/glib/RunLoopSourcePriority.h> |
46 | 46 | #include <wtf/text/StringConcatenateNumbers.h> |
| 47 | +#include <wtf/text/ASCIILiteral.h> |
47 | 48 |
|
48 | 49 | GST_DEBUG_CATEGORY_EXTERN(webkit_mse_debug); |
49 | 50 | #define GST_CAT_DEFAULT webkit_mse_debug |
@@ -98,6 +99,40 @@ static void assertedElementSetState(GstElement* element, GstState desiredState) |
98 | 99 | } |
99 | 100 | } |
100 | 101 |
|
| 102 | +void AppendPipeline::configureOptionalDemuxerFromAnyThread() |
| 103 | +{ |
| 104 | + ASSERT(m_demux); |
| 105 | + |
| 106 | + auto elementClass = makeString(gst_element_get_metadata(m_demux.get(), GST_ELEMENT_METADATA_KLASS)); |
| 107 | + // We try to detect special cases of demuxers that have a single static src pad, such as id3demux. |
| 108 | + GRefPtr<GstPad> demuxerSrcPad = adoptGRef(gst_element_get_static_pad(m_demux.get(), "src")); |
| 109 | + if (!demuxerSrcPad && elementClass.split('/').contains("Demuxer"_s)) { |
| 110 | + // These signals won't outlive the lifetime of `this`. |
| 111 | + g_signal_connect_swapped(m_demux.get(), "no-more-pads", G_CALLBACK(+[](AppendPipeline* appendPipeline) { |
| 112 | + ASSERT(!isMainThread()); |
| 113 | + GST_DEBUG_OBJECT(appendPipeline->pipeline(), "Posting no-more-pads task to main thread"); |
| 114 | + appendPipeline->m_taskQueue.enqueueTaskAndWait<AbortableTaskQueue::Void>([appendPipeline]() { |
| 115 | + appendPipeline->didReceiveInitializationSegment(); |
| 116 | + return AbortableTaskQueue::Void(); |
| 117 | + }); |
| 118 | + }), this); |
| 119 | + } else { |
| 120 | + // m_demux can be an identity or an id3demux element at this point. |
| 121 | + gst_pad_add_probe(demuxerSrcPad.get(), GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>( |
| 122 | + +[](GstPad *pad, GstPadProbeInfo*, AppendPipeline* appendPipeline) { |
| 123 | + GRefPtr<GstCaps> caps = adoptGRef(gst_pad_get_current_caps(pad)); |
| 124 | + if (!caps) |
| 125 | + return GST_PAD_PROBE_DROP; |
| 126 | + appendPipeline->m_taskQueue.enqueueTaskAndWait<AbortableTaskQueue::Void>([appendPipeline]() { |
| 127 | + appendPipeline->didReceiveInitializationSegment(); |
| 128 | + return AbortableTaskQueue::Void(); |
| 129 | + }); |
| 130 | + return GST_PAD_PROBE_REMOVE; |
| 131 | + } |
| 132 | + ), this, nullptr); |
| 133 | + } |
| 134 | +} |
| 135 | + |
101 | 136 | AppendPipeline::AppendPipeline(SourceBufferPrivateGStreamer& sourceBufferPrivate, MediaPlayerPrivateGStreamerMSE& playerPrivate) |
102 | 137 | : m_sourceBufferPrivate(sourceBufferPrivate) |
103 | 138 | , m_playerPrivate(&playerPrivate) |
@@ -150,47 +185,68 @@ AppendPipeline::AppendPipeline(SourceBufferPrivateGStreamer& sourceBufferPrivate |
150 | 185 | m_demux = makeGStreamerElement("matroskademux", nullptr); |
151 | 186 | m_typefind = makeGStreamerElement("identity", nullptr); |
152 | 187 | } else if (type == "audio/mpeg"_s) { |
153 | | - m_demux = makeGStreamerElement("identity", nullptr); |
| 188 | + // Will be instantiated later based on typefind results. |
| 189 | + m_demux = nullptr; |
154 | 190 | m_typefind = makeGStreamerElement("typefind", nullptr); |
155 | | - } else |
156 | | - ASSERT_NOT_REACHED(); |
157 | | - |
158 | | -#if !LOG_DISABLED |
159 | | - GRefPtr<GstPad> demuxerPad = adoptGRef(gst_element_get_static_pad(m_demux.get(), "sink")); |
160 | | - m_demuxerDataEnteringPadProbeInformation.appendPipeline = this; |
161 | | - m_demuxerDataEnteringPadProbeInformation.description = "demuxer data entering"; |
162 | | - m_demuxerDataEnteringPadProbeInformation.probeId = gst_pad_add_probe(demuxerPad.get(), GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>(appendPipelinePadProbeDebugInformation), &m_demuxerDataEnteringPadProbeInformation, nullptr); |
163 | | -#endif |
164 | 191 |
|
165 | | - auto elementClass = makeString(gst_element_get_metadata(m_demux.get(), GST_ELEMENT_METADATA_KLASS)); |
166 | | - auto classifiers = elementClass.split('/'); |
167 | | - if (classifiers.contains("Demuxer"_s)) { |
168 | | - // These signals won't outlive the lifetime of `this`. |
169 | | - g_signal_connect_swapped(m_demux.get(), "no-more-pads", G_CALLBACK(+[](AppendPipeline* appendPipeline) { |
| 192 | + g_signal_connect(m_typefind.get(), "have-type", G_CALLBACK(+[]( |
| 193 | + GstElement* typefind, guint, GstCaps* caps, AppendPipeline* appendPipeline) { |
170 | 194 | ASSERT(!isMainThread()); |
171 | | - GST_DEBUG_OBJECT(appendPipeline->pipeline(), "Posting no-more-pads task to main thread"); |
172 | | - appendPipeline->m_taskQueue.enqueueTaskAndWait<AbortableTaskQueue::Void>([appendPipeline]() { |
173 | | - appendPipeline->didReceiveInitializationSegment(); |
174 | | - return AbortableTaskQueue::Void(); |
175 | | - }); |
| 195 | + |
| 196 | + // We don't want to create the demuxer twice if the type changes for whatever reason. |
| 197 | + if (appendPipeline->m_demux) |
| 198 | + return; |
| 199 | + |
| 200 | + auto capsStructure = gst_caps_get_structure(caps, 0); |
| 201 | + ASCIILiteral demuxerElementName; |
| 202 | + if (gst_structure_has_name(capsStructure, "application/x-id3")) |
| 203 | + demuxerElementName = "id3demux"_s; |
| 204 | + else if (gst_structure_has_name(capsStructure, "audio/mpeg")) |
| 205 | + demuxerElementName = "identity"_s; |
| 206 | + |
| 207 | + if (demuxerElementName.isNull()) { |
| 208 | + GST_ELEMENT_ERROR(appendPipeline->pipeline(), STREAM, WRONG_TYPE, |
| 209 | + ("Unsupported caps for audio/mpeg mimetype: %s", |
| 210 | + gst_structure_get_name(capsStructure)), (nullptr)); |
| 211 | + return; |
| 212 | + } |
| 213 | + |
| 214 | + GST_DEBUG_OBJECT(appendPipeline->pipeline(), "Creating %s demuxer for caps: %s", |
| 215 | + demuxerElementName.characters(), gst_structure_get_name(capsStructure)); |
| 216 | + appendPipeline->m_demux = makeGStreamerElement(demuxerElementName.characters(), nullptr); |
| 217 | + ASSERT(appendPipeline->m_demux); |
| 218 | + |
| 219 | + appendPipeline->configureOptionalDemuxerFromAnyThread(); |
| 220 | + |
| 221 | + // The added element had its floating reference sunk after being assigned to the GRefPtr, so the transfer-floating |
| 222 | + // parameter is working as transfer-none here. |
| 223 | + gst_bin_add(GST_BIN(GST_ELEMENT_PARENT(typefind)), appendPipeline->m_demux.get()); |
| 224 | + gst_element_link(appendPipeline->m_typefind.get(), appendPipeline->m_demux.get()); |
| 225 | + |
| 226 | + assertedElementSetState(appendPipeline->m_demux.get(), GST_STATE_PLAYING); |
176 | 227 | }), this); |
177 | 228 | } else { |
178 | | - GRefPtr<GstPad> identitySrcPad = adoptGRef(gst_element_get_static_pad(m_demux.get(), "src")); |
179 | | - gst_pad_add_probe(identitySrcPad.get(), GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>( |
180 | | - +[](GstPad *pad, GstPadProbeInfo*, AppendPipeline* appendPipeline) { |
181 | | - GRefPtr<GstCaps> caps = adoptGRef(gst_pad_get_current_caps(pad)); |
182 | | - if (!caps) |
183 | | - return GST_PAD_PROBE_DROP; |
184 | | - appendPipeline->m_taskQueue.enqueueTaskAndWait<AbortableTaskQueue::Void>([appendPipeline]() { |
185 | | - appendPipeline->didReceiveInitializationSegment(); |
186 | | - return AbortableTaskQueue::Void(); |
187 | | - }); |
188 | | - return GST_PAD_PROBE_REMOVE; |
189 | | - } |
190 | | - ), this, nullptr); |
| 229 | + GST_ELEMENT_ERROR(pipeline(), STREAM, WRONG_TYPE, ("Unsupported container mimetype: %s", type.utf8().data()), (nullptr)); |
| 230 | + return; |
| 231 | + } |
| 232 | + |
| 233 | + // m_demux might be null at this point if there's a typefind pending to identify the proper demuxer to be used |
| 234 | + // (see the audio/mpeg case right above). |
| 235 | + if (m_demux) { |
| 236 | +#if !LOG_DISABLED |
| 237 | + GRefPtr<GstPad> demuxerPad = adoptGRef(gst_element_get_static_pad(m_demux.get(), "sink")); |
| 238 | + m_demuxerDataEnteringPadProbeInformation.appendPipeline = this; |
| 239 | + m_demuxerDataEnteringPadProbeInformation.description = "demuxer data entering"; |
| 240 | + m_demuxerDataEnteringPadProbeInformation.probeId = gst_pad_add_probe(demuxerPad.get(), GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>(appendPipelinePadProbeDebugInformation), &m_demuxerDataEnteringPadProbeInformation, nullptr); |
| 241 | +#endif |
| 242 | + |
| 243 | + configureOptionalDemuxerFromAnyThread(); |
191 | 244 | } |
192 | 245 |
|
193 | | - // Add_many will take ownership of a reference. That's why we used an assignment before. |
| 246 | + // The added elements had their floating references sunk after being assigned to the GRefPtr, so the transfer-floating |
| 247 | + // parameters are working as transfer-none here. |
| 248 | + // Note that m_demux may be null at this point, so the variable argument list would ignore it (m_demux would |
| 249 | + // act as a nullptr list guard). |
194 | 250 | gst_bin_add_many(GST_BIN(m_pipeline.get()), m_appsrc.get(), m_typefind.get(), m_demux.get(), nullptr); |
195 | 251 | gst_element_link_many(m_appsrc.get(), m_typefind.get(), m_demux.get(), nullptr); |
196 | 252 |
|
|
0 commit comments