1 // MediaParserFfmpeg.cpp: FFMPG media parsers, for Gnash
2 //
3 // Copyright (C) 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc.
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program 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
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 //
20
21
22 #include "MediaParserGst.h"
23 #include "GnashException.h"
24 #include "log.h"
25 #include "IOChannel.h"
26
27 #include "GstUtil.h" // for GST_TIME_AS_MSECONDS
28 #include "swfdec_codec_gst.h"
29 #include <iostream>
30 #include <fstream>
31 #include <mutex>
32
33 #define PUSHBUF_SIZE 1024
34
35 // #define GNASH_DEBUG_DATAFLOW
36
37 namespace gnash {
38 namespace media {
39 namespace gst {
40
41
MediaParserGst(std::unique_ptr<IOChannel> stream)42 MediaParserGst::MediaParserGst(std::unique_ptr<IOChannel> stream)
43 : MediaParser(std::move(stream)),
44 _bin(nullptr),
45 _srcpad(nullptr),
46 _audiosink(nullptr),
47 _videosink(nullptr),
48 _demux_probe_ended(false)
49 {
50 gst_init (nullptr, nullptr);
51
52 _bin = gst_bin_new ("NULL");
53 if (!_bin) {
54 throw GnashException(_("MediaParserGst couldn't create a bin"));
55 }
56
57 GstElement* typefind = gst_element_factory_make("typefind", nullptr);
58 if (!typefind) {
59 throw GnashException(_("MediaParserGst couldn't create a typefind element."));
60 }
61
62 gst_bin_add(GST_BIN(_bin), typefind);
63
64 g_signal_connect (typefind, "have-type", G_CALLBACK (MediaParserGst::cb_typefound), this);
65
66 GstCaps* srccaps = gst_caps_new_any();
67 _srcpad = swfdec_gst_connect_srcpad (typefind, srccaps);
68 gst_caps_unref(srccaps);
69
70 if (gst_element_set_state (_bin, GST_STATE_PLAYING) != GST_STATE_CHANGE_SUCCESS) {
71 throw GnashException(_("MediaParserGst could not change element state"));
72 }
73
74 if (gst_element_set_state (_bin, GST_STATE_PLAYING) != GST_STATE_CHANGE_SUCCESS) {
75 throw MediaException(_("MediaParserGst could not change element state"));
76 }
77
78 // FIXME: threading decisions should not be up to the parser!
79 startParserThread();
80 }
81
~MediaParserGst()82 MediaParserGst::~MediaParserGst()
83 {
84 stopParserThread();
85
86 if (_bin) {
87 gst_element_set_state (_bin, GST_STATE_NULL);
88 g_object_unref (_bin);
89 }
90
91 if (_srcpad) {
92 g_object_unref (_srcpad);
93 }
94
95 if (_videosink) {
96 g_object_unref (_videosink);
97 }
98
99 if (_audiosink) {
100 g_object_unref (_audiosink);
101 }
102
103 // Sanity check for threading bug...
104 assert(_enc_video_frames.empty());
105 assert(_enc_audio_frames.empty());
106 }
107
108 bool
seek(std::uint32_t & milliseconds)109 MediaParserGst::seek(std::uint32_t& milliseconds)
110 {
111 return gst_element_seek_simple(_bin, GST_FORMAT_TIME,
112 GstSeekFlags(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT),
113 GST_MSECOND * milliseconds);
114 }
115
116 boost::optional<Id3Info>
getId3Info() const117 MediaParserGst::getId3Info() const
118 {
119 return boost::optional<Id3Info>();
120 }
121
122 bool
parseNextChunk()123 MediaParserGst::parseNextChunk()
124 {
125 std::lock_guard<std::mutex> streamLock(_streamMutex);
126
127 if (emitEncodedFrames()) {
128 return true;
129 }
130
131 // FIXME: our caller check for _parsingComplete prior
132 // to call parseNextChunk
133 if (_stream->eof() || _stream->bad()) {
134 //log_debug (_("Stream EOF, emitting!"));
135 _parsingComplete = true;
136 return false;
137 }
138
139 pushGstBuffer();
140
141 _bytesLoaded = _stream->tell();
142
143 emitEncodedFrames();
144
145 return true;
146
147 }
148
149 std::uint64_t
getBytesLoaded() const150 MediaParserGst::getBytesLoaded() const
151 {
152 return _bytesLoaded.load();
153 }
154
155 bool
pushGstBuffer()156 MediaParserGst::pushGstBuffer()
157 {
158 GstBuffer* buffer = gst_buffer_new_and_alloc(PUSHBUF_SIZE);
159
160 std::streamoff ret = _stream->read(GST_BUFFER_DATA(buffer), PUSHBUF_SIZE);
161
162 if (ret < PUSHBUF_SIZE) {
163 if (!_stream->eof() && !_stream->bad()) {
164 log_error(_("MediaParserGst failed to read the stream, but it did"
165 " not reach EOF or enter a bad state."));
166 }
167
168 if (!ret) {
169 gst_buffer_unref(buffer);
170 return false;
171 }
172
173 GST_BUFFER_SIZE(buffer) = ret;
174 }
175
176 GstFlowReturn rv = gst_pad_push (_srcpad, buffer);
177 if (!GST_FLOW_IS_SUCCESS (rv)) {
178 // This is not (necessarily) an error situation, because some streams
179 // contain garbage. However, Gstreamer will continue accepting new
180 // frames.
181 log_debug(_("MediaParserGst failed to push more data into the demuxer."));
182 return true;
183 }
184
185 return true;
186 }
187
188 bool
emitEncodedFrames()189 MediaParserGst::emitEncodedFrames()
190 {
191 if (_enc_audio_frames.empty() && _enc_video_frames.empty()) {
192 return false;
193 }
194
195 while (!_enc_audio_frames.empty()) {
196 EncodedAudioFrame* frame = _enc_audio_frames.front();
197 pushEncodedAudioFrame(std::unique_ptr<EncodedAudioFrame>(frame));
198 _enc_audio_frames.pop_front();
199 }
200
201 while (!_enc_video_frames.empty()) {
202 EncodedVideoFrame* frame = _enc_video_frames.front();
203 pushEncodedVideoFrame(std::unique_ptr<EncodedVideoFrame>(frame));
204 _enc_video_frames.pop_front();
205 }
206
207 return true;
208 }
209
210 void
rememberAudioFrame(EncodedAudioFrame * frame)211 MediaParserGst::rememberAudioFrame(EncodedAudioFrame* frame)
212 {
213 _enc_audio_frames.push_back(frame);
214 }
215
216 void
rememberVideoFrame(EncodedVideoFrame * frame)217 MediaParserGst::rememberVideoFrame(EncodedVideoFrame* frame)
218 {
219 _enc_video_frames.push_back(frame);
220 }
221
222 /// Determines whether all multimedia streams have been found.
223 //
224 /// This can happen when
225 /// the stream has a nondemuxable format, like MP3, or when the linked
226 /// demuxer has signaled "no more pads", or when the first video and
227 /// audio streams have been found.
228
229 static void
print_caps(GstCaps * caps)230 print_caps(GstCaps* caps)
231 {
232 if (!caps) {
233 return;
234 }
235
236 gchar* capsstr = gst_caps_to_string (caps);
237
238 if (!capsstr) {
239 return;
240 }
241
242 log_debug (_("MediaParserGst/typefound: Detected media type %s"), capsstr);
243
244 g_free(capsstr);
245 }
246
247
248 void
link_to_fakesink(GstPad * pad)249 MediaParserGst::link_to_fakesink(GstPad* pad)
250 {
251 GstElement* fakesink = gst_element_factory_make("fakesink", nullptr);
252
253 if (!fakesink) {
254 throw MediaException(_("MediaParserGst Failed to create fakesink."));
255 }
256
257 gboolean success = gst_bin_add(GST_BIN(_bin), fakesink);
258
259 if (!success) {
260 gst_object_unref(fakesink);
261 throw MediaException(_("MediaParserGst Failed to create fakesink."));
262 }
263
264 GstPad* sinkpad = gst_element_get_static_pad (fakesink, "sink");
265 if (!sinkpad) {
266 gst_object_unref(fakesink);
267 throw MediaException(_("MediaParserGst: couldn't get the fakesink "
268 "src element."));
269 }
270
271 GstPadLinkReturn ret = gst_pad_link(pad, sinkpad);
272 if (!GST_PAD_LINK_SUCCESSFUL(ret)) {
273 gst_object_unref(fakesink);
274 gst_object_unref(sinkpad);
275 throw MediaException(_("MediaParserGst: couln't link fakesink"));
276 }
277
278 if (gst_element_set_state (_bin, GST_STATE_PLAYING) != GST_STATE_CHANGE_SUCCESS) {
279 throw GnashException(_("MediaParserGst could not change element state"));
280 }
281 }
282
283
284 // static
285 void
cb_typefound(GstElement * typefind,guint,GstCaps * caps,gpointer data)286 MediaParserGst::cb_typefound(GstElement* typefind, guint /*probability*/,
287 GstCaps* caps, gpointer data)
288 {
289 print_caps(caps);
290
291 MediaParserGst* parser = static_cast<MediaParserGst*>(data);
292
293 GstElementFactory* demuxfactory = swfdec_gst_get_demuxer_factory(caps);
294
295 if (!demuxfactory) {
296 GstPad* srcpad = gst_element_get_static_pad(typefind, "src");
297
298 if (!srcpad) {
299 throw MediaException(_("MediaParserGst: couldn't get the typefind "
300 "src element."));
301 }
302
303 cb_pad_added(typefind, srcpad, parser);
304 gst_object_unref(GST_OBJECT(srcpad));
305 parser->_demux_probe_ended = true;
306 return;
307 }
308
309 // We have a factory, so create the demuxer.
310 GstElement* demuxer = gst_element_factory_create(demuxfactory, "demuxer");
311 gst_object_unref(GST_OBJECT(demuxfactory));
312
313 if (!demuxer) {
314 throw MediaException(_("MediaParserGst: couldn't create the "
315 "demuxer"));
316 }
317
318 gboolean success = gst_bin_add(GST_BIN(parser->_bin), demuxer);
319 if (!success) {
320 log_error(_("MediaParserGst: failed adding demuxer to bin."));
321 }
322
323 success = gst_element_link(typefind, demuxer);
324 if (!success) {
325 throw MediaException(_("MediaParserGst: failed adding demuxer "
326 "to bin."));
327 }
328
329 g_signal_connect(demuxer, "pad-added",
330 G_CALLBACK(MediaParserGst::cb_pad_added), parser);
331 g_signal_connect(demuxer, "no-more-pads",
332 G_CALLBACK(MediaParserGst::cb_no_more_pads), parser);
333
334 if (gst_element_set_state(parser->_bin, GST_STATE_PLAYING) !=
335 GST_STATE_CHANGE_SUCCESS) {
336 throw GnashException(_("MediaParserGst could not change "
337 "element state"));
338 }
339 }
340
341 //static
cb_pad_added(GstElement *,GstPad * new_pad,gpointer data)342 void MediaParserGst::cb_pad_added(GstElement* /* element */, GstPad* new_pad,
343 gpointer data)
344 {
345 MediaParserGst* parser = static_cast<MediaParserGst*>(data);
346
347 GstCaps* caps = gst_pad_get_caps(new_pad);
348 print_caps(caps);
349
350 GstStructure* str = gst_caps_get_structure (caps, 0);
351 if (!str) {
352 log_error(_("MediaParserGst: couldn't get structure name."));
353 parser->link_to_fakesink(new_pad);
354 return;
355 }
356
357 const gchar* caps_name = gst_structure_get_name (str);
358
359 bool media_type_audio;
360
361 if (std::equal(caps_name, caps_name+5, "audio")) {
362 media_type_audio = true;
363 } else if (std::equal(caps_name, caps_name+5, "video")) {
364 media_type_audio = false;
365 } else {
366 log_error(_("MediaParserGst: ignoring stream of type %s."),
367 caps_name);
368 parser->link_to_fakesink(new_pad);
369 return;
370 }
371
372 gboolean parsed = false;
373 gboolean framed = false;
374
375 gst_structure_get_boolean(str, "parsed", &parsed);
376 gst_structure_get_boolean(str, "framed", &framed);
377
378 bool already_parsed = parsed || framed;
379
380 GstPad* final_pad = nullptr;
381
382 if (already_parsed) {
383 final_pad = new_pad;
384 } else {
385 // We'll try to find a parser, so that we will eventually receive
386 // timestamped buffers, on which the MediaParser system relies.
387 GstElementFactory* parserfactory = swfdec_gst_get_parser_factory (caps);
388
389 if (!parserfactory) {
390 log_error(_("MediaParserGst: Failed to find a parser (media: %s)."),
391 caps_name);
392 parser->link_to_fakesink(new_pad);
393 return;
394 }
395
396 GstElement* parserel = gst_element_factory_create (parserfactory, nullptr);
397 gst_object_unref (parserfactory);
398 if (!parserel) {
399 log_error(_("MediaParserGst: Failed to find a parser. We'll continue, "
400 "but either audio or video will not work!"));
401 parser->link_to_fakesink(new_pad);
402 return;
403 }
404
405 gboolean success = gst_bin_add(GST_BIN(parser->_bin), parserel);
406 if (!success) {
407 gst_object_unref(parserel);
408 log_error(_("MediaParserGst: couldn't add parser."));
409 parser->link_to_fakesink(new_pad);
410 return;
411 }
412
413 GstPad* sinkpad = gst_element_get_static_pad (parserel, "sink");
414 assert(sinkpad);
415
416 GstPadLinkReturn ret = gst_pad_link(new_pad, sinkpad);
417
418 gst_object_unref(GST_OBJECT(sinkpad));
419
420 if (!GST_PAD_LINK_SUCCESSFUL(ret)) {
421 log_error(_("MediaParserGst: couldn't link parser."));
422 parser->link_to_fakesink(new_pad);
423 return;
424 }
425
426 final_pad = gst_element_get_static_pad (parserel, "src");
427 }
428
429 if (media_type_audio) {
430
431 parser->_audiosink = swfdec_gst_connect_sinkpad_by_pad (final_pad, caps);
432 if (!parser->_audiosink) {
433 log_error(_("MediaParserGst: couldn't link \"fake\" sink."));
434 return;
435 }
436
437 gst_pad_set_chain_function(parser->_audiosink,
438 MediaParserGst::cb_chain_func_audio);
439
440 g_object_set_data(G_OBJECT (parser->_audiosink), "mediaparser-obj",
441 parser);
442
443 LOG_ONCE(
444 log_unimpl("MediaParserGst won't set codec, sampleRate, "
445 "sampleSize, stereo and duration in AudioInfo");
446 );
447 AudioInfo* audioinfo = new AudioInfo(0, 0, 0, false, 0,
448 CODEC_TYPE_CUSTOM);
449 audioinfo->extra.reset(new ExtraInfoGst(caps));
450
451 parser->_audioInfo.reset(audioinfo);
452 log_debug(_("MediaParserGst: Linked audio source (type: %s)"), caps_name);
453
454 } else {
455 // A parser element may still change the caps in a way that is
456 // incompatible with the caps provided by the demuxer. The only parser
457 // I am aware of that does this is h264parse. Setting the caps to the
458 // very simple form "video/codec" does not appear to be problematic for
459 // other codecs either.
460 GstCaps* sinkcaps = gst_caps_from_string(caps_name);
461
462 parser->_videosink = swfdec_gst_connect_sinkpad_by_pad (final_pad, sinkcaps);
463 gst_caps_unref(sinkcaps);
464
465 if (!parser->_videosink) {
466 log_error(_("MediaParserGst: couldn't link \"fake\" sink."));
467 return;
468 }
469
470 gst_pad_set_chain_function(parser->_videosink,
471 MediaParserGst::cb_chain_func_video);
472
473 g_object_set_data(G_OBJECT(parser->_videosink), "mediaparser-obj",
474 parser);
475
476 VideoInfo* videoinfo = new VideoInfo(0, 0, 0, false, 0,
477 CODEC_TYPE_CUSTOM);
478 videoinfo->extra.reset(new ExtraInfoGst(caps));
479
480 parser->_videoInfo.reset(videoinfo);
481
482 log_debug(_("MediaParserGst: Linked video source (type: %s)"), caps_name);
483 }
484
485 if (!already_parsed) {
486 gst_object_unref(GST_OBJECT(final_pad));
487 }
488
489 if (gst_element_set_state (parser->_bin, GST_STATE_PLAYING) != GST_STATE_CHANGE_SUCCESS) {
490 throw GnashException(_("MediaParserGst could not change element state"));
491 }
492 }
493
494 // static
495 void
cb_no_more_pads(GstElement *,gpointer data)496 MediaParserGst::cb_no_more_pads (GstElement* /* demuxer */, gpointer data)
497 {
498 MediaParserGst* parser = static_cast<MediaParserGst*>(data);
499
500 parser->_demux_probe_ended = true;
501 }
502
503
504
505 // static
506 GstFlowReturn
cb_chain_func_video(GstPad * pad,GstBuffer * buffer)507 MediaParserGst::cb_chain_func_video (GstPad *pad, GstBuffer *buffer)
508 {
509 MediaParserGst* parser = (MediaParserGst*) g_object_get_data (G_OBJECT (pad), "mediaparser-obj");
510 assert(parser);
511
512 unsigned int frame_num = 0;
513 unsigned int timestamp = 0;
514
515 if (GST_BUFFER_TIMESTAMP_IS_VALID(buffer)) {
516 timestamp = GST_TIME_AS_MSECONDS(GST_BUFFER_TIMESTAMP(buffer));
517 }
518
519 if (GST_BUFFER_OFFSET_IS_VALID(buffer)) {
520 frame_num = GST_BUFFER_OFFSET(buffer);
521 }
522
523 EncodedVideoFrame* frame = new EncodedVideoFrame(nullptr, GST_BUFFER_SIZE(buffer), frame_num, timestamp);
524
525 frame->extradata.reset(new EncodedExtraGstData(buffer));
526
527 #ifdef GNASH_DEBUG_DATAFLOW
528 log_debug("remembering video buffer with timestamp %d and frame number %d", timestamp, frame_num);
529 #endif
530
531 parser->rememberVideoFrame(frame);
532
533 return GST_FLOW_OK;
534 }
535
536
537 // static
538 GstFlowReturn
cb_chain_func_audio(GstPad * pad,GstBuffer * buffer)539 MediaParserGst::cb_chain_func_audio (GstPad *pad, GstBuffer *buffer)
540 {
541 MediaParserGst* parser = (MediaParserGst*) g_object_get_data (G_OBJECT (pad), "mediaparser-obj");
542 assert(parser);
543
544 EncodedAudioFrame* frame = new EncodedAudioFrame;
545
546 // 'dataSize' should reflect size of 'data'.
547 // Since we're not allocating any 'data' there's no point
548 // in setting dataSize.
549 //frame->dataSize = GST_BUFFER_SIZE(buffer);
550
551 if (GST_BUFFER_TIMESTAMP_IS_VALID(buffer)) {
552 frame->timestamp = GST_TIME_AS_MSECONDS(GST_BUFFER_TIMESTAMP(buffer));
553 } else {
554 // What if a frame with invalid timestamp is found
555 // in the middle of the stream ? Using 0 here, might
556 // mean that the computed "bufferTime" is huge (0 to last valid timestamp)
557 // Not necessarely a big deal, but a conceptual glitch.
558 frame->timestamp = 0;
559 }
560
561 frame->extradata.reset(new EncodedExtraGstData(buffer));
562 frame->dataSize = GST_BUFFER_SIZE(buffer);
563
564 #ifdef GNASH_DEBUG_DATAFLOW
565 log_debug("remembering audio buffer with timestamp %d.", frame->timestamp);
566 #endif
567
568 parser->rememberAudioFrame(frame);
569
570
571 return GST_FLOW_OK;
572 }
573
574 } // gnash.media.gst namespace
575 } // namespace media
576 } // namespace gnash
577