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