1 /* GStreamer
2  * Copyright (C) <2007> Julien Moutte <julien@moutte.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 /**
21  * SECTION:element-flvdemux
22  *
23  * flvdemux demuxes an FLV file into the different contained streams.
24  *
25  * <refsect2>
26  * <title>Example launch line</title>
27  * |[
28  * gst-launch-1.0 -v filesrc location=/path/to/flv ! flvdemux ! audioconvert ! autoaudiosink
29  * ]| This pipeline demuxes an FLV file and outputs the contained raw audio streams.
30  * </refsect2>
31  */
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #include "gstflvdemux.h"
38 #include "gstflvmux.h"
39 
40 #include <string.h>
41 #include <stdio.h>
42 #include <gst/base/gstbytereader.h>
43 #include <gst/base/gstbytewriter.h>
44 #include <gst/pbutils/descriptions.h>
45 #include <gst/pbutils/pbutils.h>
46 #include <gst/audio/audio.h>
47 #include <gst/video/video.h>
48 #include <gst/tag/tag.h>
49 
50 /* FIXME: don't rely on own GstIndex */
51 #include "gstindex.c"
52 #include "gstmemindex.c"
53 #define GST_ASSOCIATION_FLAG_NONE GST_INDEX_ASSOCIATION_FLAG_NONE
54 #define GST_ASSOCIATION_FLAG_KEY_UNIT GST_INDEX_ASSOCIATION_FLAG_KEY_UNIT
55 #define GST_ASSOCIATION_FLAG_DELTA_UNIT GST_INDEX_ASSOCIATION_FLAG_DELTA_UNIT
56 
57 static GstStaticPadTemplate flv_sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
58     GST_PAD_SINK,
59     GST_PAD_ALWAYS,
60     GST_STATIC_CAPS ("video/x-flv")
61     );
62 
63 static GstStaticPadTemplate audio_src_template =
64     GST_STATIC_PAD_TEMPLATE ("audio",
65     GST_PAD_SRC,
66     GST_PAD_SOMETIMES,
67     GST_STATIC_CAPS
68     ("audio/x-adpcm, layout = (string) swf, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
69         "audio/mpeg, mpegversion = (int) 1, layer = (int) 3, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 22050, 44100 }, parsed = (boolean) TRUE; "
70         "audio/mpeg, mpegversion = (int) 4, stream-format = (string) raw, framed = (boolean) TRUE; "
71         "audio/x-nellymoser, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 16000, 22050, 44100 }; "
72         "audio/x-raw, format = (string) { U8, S16LE }, layout = (string) interleaved, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
73         "audio/x-alaw, channels = (int) { 1, 2 }, rate = (int) 8000; "
74         "audio/x-mulaw, channels = (int) { 1, 2 }, rate = (int) 8000; "
75         "audio/x-speex, channels = (int) 1, rate = (int) 16000;")
76     );
77 
78 static GstStaticPadTemplate video_src_template =
79     GST_STATIC_PAD_TEMPLATE ("video",
80     GST_PAD_SRC,
81     GST_PAD_SOMETIMES,
82     GST_STATIC_CAPS ("video/x-flash-video, flvversion=(int) 1; "
83         "video/x-flash-screen; "
84         "video/x-vp6-flash; " "video/x-vp6-alpha; "
85         "video/x-h264, stream-format=avc;")
86     );
87 
88 GST_DEBUG_CATEGORY_STATIC (flvdemux_debug);
89 #define GST_CAT_DEFAULT flvdemux_debug
90 
91 #define gst_flv_demux_parent_class parent_class
92 G_DEFINE_TYPE (GstFlvDemux, gst_flv_demux, GST_TYPE_ELEMENT);
93 
94 /* 9 bytes of header + 4 bytes of first previous tag size */
95 #define FLV_HEADER_SIZE 13
96 /* 1 byte of tag type + 3 bytes of tag data size */
97 #define FLV_TAG_TYPE_SIZE 4
98 
99 /* two seconds - consider dts are resynced to another base if this different */
100 #define RESYNC_THRESHOLD 2000
101 
102 /* how much stream time to wait for audio tags to appear after we have video, or vice versa */
103 #define NO_MORE_PADS_THRESHOLD (6 * GST_SECOND)
104 
105 static gboolean flv_demux_handle_seek_push (GstFlvDemux * demux,
106     GstEvent * event);
107 static gboolean gst_flv_demux_handle_seek_pull (GstFlvDemux * demux,
108     GstEvent * event, gboolean seeking);
109 
110 static gboolean gst_flv_demux_query (GstPad * pad, GstObject * parent,
111     GstQuery * query);
112 static gboolean gst_flv_demux_src_event (GstPad * pad, GstObject * parent,
113     GstEvent * event);
114 
115 static GstIndex *gst_flv_demux_get_index (GstElement * element);
116 
117 static void gst_flv_demux_push_tags (GstFlvDemux * demux);
118 
119 static void
gst_flv_demux_parse_and_add_index_entry(GstFlvDemux * demux,GstClockTime ts,guint64 pos,gboolean keyframe)120 gst_flv_demux_parse_and_add_index_entry (GstFlvDemux * demux, GstClockTime ts,
121     guint64 pos, gboolean keyframe)
122 {
123   GstIndexAssociation associations[2];
124   GstIndex *index;
125   GstIndexEntry *entry;
126 
127   GST_LOG_OBJECT (demux,
128       "adding key=%d association %" GST_TIME_FORMAT "-> %" G_GUINT64_FORMAT,
129       keyframe, GST_TIME_ARGS (ts), pos);
130 
131   /* if upstream is not seekable there is no point in building an index */
132   if (!demux->upstream_seekable)
133     return;
134 
135   index = gst_flv_demux_get_index (GST_ELEMENT (demux));
136 
137   if (!index)
138     return;
139 
140   /* entry may already have been added before, avoid adding indefinitely */
141   entry = gst_index_get_assoc_entry (index, demux->index_id,
142       GST_INDEX_LOOKUP_EXACT, GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_BYTES, pos);
143 
144   if (entry) {
145 #ifndef GST_DISABLE_GST_DEBUG
146     gint64 time = 0;
147     gboolean key;
148 
149     gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
150     key = ! !(GST_INDEX_ASSOC_FLAGS (entry) & GST_ASSOCIATION_FLAG_KEY_UNIT);
151     GST_LOG_OBJECT (demux, "position already mapped to time %" GST_TIME_FORMAT
152         ", keyframe %d", GST_TIME_ARGS (time), key);
153     /* there is not really a way to delete the existing one */
154     if (time != ts || key != ! !keyframe)
155       GST_DEBUG_OBJECT (demux, "metadata mismatch");
156 #endif
157     gst_object_unref (index);
158     return;
159   }
160 
161   associations[0].format = GST_FORMAT_TIME;
162   associations[0].value = ts;
163   associations[1].format = GST_FORMAT_BYTES;
164   associations[1].value = pos;
165 
166   gst_index_add_associationv (index, demux->index_id,
167       (keyframe) ? GST_ASSOCIATION_FLAG_KEY_UNIT :
168       GST_ASSOCIATION_FLAG_DELTA_UNIT, 2,
169       (const GstIndexAssociation *) &associations);
170 
171   if (pos > demux->index_max_pos)
172     demux->index_max_pos = pos;
173   if (ts > demux->index_max_time)
174     demux->index_max_time = ts;
175 
176   gst_object_unref (index);
177 }
178 
179 static gchar *
FLV_GET_STRING(GstByteReader * reader)180 FLV_GET_STRING (GstByteReader * reader)
181 {
182   guint16 string_size = 0;
183   gchar *string = NULL;
184   const guint8 *str = NULL;
185 
186   g_return_val_if_fail (reader != NULL, NULL);
187 
188   if (G_UNLIKELY (!gst_byte_reader_get_uint16_be (reader, &string_size)))
189     return NULL;
190 
191   if (G_UNLIKELY (string_size > gst_byte_reader_get_remaining (reader)))
192     return NULL;
193 
194   string = g_try_malloc0 (string_size + 1);
195   if (G_UNLIKELY (!string)) {
196     return NULL;
197   }
198 
199   if (G_UNLIKELY (!gst_byte_reader_get_data (reader, string_size, &str))) {
200     g_free (string);
201     return NULL;
202   }
203 
204   memcpy (string, str, string_size);
205   if (!g_utf8_validate (string, string_size, NULL)) {
206     g_free (string);
207     return NULL;
208   }
209 
210   return string;
211 }
212 
213 static void
gst_flv_demux_check_seekability(GstFlvDemux * demux)214 gst_flv_demux_check_seekability (GstFlvDemux * demux)
215 {
216   GstQuery *query;
217   gint64 start = -1, stop = -1;
218 
219   demux->upstream_seekable = FALSE;
220 
221   query = gst_query_new_seeking (GST_FORMAT_BYTES);
222   if (!gst_pad_peer_query (demux->sinkpad, query)) {
223     GST_DEBUG_OBJECT (demux, "seeking query failed");
224     gst_query_unref (query);
225     return;
226   }
227 
228   gst_query_parse_seeking (query, NULL, &demux->upstream_seekable,
229       &start, &stop);
230 
231   gst_query_unref (query);
232 
233   /* try harder to query upstream size if we didn't get it the first time */
234   if (demux->upstream_seekable && stop == -1) {
235     GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
236     gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
237   }
238 
239   /* if upstream doesn't know the size, it's likely that it's not seekable in
240    * practice even if it technically may be seekable */
241   if (demux->upstream_seekable && (start != 0 || stop <= start)) {
242     GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
243     demux->upstream_seekable = FALSE;
244   }
245 
246   GST_DEBUG_OBJECT (demux, "upstream seekable: %d", demux->upstream_seekable);
247 }
248 
249 static GstDateTime *
parse_flv_demux_parse_date_string(const gchar * s)250 parse_flv_demux_parse_date_string (const gchar * s)
251 {
252   static const gchar months[12][4] = {
253     "Jan", "Feb", "Mar", "Apr",
254     "May", "Jun", "Jul", "Aug",
255     "Sep", "Oct", "Nov", "Dec"
256   };
257   GstDateTime *dt = NULL;
258   gchar **tokens;
259   guint64 d;
260   gchar *endptr, *stripped;
261   gint i, hh, mm, ss;
262   gint year = -1, month = -1, day = -1;
263   gint hour = -1, minute = -1, seconds = -1;
264 
265   stripped = g_strstrip (g_strdup (s));
266 
267   /* "Fri Oct 15 15:13:16 2004" needs to be parsed */
268   tokens = g_strsplit (stripped, " ", -1);
269 
270   g_free (stripped);
271 
272   if (g_strv_length (tokens) != 5)
273     goto out;
274 
275   /* year */
276   d = g_ascii_strtoull (tokens[4], &endptr, 10);
277   if (d == 0 && *endptr != '\0')
278     goto out;
279 
280   year = d;
281 
282   /* month */
283   if (strlen (tokens[1]) != 3)
284     goto out;
285   for (i = 0; i < 12; i++) {
286     if (!strcmp (tokens[1], months[i])) {
287       break;
288     }
289   }
290   if (i == 12)
291     goto out;
292 
293   month = i + 1;
294 
295   /* day */
296   d = g_ascii_strtoull (tokens[2], &endptr, 10);
297   if (d == 0 && *endptr != '\0')
298     goto out;
299 
300   day = d;
301 
302   /* time */
303   hh = mm = ss = 0;
304   if (sscanf (tokens[3], "%d:%d:%d", &hh, &mm, &ss) < 2)
305     goto out;
306   if (hh >= 0 && hh < 24 && mm >= 0 && mm < 60 && ss >= 0 && ss < 60) {
307     hour = hh;
308     minute = mm;
309     seconds = ss;
310   }
311 
312 out:
313 
314   if (tokens)
315     g_strfreev (tokens);
316 
317   if (year > 0)
318     dt = gst_date_time_new (0.0, year, month, day, hour, minute, seconds);
319 
320   return dt;
321 }
322 
323 static gboolean
gst_flv_demux_parse_metadata_item(GstFlvDemux * demux,GstByteReader * reader,gboolean * end_marker)324 gst_flv_demux_parse_metadata_item (GstFlvDemux * demux, GstByteReader * reader,
325     gboolean * end_marker)
326 {
327   gchar *tag_name = NULL;
328   guint8 tag_type = 0;
329 
330   /* Initialize the end_marker flag to FALSE */
331   *end_marker = FALSE;
332 
333   /* Name of the tag */
334   tag_name = FLV_GET_STRING (reader);
335   if (G_UNLIKELY (!tag_name)) {
336     GST_WARNING_OBJECT (demux, "failed reading tag name");
337     return FALSE;
338   }
339 
340   /* What kind of object is that */
341   if (!gst_byte_reader_get_uint8 (reader, &tag_type))
342     goto error;
343 
344   GST_DEBUG_OBJECT (demux, "tag name %s, tag type %d", tag_name, tag_type);
345 
346   switch (tag_type) {
347     case 0:                    /* Double */
348     {                           /* Use a union to read the uint64 and then as a double */
349       gdouble d = 0;
350 
351       if (!gst_byte_reader_get_float64_be (reader, &d))
352         goto error;
353 
354       GST_DEBUG_OBJECT (demux, "%s => (double) %f", tag_name, d);
355 
356       if (!strcmp (tag_name, "duration")) {
357         demux->duration = d * GST_SECOND;
358 
359         gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
360             GST_TAG_DURATION, demux->duration, NULL);
361       } else if (!strcmp (tag_name, "AspectRatioX")) {
362         demux->par_x = d;
363         demux->got_par = TRUE;
364       } else if (!strcmp (tag_name, "AspectRatioY")) {
365         demux->par_y = d;
366         demux->got_par = TRUE;
367       } else if (!strcmp (tag_name, "width")) {
368         demux->w = d;
369       } else if (!strcmp (tag_name, "height")) {
370         demux->h = d;
371       } else if (!strcmp (tag_name, "framerate")) {
372         demux->framerate = d;
373       } else if (!strcmp (tag_name, "audiodatarate")) {
374         gst_tag_list_add (demux->audio_tags, GST_TAG_MERGE_REPLACE,
375             GST_TAG_NOMINAL_BITRATE, (guint) (d * 1024), NULL);
376       } else if (!strcmp (tag_name, "videodatarate")) {
377         gst_tag_list_add (demux->video_tags, GST_TAG_MERGE_REPLACE,
378             GST_TAG_NOMINAL_BITRATE, (guint) (d * 1024), NULL);
379       } else {
380         GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name);
381       }
382 
383       break;
384     }
385     case 1:                    /* Boolean */
386     {
387       guint8 b = 0;
388 
389       if (!gst_byte_reader_get_uint8 (reader, &b))
390         goto error;
391 
392       GST_DEBUG_OBJECT (demux, "%s => (boolean) %d", tag_name, b);
393 
394       GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name);
395 
396       break;
397     }
398     case 2:                    /* String */
399     {
400       gchar *s = NULL;
401 
402       s = FLV_GET_STRING (reader);
403       if (s == NULL)
404         goto error;
405 
406       GST_DEBUG_OBJECT (demux, "%s => (string) %s", tag_name, s);
407 
408       if (!strcmp (tag_name, "creationdate")) {
409         GstDateTime *dt;
410 
411         dt = parse_flv_demux_parse_date_string (s);
412         if (dt == NULL) {
413           GST_DEBUG_OBJECT (demux, "Failed to parse '%s' into datetime", s);
414         } else {
415           gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
416               GST_TAG_DATE_TIME, dt, NULL);
417           gst_date_time_unref (dt);
418         }
419       } else if (!strcmp (tag_name, "creator")) {
420         gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
421             GST_TAG_ARTIST, s, NULL);
422       } else if (!strcmp (tag_name, "title")) {
423         gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
424             GST_TAG_TITLE, s, NULL);
425       } else if (!strcmp (tag_name, "metadatacreator")
426           || !strcmp (tag_name, "encoder")) {
427         gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
428             GST_TAG_ENCODER, s, NULL);
429       } else {
430         GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name);
431       }
432 
433       g_free (s);
434 
435       break;
436     }
437     case 3:                    /* Object */
438     {
439       gboolean end_of_object_marker = FALSE;
440 
441       while (!end_of_object_marker) {
442         gboolean ok = gst_flv_demux_parse_metadata_item (demux, reader,
443             &end_of_object_marker);
444 
445         if (G_UNLIKELY (!ok)) {
446           GST_WARNING_OBJECT (demux, "failed reading a tag, skipping");
447           goto error;
448         }
449       }
450 
451       break;
452     }
453     case 8:                    /* ECMA array */
454     {
455       guint32 nb_elems = 0;
456       gboolean end_of_object_marker = FALSE;
457 
458       if (!gst_byte_reader_get_uint32_be (reader, &nb_elems))
459         goto error;
460 
461       GST_DEBUG_OBJECT (demux, "there are approx. %d elements in the array",
462           nb_elems);
463 
464       while (!end_of_object_marker) {
465         gboolean ok = gst_flv_demux_parse_metadata_item (demux, reader,
466             &end_of_object_marker);
467 
468         if (G_UNLIKELY (!ok)) {
469           GST_WARNING_OBJECT (demux, "failed reading a tag, skipping");
470           goto error;
471         }
472       }
473 
474       break;
475     }
476     case 9:                    /* End marker */
477     {
478       GST_DEBUG_OBJECT (demux, "end marker ?");
479       if (tag_name[0] == '\0') {
480 
481         GST_DEBUG_OBJECT (demux, "end marker detected");
482 
483         *end_marker = TRUE;
484       }
485 
486       break;
487     }
488     case 10:                   /* Array */
489     {
490       guint32 nb_elems = 0;
491 
492       if (!gst_byte_reader_get_uint32_be (reader, &nb_elems))
493         goto error;
494 
495       GST_DEBUG_OBJECT (demux, "array has %d elements", nb_elems);
496 
497       if (!strcmp (tag_name, "times")) {
498         if (demux->times) {
499           g_array_free (demux->times, TRUE);
500         }
501         demux->times = g_array_new (FALSE, TRUE, sizeof (gdouble));
502       } else if (!strcmp (tag_name, "filepositions")) {
503         if (demux->filepositions) {
504           g_array_free (demux->filepositions, TRUE);
505         }
506         demux->filepositions = g_array_new (FALSE, TRUE, sizeof (gdouble));
507       }
508 
509       while (nb_elems--) {
510         guint8 elem_type = 0;
511 
512         if (!gst_byte_reader_get_uint8 (reader, &elem_type))
513           goto error;
514 
515         switch (elem_type) {
516           case 0:
517           {
518             gdouble d;
519 
520             if (!gst_byte_reader_get_float64_be (reader, &d))
521               goto error;
522 
523             GST_DEBUG_OBJECT (demux, "element is a double %f", d);
524 
525             if (!strcmp (tag_name, "times") && demux->times) {
526               g_array_append_val (demux->times, d);
527             } else if (!strcmp (tag_name, "filepositions") &&
528                 demux->filepositions) {
529               g_array_append_val (demux->filepositions, d);
530             }
531             break;
532           }
533           default:
534             GST_WARNING_OBJECT (demux, "unsupported array element type %d",
535                 elem_type);
536         }
537       }
538 
539       break;
540     }
541     case 11:                   /* Date */
542     {
543       gdouble d = 0;
544       gint16 i = 0;
545 
546       if (!gst_byte_reader_get_float64_be (reader, &d))
547         goto error;
548 
549       if (!gst_byte_reader_get_int16_be (reader, &i))
550         goto error;
551 
552       GST_DEBUG_OBJECT (demux,
553           "%s => (date as a double) %f, timezone offset %d", tag_name, d, i);
554 
555       GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name);
556 
557       break;
558     }
559     default:
560       GST_WARNING_OBJECT (demux, "unsupported tag type %d", tag_type);
561   }
562 
563   g_free (tag_name);
564 
565   return TRUE;
566 
567 error:
568   g_free (tag_name);
569 
570   return FALSE;
571 }
572 
573 static void
gst_flv_demux_clear_tags(GstFlvDemux * demux)574 gst_flv_demux_clear_tags (GstFlvDemux * demux)
575 {
576   GST_DEBUG_OBJECT (demux, "clearing taglist");
577 
578   if (demux->taglist) {
579     gst_tag_list_unref (demux->taglist);
580   }
581   demux->taglist = gst_tag_list_new_empty ();
582   gst_tag_list_set_scope (demux->taglist, GST_TAG_SCOPE_GLOBAL);
583 
584   if (demux->audio_tags) {
585     gst_tag_list_unref (demux->audio_tags);
586   }
587   demux->audio_tags = gst_tag_list_new_empty ();
588 
589   if (demux->video_tags) {
590     gst_tag_list_unref (demux->video_tags);
591   }
592   demux->video_tags = gst_tag_list_new_empty ();
593 }
594 
595 static GstFlowReturn
gst_flv_demux_parse_tag_script(GstFlvDemux * demux,GstBuffer * buffer)596 gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
597 {
598   GstFlowReturn ret = GST_FLOW_OK;
599   GstByteReader reader;
600   guint8 type = 0;
601   GstMapInfo map;
602 
603   g_return_val_if_fail (gst_buffer_get_size (buffer) >= 7, GST_FLOW_ERROR);
604 
605   gst_buffer_map (buffer, &map, GST_MAP_READ);
606   gst_byte_reader_init (&reader, map.data, map.size);
607 
608   gst_byte_reader_skip_unchecked (&reader, 7);
609 
610   GST_LOG_OBJECT (demux, "parsing a script tag");
611 
612   if (!gst_byte_reader_get_uint8 (&reader, &type))
613     goto cleanup;
614 
615   /* Must be string */
616   if (type == 2) {
617     gchar *function_name;
618     guint i;
619 
620     function_name = FLV_GET_STRING (&reader);
621 
622     GST_LOG_OBJECT (demux, "function name is %s", GST_STR_NULL (function_name));
623 
624     if (function_name != NULL && strcmp (function_name, "onMetaData") == 0) {
625       gboolean end_marker = FALSE;
626       GST_DEBUG_OBJECT (demux, "we have a metadata script object");
627 
628       gst_flv_demux_clear_tags (demux);
629 
630       if (!gst_byte_reader_get_uint8 (&reader, &type)) {
631         g_free (function_name);
632         goto cleanup;
633       }
634 
635       switch (type) {
636         case 8:
637         {
638           guint32 nb_elems = 0;
639 
640           /* ECMA array */
641           if (!gst_byte_reader_get_uint32_be (&reader, &nb_elems)) {
642             g_free (function_name);
643             goto cleanup;
644           }
645 
646           /* The number of elements is just a hint, some files have
647              nb_elements == 0 and actually contain items. */
648           GST_DEBUG_OBJECT (demux, "there are approx. %d elements in the array",
649               nb_elems);
650         }
651           /* fallthrough to read data */
652         case 3:
653         {
654           /* Object */
655           while (!end_marker) {
656             gboolean ok =
657                 gst_flv_demux_parse_metadata_item (demux, &reader, &end_marker);
658 
659             if (G_UNLIKELY (!ok)) {
660               GST_WARNING_OBJECT (demux, "failed reading a tag, skipping");
661               break;
662             }
663           }
664         }
665           break;
666         default:
667           GST_DEBUG_OBJECT (demux, "Unhandled script data type : %d", type);
668           g_free (function_name);
669           goto cleanup;
670       }
671 
672       gst_flv_demux_push_tags (demux);
673     }
674 
675     g_free (function_name);
676 
677     if (demux->times && demux->filepositions) {
678       guint num;
679 
680       /* If an index was found, insert associations */
681       num = MIN (demux->times->len, demux->filepositions->len);
682       for (i = 0; i < num; i++) {
683         guint64 time, fileposition;
684 
685         time = g_array_index (demux->times, gdouble, i) * GST_SECOND;
686         fileposition = g_array_index (demux->filepositions, gdouble, i);
687         gst_flv_demux_parse_and_add_index_entry (demux, time, fileposition,
688             TRUE);
689       }
690       demux->indexed = TRUE;
691     }
692   }
693 
694 cleanup:
695   gst_buffer_unmap (buffer, &map);
696 
697   return ret;
698 }
699 
700 static gboolean
have_group_id(GstFlvDemux * demux)701 have_group_id (GstFlvDemux * demux)
702 {
703   GstEvent *event;
704 
705   event = gst_pad_get_sticky_event (demux->sinkpad, GST_EVENT_STREAM_START, 0);
706   if (event) {
707     if (gst_event_parse_group_id (event, &demux->group_id))
708       demux->have_group_id = TRUE;
709     else
710       demux->have_group_id = FALSE;
711     gst_event_unref (event);
712   } else if (!demux->have_group_id) {
713     demux->have_group_id = TRUE;
714     demux->group_id = gst_util_group_id_next ();
715   }
716 
717   return demux->have_group_id;
718 }
719 
720 static gboolean
gst_flv_demux_audio_negotiate(GstFlvDemux * demux,guint32 codec_tag,guint32 rate,guint32 channels,guint32 width)721 gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag,
722     guint32 rate, guint32 channels, guint32 width)
723 {
724   GstCaps *caps = NULL, *old_caps;
725   gboolean ret = FALSE;
726   guint adjusted_rate = rate;
727   guint adjusted_channels = channels;
728   GstEvent *event;
729   gchar *stream_id;
730 
731   switch (codec_tag) {
732     case 1:
733       caps = gst_caps_new_simple ("audio/x-adpcm", "layout", G_TYPE_STRING,
734           "swf", NULL);
735       break;
736     case 2:
737     case 14:
738       caps = gst_caps_new_simple ("audio/mpeg",
739           "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3,
740           "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
741       break;
742     case 0:
743     case 3:
744     {
745       GstAudioFormat format;
746 
747       /* Assuming little endian for 0 (aka endianness of the
748        * system on which the file was created) as most people
749        * are probably using little endian machines */
750       format = gst_audio_format_build_integer ((width == 8) ? FALSE : TRUE,
751           G_LITTLE_ENDIAN, width, width);
752 
753       caps = gst_caps_new_simple ("audio/x-raw",
754           "format", G_TYPE_STRING, gst_audio_format_to_string (format),
755           "layout", G_TYPE_STRING, "interleaved", NULL);
756       break;
757     }
758     case 4:
759     case 5:
760     case 6:
761       caps = gst_caps_new_empty_simple ("audio/x-nellymoser");
762       break;
763     case 10:
764     {
765       GstMapInfo map;
766       if (!demux->audio_codec_data) {
767         GST_DEBUG_OBJECT (demux, "don't have AAC codec data yet");
768         ret = TRUE;
769         goto done;
770       }
771 
772       gst_buffer_map (demux->audio_codec_data, &map, GST_MAP_READ);
773 
774       /* use codec-data to extract and verify samplerate */
775       if (map.size >= 2) {
776         gint freq_index;
777 
778         freq_index = GST_READ_UINT16_BE (map.data);
779         freq_index = (freq_index & 0x0780) >> 7;
780         adjusted_rate =
781             gst_codec_utils_aac_get_sample_rate_from_index (freq_index);
782 
783         if (adjusted_rate && (rate != adjusted_rate)) {
784           GST_LOG_OBJECT (demux, "Ajusting AAC sample rate %d -> %d", rate,
785               adjusted_rate);
786         } else {
787           adjusted_rate = rate;
788         }
789 
790         adjusted_channels =
791             gst_codec_utils_aac_get_channels (map.data, map.size);
792 
793         if (adjusted_channels && (channels != adjusted_channels)) {
794           GST_LOG_OBJECT (demux, "Ajusting AAC channels %d -> %d", channels,
795               adjusted_channels);
796         } else {
797           adjusted_channels = channels;
798         }
799       }
800       gst_buffer_unmap (demux->audio_codec_data, &map);
801 
802       caps = gst_caps_new_simple ("audio/mpeg",
803           "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
804           "stream-format", G_TYPE_STRING, "raw", NULL);
805       break;
806     }
807     case 7:
808       caps = gst_caps_new_empty_simple ("audio/x-alaw");
809       break;
810     case 8:
811       caps = gst_caps_new_empty_simple ("audio/x-mulaw");
812       break;
813     case 11:
814     {
815       GValue streamheader = G_VALUE_INIT;
816       GValue value = G_VALUE_INIT;
817       GstByteWriter w;
818       GstStructure *structure;
819       GstBuffer *buf;
820       GstTagList *tags;
821 
822       caps = gst_caps_new_empty_simple ("audio/x-speex");
823       structure = gst_caps_get_structure (caps, 0);
824 
825       GST_DEBUG_OBJECT (demux, "generating speex header");
826 
827       /* Speex decoder expects streamheader to be { [header], [comment] } */
828       g_value_init (&streamheader, GST_TYPE_ARRAY);
829 
830       /* header part */
831       gst_byte_writer_init_with_size (&w, 80, TRUE);
832       gst_byte_writer_put_data (&w, (guint8 *) "Speex   ", 8);
833       gst_byte_writer_put_data (&w, (guint8 *) "1.1.12", 7);
834       gst_byte_writer_fill (&w, 0, 13);
835       gst_byte_writer_put_uint32_le (&w, 1);    /* version */
836       gst_byte_writer_put_uint32_le (&w, 80);   /* header_size */
837       gst_byte_writer_put_uint32_le (&w, 16000);        /* rate */
838       gst_byte_writer_put_uint32_le (&w, 1);    /* mode: Wideband */
839       gst_byte_writer_put_uint32_le (&w, 4);    /* mode_bitstream_version */
840       gst_byte_writer_put_uint32_le (&w, 1);    /* nb_channels: 1 */
841       gst_byte_writer_put_uint32_le (&w, -1);   /* bitrate */
842       gst_byte_writer_put_uint32_le (&w, 0x50); /* frame_size */
843       gst_byte_writer_put_uint32_le (&w, 0);    /* VBR */
844       gst_byte_writer_put_uint32_le (&w, 1);    /* frames_per_packet */
845       gst_byte_writer_put_uint32_le (&w, 0);    /* extra_headers */
846       gst_byte_writer_put_uint32_le (&w, 0);    /* reserved1 */
847       gst_byte_writer_put_uint32_le (&w, 0);    /* reserved2 */
848       g_assert (gst_byte_writer_get_size (&w) == 80);
849 
850       g_value_init (&value, GST_TYPE_BUFFER);
851       g_value_take_boxed (&value, gst_byte_writer_reset_and_get_buffer (&w));
852       gst_value_array_append_value (&streamheader, &value);
853       g_value_unset (&value);
854 
855       /* comment part */
856       g_value_init (&value, GST_TYPE_BUFFER);
857       tags = gst_tag_list_new_empty ();
858       buf = gst_tag_list_to_vorbiscomment_buffer (tags, NULL, 0, "No comments");
859       gst_tag_list_unref (tags);
860       g_value_take_boxed (&value, buf);
861       gst_value_array_append_value (&streamheader, &value);
862       g_value_unset (&value);
863 
864       gst_structure_take_value (structure, "streamheader", &streamheader);
865 
866       channels = 1;
867       adjusted_rate = 16000;
868       break;
869     }
870     default:
871       GST_WARNING_OBJECT (demux, "unsupported audio codec tag %u", codec_tag);
872       break;
873   }
874 
875   if (G_UNLIKELY (!caps)) {
876     GST_WARNING_OBJECT (demux, "failed creating caps for audio pad");
877     goto beach;
878   }
879 
880   gst_caps_set_simple (caps, "rate", G_TYPE_INT, adjusted_rate,
881       "channels", G_TYPE_INT, adjusted_channels, NULL);
882 
883   if (demux->audio_codec_data) {
884     gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER,
885         demux->audio_codec_data, NULL);
886   }
887 
888   old_caps = gst_pad_get_current_caps (demux->audio_pad);
889   if (!old_caps) {
890     stream_id =
891         gst_pad_create_stream_id (demux->audio_pad, GST_ELEMENT_CAST (demux),
892         "audio");
893 
894     event = gst_event_new_stream_start (stream_id);
895     if (have_group_id (demux))
896       gst_event_set_group_id (event, demux->group_id);
897     gst_pad_push_event (demux->audio_pad, event);
898     g_free (stream_id);
899   }
900   if (!old_caps || !gst_caps_is_equal (old_caps, caps))
901     ret = gst_pad_set_caps (demux->audio_pad, caps);
902   else
903     ret = TRUE;
904 
905   if (old_caps)
906     gst_caps_unref (old_caps);
907 
908 done:
909   if (G_LIKELY (ret)) {
910     /* Store the caps we got from tags */
911     demux->audio_codec_tag = codec_tag;
912     demux->rate = rate;
913     demux->channels = channels;
914     demux->width = width;
915 
916     if (caps) {
917       GST_DEBUG_OBJECT (demux->audio_pad, "successfully negotiated caps %"
918           GST_PTR_FORMAT, caps);
919 
920       gst_flv_demux_push_tags (demux);
921     } else {
922       GST_DEBUG_OBJECT (demux->audio_pad, "delayed setting caps");
923     }
924   } else {
925     GST_WARNING_OBJECT (demux->audio_pad, "failed negotiating caps %"
926         GST_PTR_FORMAT, caps);
927   }
928 
929   if (caps)
930     gst_caps_unref (caps);
931 
932 beach:
933   return ret;
934 }
935 
936 static gboolean
gst_flv_demux_push_src_event(GstFlvDemux * demux,GstEvent * event)937 gst_flv_demux_push_src_event (GstFlvDemux * demux, GstEvent * event)
938 {
939   gboolean ret = TRUE;
940 
941   if (demux->audio_pad)
942     ret |= gst_pad_push_event (demux->audio_pad, gst_event_ref (event));
943 
944   if (demux->video_pad)
945     ret |= gst_pad_push_event (demux->video_pad, gst_event_ref (event));
946 
947   gst_event_unref (event);
948 
949   return ret;
950 }
951 
952 static void
gst_flv_demux_add_codec_tag(GstFlvDemux * demux,const gchar * tag,GstPad * pad)953 gst_flv_demux_add_codec_tag (GstFlvDemux * demux, const gchar * tag,
954     GstPad * pad)
955 {
956   if (pad) {
957     GstCaps *caps = gst_pad_get_current_caps (pad);
958 
959     if (caps) {
960       gchar *codec_name = gst_pb_utils_get_codec_description (caps);
961 
962       if (codec_name) {
963         gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
964             tag, codec_name, NULL);
965         g_free (codec_name);
966       }
967 
968       gst_caps_unref (caps);
969     }
970   }
971 }
972 
973 static void
gst_flv_demux_push_tags(GstFlvDemux * demux)974 gst_flv_demux_push_tags (GstFlvDemux * demux)
975 {
976   gst_flv_demux_add_codec_tag (demux, GST_TAG_AUDIO_CODEC, demux->audio_pad);
977   gst_flv_demux_add_codec_tag (demux, GST_TAG_VIDEO_CODEC, demux->video_pad);
978 
979   GST_DEBUG_OBJECT (demux, "pushing %" GST_PTR_FORMAT, demux->taglist);
980 
981   gst_flv_demux_push_src_event (demux,
982       gst_event_new_tag (gst_tag_list_copy (demux->taglist)));
983 
984   if (demux->audio_pad) {
985     GST_DEBUG_OBJECT (demux->audio_pad, "pushing audio %" GST_PTR_FORMAT,
986         demux->audio_tags);
987     gst_pad_push_event (demux->audio_pad,
988         gst_event_new_tag (gst_tag_list_copy (demux->audio_tags)));
989   }
990 
991   if (demux->video_pad) {
992     GST_DEBUG_OBJECT (demux->video_pad, "pushing video %" GST_PTR_FORMAT,
993         demux->video_tags);
994     gst_pad_push_event (demux->video_pad,
995         gst_event_new_tag (gst_tag_list_copy (demux->video_tags)));
996   }
997 }
998 
999 static gboolean
gst_flv_demux_update_resync(GstFlvDemux * demux,guint32 dts,gboolean discont,guint32 * last,GstClockTime * offset)1000 gst_flv_demux_update_resync (GstFlvDemux * demux, guint32 dts, gboolean discont,
1001     guint32 * last, GstClockTime * offset)
1002 {
1003   gboolean ret = FALSE;
1004   gint32 ddts = dts - *last;
1005   if (!discont && ddts <= -RESYNC_THRESHOLD) {
1006     /* Theoretically, we should use substract the duration of the last buffer,
1007        but this demuxer sends no durations on buffers, not sure if it cannot
1008        know, or just does not care to calculate. */
1009     *offset -= ddts * GST_MSECOND;
1010     GST_WARNING_OBJECT (demux,
1011         "Large dts gap (%" G_GINT32_FORMAT " ms), assuming resync, offset now %"
1012         GST_TIME_FORMAT "", ddts, GST_TIME_ARGS (*offset));
1013 
1014     ret = TRUE;
1015   }
1016   *last = dts;
1017 
1018   return ret;
1019 }
1020 
1021 static GstFlowReturn
gst_flv_demux_parse_tag_audio(GstFlvDemux * demux,GstBuffer * buffer)1022 gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
1023 {
1024   GstFlowReturn ret = GST_FLOW_OK;
1025   guint32 pts = 0, codec_tag = 0, rate = 5512, width = 8, channels = 1;
1026   guint32 codec_data = 0, pts_ext = 0;
1027   guint8 flags = 0;
1028   GstMapInfo map;
1029   GstBuffer *outbuf;
1030   guint8 *data;
1031 
1032   GST_LOG_OBJECT (demux, "parsing an audio tag");
1033 
1034   if (G_UNLIKELY (!demux->audio_pad && demux->no_more_pads)) {
1035 #ifndef GST_DISABLE_DEBUG
1036     if (G_UNLIKELY (!demux->no_audio_warned)) {
1037       GST_WARNING_OBJECT (demux,
1038           "Signaled no-more-pads already but had no audio pad -- ignoring");
1039       demux->no_audio_warned = TRUE;
1040     }
1041 #endif
1042     return GST_FLOW_OK;
1043   }
1044 
1045   g_return_val_if_fail (gst_buffer_get_size (buffer) == demux->tag_size,
1046       GST_FLOW_ERROR);
1047 
1048   /* Error out on tags with too small headers */
1049   if (gst_buffer_get_size (buffer) < 11) {
1050     GST_ERROR_OBJECT (demux, "Too small tag size (%" G_GSIZE_FORMAT ")",
1051         gst_buffer_get_size (buffer));
1052     return GST_FLOW_ERROR;
1053   }
1054 
1055   gst_buffer_map (buffer, &map, GST_MAP_READ);
1056   data = map.data;
1057 
1058   /* Grab information about audio tag */
1059   pts = GST_READ_UINT24_BE (data);
1060   /* read the pts extension to 32 bits integer */
1061   pts_ext = GST_READ_UINT8 (data + 3);
1062   /* Combine them */
1063   pts |= pts_ext << 24;
1064 
1065   GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X (%d)", data[0], data[1],
1066       data[2], data[3], pts);
1067 
1068   /* Skip the stream id and go directly to the flags */
1069   flags = GST_READ_UINT8 (data + 7);
1070 
1071   /* Silently skip buffers with no data */
1072   if (map.size == 11)
1073     goto beach;
1074 
1075   /* Channels */
1076   if (flags & 0x01) {
1077     channels = 2;
1078   }
1079   /* Width */
1080   if (flags & 0x02) {
1081     width = 16;
1082   }
1083   /* Sampling rate */
1084   if ((flags & 0x0C) == 0x0C) {
1085     rate = 44100;
1086   } else if ((flags & 0x0C) == 0x08) {
1087     rate = 22050;
1088   } else if ((flags & 0x0C) == 0x04) {
1089     rate = 11025;
1090   }
1091   /* Codec tag */
1092   codec_tag = flags >> 4;
1093   if (codec_tag == 10) {        /* AAC has an extra byte for packet type */
1094     codec_data = 2;
1095   } else {
1096     codec_data = 1;
1097   }
1098 
1099   /* codec tags with special rates */
1100   if (codec_tag == 5 || codec_tag == 14 || codec_tag == 7 || codec_tag == 8)
1101     rate = 8000;
1102   else if ((codec_tag == 4) || (codec_tag == 11))
1103     rate = 16000;
1104 
1105   GST_LOG_OBJECT (demux, "audio tag with %d channels, %dHz sampling rate, "
1106       "%d bits width, codec tag %u (flags %02X)", channels, rate, width,
1107       codec_tag, flags);
1108 
1109   if (codec_tag == 10) {
1110     guint8 aac_packet_type = GST_READ_UINT8 (data + 8);
1111 
1112     switch (aac_packet_type) {
1113       case 0:
1114       {
1115         /* AudioSpecificConfig data */
1116         GST_LOG_OBJECT (demux, "got an AAC codec data packet");
1117         if (demux->audio_codec_data) {
1118           gst_buffer_unref (demux->audio_codec_data);
1119         }
1120         demux->audio_codec_data =
1121             gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY,
1122             7 + codec_data, demux->tag_data_size - codec_data);
1123 
1124         /* Use that buffer data in the caps */
1125         if (demux->audio_pad)
1126           gst_flv_demux_audio_negotiate (demux, codec_tag, rate, channels,
1127               width);
1128         goto beach;
1129       }
1130       case 1:
1131         if (!demux->audio_codec_data) {
1132           GST_ERROR_OBJECT (demux, "got AAC audio packet before codec data");
1133           ret = GST_FLOW_OK;
1134           goto beach;
1135         }
1136         /* AAC raw packet */
1137         GST_LOG_OBJECT (demux, "got a raw AAC audio packet");
1138         break;
1139       default:
1140         GST_WARNING_OBJECT (demux, "invalid AAC packet type %u",
1141             aac_packet_type);
1142     }
1143   }
1144 
1145   /* If we don't have our audio pad created, then create it. */
1146   if (G_UNLIKELY (!demux->audio_pad)) {
1147     demux->audio_pad =
1148         gst_pad_new_from_template (gst_element_class_get_pad_template
1149         (GST_ELEMENT_GET_CLASS (demux), "audio"), "audio");
1150     if (G_UNLIKELY (!demux->audio_pad)) {
1151       GST_WARNING_OBJECT (demux, "failed creating audio pad");
1152       ret = GST_FLOW_ERROR;
1153       goto beach;
1154     }
1155 
1156     /* Set functions on the pad */
1157     gst_pad_set_query_function (demux->audio_pad,
1158         GST_DEBUG_FUNCPTR (gst_flv_demux_query));
1159     gst_pad_set_event_function (demux->audio_pad,
1160         GST_DEBUG_FUNCPTR (gst_flv_demux_src_event));
1161 
1162     gst_pad_use_fixed_caps (demux->audio_pad);
1163 
1164     /* Make it active */
1165     gst_pad_set_active (demux->audio_pad, TRUE);
1166 
1167     /* Negotiate caps */
1168     if (!gst_flv_demux_audio_negotiate (demux, codec_tag, rate, channels,
1169             width)) {
1170       gst_object_unref (demux->audio_pad);
1171       demux->audio_pad = NULL;
1172       ret = GST_FLOW_ERROR;
1173       goto beach;
1174     }
1175 #ifndef GST_DISABLE_GST_DEBUG
1176     {
1177       GstCaps *caps;
1178 
1179       caps = gst_pad_get_current_caps (demux->audio_pad);
1180       GST_DEBUG_OBJECT (demux, "created audio pad with caps %" GST_PTR_FORMAT,
1181           caps);
1182       if (caps)
1183         gst_caps_unref (caps);
1184     }
1185 #endif
1186 
1187     /* We need to set caps before adding */
1188     gst_element_add_pad (GST_ELEMENT (demux),
1189         gst_object_ref (demux->audio_pad));
1190     gst_flow_combiner_add_pad (demux->flowcombiner, demux->audio_pad);
1191 
1192     /* We only emit no more pads when we have audio and video. Indeed we can
1193      * not trust the FLV header to tell us if there will be only audio or
1194      * only video and we would just break discovery of some files */
1195     if (demux->audio_pad && demux->video_pad) {
1196       GST_DEBUG_OBJECT (demux, "emitting no more pads");
1197       gst_element_no_more_pads (GST_ELEMENT (demux));
1198       demux->no_more_pads = TRUE;
1199     }
1200   }
1201 
1202   /* Check if caps have changed */
1203   if (G_UNLIKELY (rate != demux->rate || channels != demux->channels ||
1204           codec_tag != demux->audio_codec_tag || width != demux->width)) {
1205     GST_DEBUG_OBJECT (demux, "audio settings have changed, changing caps");
1206 
1207     gst_buffer_replace (&demux->audio_codec_data, NULL);
1208 
1209     /* Negotiate caps */
1210     if (!gst_flv_demux_audio_negotiate (demux, codec_tag, rate, channels,
1211             width)) {
1212       ret = GST_FLOW_ERROR;
1213       goto beach;
1214     }
1215   }
1216 
1217   /* Check if we have anything to push */
1218   if (demux->tag_data_size <= codec_data) {
1219     GST_LOG_OBJECT (demux, "Nothing left in this tag, returning");
1220     goto beach;
1221   }
1222 
1223   /* Create buffer from pad */
1224   outbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY,
1225       7 + codec_data, demux->tag_data_size - codec_data);
1226 
1227   /* detect (and deem to be resyncs)  large pts gaps */
1228   if (gst_flv_demux_update_resync (demux, pts, demux->audio_need_discont,
1229           &demux->last_audio_pts, &demux->audio_time_offset)) {
1230     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC);
1231   }
1232 
1233   /* Fill buffer with data */
1234   GST_BUFFER_PTS (outbuf) = pts * GST_MSECOND + demux->audio_time_offset;
1235   GST_BUFFER_DTS (outbuf) = GST_BUFFER_PTS (outbuf);
1236   GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
1237   GST_BUFFER_OFFSET (outbuf) = demux->audio_offset++;
1238   GST_BUFFER_OFFSET_END (outbuf) = demux->audio_offset;
1239 
1240   if (demux->duration == GST_CLOCK_TIME_NONE ||
1241       demux->duration < GST_BUFFER_TIMESTAMP (outbuf))
1242     demux->duration = GST_BUFFER_TIMESTAMP (outbuf);
1243 
1244   /* Only add audio frames to the index if we have no video,
1245    * and if the index is not yet complete */
1246   if (!demux->has_video && !demux->indexed) {
1247     gst_flv_demux_parse_and_add_index_entry (demux,
1248         GST_BUFFER_TIMESTAMP (outbuf), demux->cur_tag_offset, TRUE);
1249   }
1250 
1251   if (G_UNLIKELY (demux->audio_need_discont)) {
1252     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
1253     demux->audio_need_discont = FALSE;
1254   }
1255 
1256   demux->segment.position = GST_BUFFER_TIMESTAMP (outbuf);
1257 
1258   /* Do we need a newsegment event ? */
1259   if (G_UNLIKELY (demux->audio_need_segment)) {
1260     if (!demux->new_seg_event) {
1261       GST_DEBUG_OBJECT (demux, "pushing newsegment from %"
1262           GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
1263           GST_TIME_ARGS (demux->segment.position),
1264           GST_TIME_ARGS (demux->segment.stop));
1265       demux->segment.start = demux->segment.time = demux->segment.position;
1266       demux->new_seg_event = gst_event_new_segment (&demux->segment);
1267     } else {
1268       GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event");
1269     }
1270 
1271     gst_pad_push_event (demux->audio_pad, gst_event_ref (demux->new_seg_event));
1272 
1273     demux->audio_need_segment = FALSE;
1274   }
1275 
1276   GST_LOG_OBJECT (demux,
1277       "pushing %" G_GSIZE_FORMAT " bytes buffer at pts %" GST_TIME_FORMAT
1278       " with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT,
1279       gst_buffer_get_size (outbuf),
1280       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
1281       GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf));
1282 
1283   if (!GST_CLOCK_TIME_IS_VALID (demux->audio_start)) {
1284     demux->audio_start = GST_BUFFER_TIMESTAMP (outbuf);
1285   }
1286   if (!GST_CLOCK_TIME_IS_VALID (demux->audio_first_ts)) {
1287     demux->audio_first_ts = GST_BUFFER_TIMESTAMP (outbuf);
1288   }
1289 
1290   if (G_UNLIKELY (!demux->no_more_pads
1291           && (GST_CLOCK_DIFF (demux->audio_start,
1292                   GST_BUFFER_TIMESTAMP (outbuf)) > NO_MORE_PADS_THRESHOLD))) {
1293     GST_DEBUG_OBJECT (demux,
1294         "Signalling no-more-pads because no video stream was found"
1295         " after 6 seconds of audio");
1296     gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
1297     demux->no_more_pads = TRUE;
1298   }
1299 
1300   /* Push downstream */
1301   ret = gst_pad_push (demux->audio_pad, outbuf);
1302 
1303   if (G_UNLIKELY (ret != GST_FLOW_OK) &&
1304       demux->segment.rate < 0.0 && ret == GST_FLOW_EOS &&
1305       demux->segment.position > demux->segment.stop) {
1306     /* In reverse playback we can get a GST_FLOW_EOS when
1307      * we are at the end of the segment, so we just need to jump
1308      * back to the previous section. */
1309     GST_DEBUG_OBJECT (demux, "downstream has reached end of segment");
1310     demux->audio_done = TRUE;
1311     ret = GST_FLOW_OK;
1312     goto beach;
1313   }
1314 
1315   ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner,
1316       demux->audio_pad, ret);
1317 
1318 beach:
1319   gst_buffer_unmap (buffer, &map);
1320 
1321   return ret;
1322 }
1323 
1324 static gboolean
gst_flv_demux_video_negotiate(GstFlvDemux * demux,guint32 codec_tag)1325 gst_flv_demux_video_negotiate (GstFlvDemux * demux, guint32 codec_tag)
1326 {
1327   gboolean ret = FALSE;
1328   GstCaps *caps = NULL, *old_caps;
1329   GstEvent *event;
1330   gchar *stream_id;
1331 
1332   /* Generate caps for that pad */
1333   switch (codec_tag) {
1334     case 2:
1335       caps =
1336           gst_caps_new_simple ("video/x-flash-video", "flvversion", G_TYPE_INT,
1337           1, NULL);
1338       break;
1339     case 3:
1340       caps = gst_caps_new_empty_simple ("video/x-flash-screen");
1341       break;
1342     case 4:
1343       caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
1344       break;
1345     case 5:
1346       caps = gst_caps_new_empty_simple ("video/x-vp6-alpha");
1347       break;
1348     case 7:
1349       if (!demux->video_codec_data) {
1350         GST_DEBUG_OBJECT (demux, "don't have h264 codec data yet");
1351         ret = TRUE;
1352         goto done;
1353       }
1354       caps =
1355           gst_caps_new_simple ("video/x-h264", "stream-format", G_TYPE_STRING,
1356           "avc", NULL);
1357       break;
1358       /* The following two are non-standard but apparently used, see in ffmpeg
1359        * https://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavformat/flvdec.c;h=2bf1e059e1cbeeb79e4af9542da23f4560e1cf59;hb=b18d6c58000beed872d6bb1fe7d0fbe75ae26aef#l254
1360        * https://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavformat/flvdec.c;h=2bf1e059e1cbeeb79e4af9542da23f4560e1cf59;hb=b18d6c58000beed872d6bb1fe7d0fbe75ae26aef#l282
1361        */
1362     case 8:
1363       caps = gst_caps_new_empty_simple ("video/x-h263");
1364       break;
1365     case 9:
1366       caps =
1367           gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
1368           "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
1369       break;
1370     default:
1371       GST_WARNING_OBJECT (demux, "unsupported video codec tag %u", codec_tag);
1372   }
1373 
1374   if (G_UNLIKELY (!caps)) {
1375     GST_WARNING_OBJECT (demux, "failed creating caps for video pad");
1376     goto beach;
1377   }
1378 
1379   if (demux->got_par) {
1380     gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1381         demux->par_x, demux->par_y, NULL);
1382   }
1383 
1384   if (G_LIKELY (demux->w)) {
1385     gst_caps_set_simple (caps, "width", G_TYPE_INT, demux->w, NULL);
1386   }
1387 
1388   if (G_LIKELY (demux->h)) {
1389     gst_caps_set_simple (caps, "height", G_TYPE_INT, demux->h, NULL);
1390   }
1391 
1392   if (G_LIKELY (demux->framerate)) {
1393     gint num = 0, den = 0;
1394 
1395     gst_video_guess_framerate (GST_SECOND / demux->framerate, &num, &den);
1396     GST_DEBUG_OBJECT (demux->video_pad,
1397         "fps to be used on caps %f (as a fraction = %d/%d)", demux->framerate,
1398         num, den);
1399 
1400     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, num, den, NULL);
1401   }
1402 
1403   if (demux->video_codec_data) {
1404     gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER,
1405         demux->video_codec_data, NULL);
1406   }
1407 
1408   old_caps = gst_pad_get_current_caps (demux->video_pad);
1409   if (!old_caps) {
1410     stream_id =
1411         gst_pad_create_stream_id (demux->video_pad, GST_ELEMENT_CAST (demux),
1412         "video");
1413     event = gst_event_new_stream_start (stream_id);
1414     g_free (stream_id);
1415 
1416     if (have_group_id (demux))
1417       gst_event_set_group_id (event, demux->group_id);
1418     gst_pad_push_event (demux->video_pad, event);
1419   }
1420 
1421   if (!old_caps || !gst_caps_is_equal (old_caps, caps))
1422     ret = gst_pad_set_caps (demux->video_pad, caps);
1423   else
1424     ret = TRUE;
1425 
1426   if (old_caps)
1427     gst_caps_unref (old_caps);
1428 
1429 done:
1430   if (G_LIKELY (ret)) {
1431     /* Store the caps we have set */
1432     demux->video_codec_tag = codec_tag;
1433 
1434     if (caps) {
1435       GST_DEBUG_OBJECT (demux->video_pad, "successfully negotiated caps %"
1436           GST_PTR_FORMAT, caps);
1437 
1438       gst_flv_demux_push_tags (demux);
1439     } else {
1440       GST_DEBUG_OBJECT (demux->video_pad, "delayed setting caps");
1441     }
1442   } else {
1443     GST_WARNING_OBJECT (demux->video_pad, "failed negotiating caps %"
1444         GST_PTR_FORMAT, caps);
1445   }
1446 
1447   if (caps)
1448     gst_caps_unref (caps);
1449 
1450 beach:
1451   return ret;
1452 }
1453 
1454 static GstFlowReturn
gst_flv_demux_parse_tag_video(GstFlvDemux * demux,GstBuffer * buffer)1455 gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
1456 {
1457   GstFlowReturn ret = GST_FLOW_OK;
1458   guint32 dts = 0, codec_data = 1, dts_ext = 0;
1459   gint32 cts = 0;
1460   gboolean keyframe = FALSE;
1461   guint8 flags = 0, codec_tag = 0;
1462   GstBuffer *outbuf;
1463   GstMapInfo map;
1464   guint8 *data;
1465 
1466   g_return_val_if_fail (gst_buffer_get_size (buffer) == demux->tag_size,
1467       GST_FLOW_ERROR);
1468 
1469   GST_LOG_OBJECT (demux, "parsing a video tag");
1470 
1471   if G_UNLIKELY
1472     (!demux->video_pad && demux->no_more_pads) {
1473 #ifndef GST_DISABLE_DEBUG
1474     if G_UNLIKELY
1475       (!demux->no_video_warned) {
1476       GST_WARNING_OBJECT (demux,
1477           "Signaled no-more-pads already but had no video pad -- ignoring");
1478       demux->no_video_warned = TRUE;
1479       }
1480 #endif
1481     return GST_FLOW_OK;
1482     }
1483 
1484   if (gst_buffer_get_size (buffer) < 12) {
1485     GST_ERROR_OBJECT (demux, "Too small tag size");
1486     return GST_FLOW_ERROR;
1487   }
1488 
1489   gst_buffer_map (buffer, &map, GST_MAP_READ);
1490   data = map.data;
1491 
1492   /* Grab information about video tag */
1493   dts = GST_READ_UINT24_BE (data);
1494   /* read the dts extension to 32 bits integer */
1495   dts_ext = GST_READ_UINT8 (data + 3);
1496   /* Combine them */
1497   dts |= dts_ext << 24;
1498 
1499   GST_LOG_OBJECT (demux, "dts bytes %02X %02X %02X %02X (%d)", data[0], data[1],
1500       data[2], data[3], dts);
1501 
1502   /* Skip the stream id and go directly to the flags */
1503   flags = GST_READ_UINT8 (data + 7);
1504 
1505   /* Keyframe */
1506   if ((flags >> 4) == 1) {
1507     keyframe = TRUE;
1508   }
1509   /* Codec tag */
1510   codec_tag = flags & 0x0F;
1511   if (codec_tag == 4 || codec_tag == 5) {
1512     codec_data = 2;
1513   } else if (codec_tag == 7) {
1514     codec_data = 5;
1515 
1516     cts = GST_READ_UINT24_BE (data + 9);
1517     cts = (cts + 0xff800000) ^ 0xff800000;
1518 
1519     if (cts < 0 && ABS (cts) > dts) {
1520       GST_ERROR_OBJECT (demux, "Detected a negative composition time offset "
1521           "'%d' that would lead to negative PTS, fixing", cts);
1522       cts += ABS (cts) - dts;
1523     }
1524 
1525     GST_LOG_OBJECT (demux, "got cts %d", cts);
1526   }
1527 
1528   GST_LOG_OBJECT (demux, "video tag with codec tag %u, keyframe (%d) "
1529       "(flags %02X)", codec_tag, keyframe, flags);
1530 
1531   if (codec_tag == 7) {
1532     guint8 avc_packet_type = GST_READ_UINT8 (data + 8);
1533 
1534     switch (avc_packet_type) {
1535       case 0:
1536       {
1537         if (demux->tag_data_size < codec_data) {
1538           GST_ERROR_OBJECT (demux, "Got invalid H.264 codec, ignoring.");
1539           break;
1540         }
1541 
1542         /* AVCDecoderConfigurationRecord data */
1543         GST_LOG_OBJECT (demux, "got an H.264 codec data packet");
1544         if (demux->video_codec_data) {
1545           gst_buffer_unref (demux->video_codec_data);
1546         }
1547         demux->video_codec_data = gst_buffer_copy_region (buffer,
1548             GST_BUFFER_COPY_MEMORY, 7 + codec_data,
1549             demux->tag_data_size - codec_data);;
1550         /* Use that buffer data in the caps */
1551         if (demux->video_pad)
1552           gst_flv_demux_video_negotiate (demux, codec_tag);
1553         goto beach;
1554       }
1555       case 1:
1556         /* H.264 NALU packet */
1557         if (!demux->video_codec_data) {
1558           GST_ERROR_OBJECT (demux, "got H.264 video packet before codec data");
1559           ret = GST_FLOW_OK;
1560           goto beach;
1561         }
1562         GST_LOG_OBJECT (demux, "got a H.264 NALU video packet");
1563         break;
1564       default:
1565         GST_WARNING_OBJECT (demux, "invalid video packet type %u",
1566             avc_packet_type);
1567     }
1568   }
1569 
1570   /* If we don't have our video pad created, then create it. */
1571   if (G_UNLIKELY (!demux->video_pad)) {
1572     demux->video_pad =
1573         gst_pad_new_from_template (gst_element_class_get_pad_template
1574         (GST_ELEMENT_GET_CLASS (demux), "video"), "video");
1575     if (G_UNLIKELY (!demux->video_pad)) {
1576       GST_WARNING_OBJECT (demux, "failed creating video pad");
1577       ret = GST_FLOW_ERROR;
1578       goto beach;
1579     }
1580 
1581     /* Set functions on the pad */
1582     gst_pad_set_query_function (demux->video_pad,
1583         GST_DEBUG_FUNCPTR (gst_flv_demux_query));
1584     gst_pad_set_event_function (demux->video_pad,
1585         GST_DEBUG_FUNCPTR (gst_flv_demux_src_event));
1586 
1587     gst_pad_use_fixed_caps (demux->video_pad);
1588 
1589     /* Make it active */
1590     gst_pad_set_active (demux->video_pad, TRUE);
1591 
1592     /* Needs to be active before setting caps */
1593     if (!gst_flv_demux_video_negotiate (demux, codec_tag)) {
1594       gst_object_unref (demux->video_pad);
1595       demux->video_pad = NULL;
1596       ret = GST_FLOW_ERROR;
1597       goto beach;
1598     }
1599 
1600     /* When we ve set pixel-aspect-ratio we use that boolean to detect a
1601      * metadata tag that would come later and trigger a caps change */
1602     demux->got_par = FALSE;
1603 
1604 #ifndef GST_DISABLE_GST_DEBUG
1605     {
1606       GstCaps *caps;
1607 
1608       caps = gst_pad_get_current_caps (demux->video_pad);
1609       GST_DEBUG_OBJECT (demux, "created video pad with caps %" GST_PTR_FORMAT,
1610           caps);
1611       if (caps)
1612         gst_caps_unref (caps);
1613     }
1614 #endif
1615 
1616     /* We need to set caps before adding */
1617     gst_element_add_pad (GST_ELEMENT (demux),
1618         gst_object_ref (demux->video_pad));
1619     gst_flow_combiner_add_pad (demux->flowcombiner, demux->video_pad);
1620 
1621     /* We only emit no more pads when we have audio and video. Indeed we can
1622      * not trust the FLV header to tell us if there will be only audio or
1623      * only video and we would just break discovery of some files */
1624     if (demux->audio_pad && demux->video_pad) {
1625       GST_DEBUG_OBJECT (demux, "emitting no more pads");
1626       gst_element_no_more_pads (GST_ELEMENT (demux));
1627       demux->no_more_pads = TRUE;
1628     }
1629   }
1630 
1631   /* Check if caps have changed */
1632   if (G_UNLIKELY (codec_tag != demux->video_codec_tag || demux->got_par)) {
1633     GST_DEBUG_OBJECT (demux, "video settings have changed, changing caps");
1634     gst_buffer_replace (&demux->video_codec_data, NULL);
1635 
1636     if (!gst_flv_demux_video_negotiate (demux, codec_tag)) {
1637       ret = GST_FLOW_ERROR;
1638       goto beach;
1639     }
1640 
1641     /* When we ve set pixel-aspect-ratio we use that boolean to detect a
1642      * metadata tag that would come later and trigger a caps change */
1643     demux->got_par = FALSE;
1644   }
1645 
1646   /* Check if we have anything to push */
1647   if (demux->tag_data_size <= codec_data) {
1648     GST_LOG_OBJECT (demux, "Nothing left in this tag, returning");
1649     goto beach;
1650   }
1651 
1652   /* Create buffer from pad */
1653   outbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY,
1654       7 + codec_data, demux->tag_data_size - codec_data);
1655 
1656   /* detect (and deem to be resyncs)  large dts gaps */
1657   if (gst_flv_demux_update_resync (demux, dts, demux->video_need_discont,
1658           &demux->last_video_dts, &demux->video_time_offset)) {
1659     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC);
1660   }
1661 
1662   /* Fill buffer with data */
1663   GST_LOG_OBJECT (demux, "dts %u pts %u cts %d", dts, dts + cts, cts);
1664 
1665   GST_BUFFER_PTS (outbuf) =
1666       (dts + cts) * GST_MSECOND + demux->video_time_offset;
1667   GST_BUFFER_DTS (outbuf) = dts * GST_MSECOND + demux->video_time_offset;
1668   GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
1669   GST_BUFFER_OFFSET (outbuf) = demux->video_offset++;
1670   GST_BUFFER_OFFSET_END (outbuf) = demux->video_offset;
1671 
1672   if (demux->duration == GST_CLOCK_TIME_NONE ||
1673       demux->duration < GST_BUFFER_TIMESTAMP (outbuf))
1674     demux->duration = GST_BUFFER_TIMESTAMP (outbuf);
1675 
1676   if (!keyframe)
1677     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
1678 
1679   if (!demux->indexed) {
1680     gst_flv_demux_parse_and_add_index_entry (demux,
1681         GST_BUFFER_TIMESTAMP (outbuf), demux->cur_tag_offset, keyframe);
1682   }
1683 
1684   if (G_UNLIKELY (demux->video_need_discont)) {
1685     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
1686     demux->video_need_discont = FALSE;
1687   }
1688 
1689   demux->segment.position = GST_BUFFER_TIMESTAMP (outbuf);
1690 
1691   /* Do we need a newsegment event ? */
1692   if (G_UNLIKELY (demux->video_need_segment)) {
1693     if (!demux->new_seg_event) {
1694       GST_DEBUG_OBJECT (demux, "pushing newsegment from %"
1695           GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
1696           GST_TIME_ARGS (demux->segment.position),
1697           GST_TIME_ARGS (demux->segment.stop));
1698       demux->segment.start = demux->segment.time = demux->segment.position;
1699       demux->new_seg_event = gst_event_new_segment (&demux->segment);
1700     } else {
1701       GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event");
1702     }
1703 
1704     gst_pad_push_event (demux->video_pad, gst_event_ref (demux->new_seg_event));
1705 
1706     demux->video_need_segment = FALSE;
1707   }
1708 
1709   GST_LOG_OBJECT (demux,
1710       "pushing %" G_GSIZE_FORMAT " bytes buffer at dts %" GST_TIME_FORMAT
1711       " with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
1712       ", keyframe (%d)", gst_buffer_get_size (outbuf),
1713       GST_TIME_ARGS (GST_BUFFER_DTS (outbuf)),
1714       GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
1715       keyframe);
1716 
1717   if (!GST_CLOCK_TIME_IS_VALID (demux->video_start)) {
1718     demux->video_start = GST_BUFFER_TIMESTAMP (outbuf);
1719   }
1720   if (!GST_CLOCK_TIME_IS_VALID (demux->audio_first_ts)) {
1721     demux->video_first_ts = GST_BUFFER_TIMESTAMP (outbuf);
1722   }
1723 
1724   if (G_UNLIKELY (!demux->no_more_pads
1725           && (GST_CLOCK_DIFF (demux->video_start,
1726                   GST_BUFFER_TIMESTAMP (outbuf)) > NO_MORE_PADS_THRESHOLD))) {
1727     GST_DEBUG_OBJECT (demux,
1728         "Signalling no-more-pads because no audio stream was found"
1729         " after 6 seconds of video");
1730     gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
1731     demux->no_more_pads = TRUE;
1732   }
1733 
1734   /* Push downstream */
1735   ret = gst_pad_push (demux->video_pad, outbuf);
1736 
1737   if (G_UNLIKELY (ret != GST_FLOW_OK) &&
1738       demux->segment.rate < 0.0 && ret == GST_FLOW_EOS &&
1739       demux->segment.position > demux->segment.stop) {
1740     /* In reverse playback we can get a GST_FLOW_EOS when
1741      * we are at the end of the segment, so we just need to jump
1742      * back to the previous section. */
1743     GST_DEBUG_OBJECT (demux, "downstream has reached end of segment");
1744     demux->video_done = TRUE;
1745     ret = GST_FLOW_OK;
1746     goto beach;
1747   }
1748 
1749   ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner,
1750       demux->video_pad, ret);
1751 
1752 beach:
1753   gst_buffer_unmap (buffer, &map);
1754   return ret;
1755 }
1756 
1757 static GstClockTime
gst_flv_demux_parse_tag_timestamp(GstFlvDemux * demux,gboolean index,GstBuffer * buffer,size_t * tag_size)1758 gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
1759     GstBuffer * buffer, size_t * tag_size)
1760 {
1761   guint32 dts = 0, dts_ext = 0;
1762   guint32 tag_data_size;
1763   guint8 type;
1764   gboolean keyframe = TRUE;
1765   GstClockTime ret = GST_CLOCK_TIME_NONE;
1766   GstMapInfo map;
1767   guint8 *data;
1768   gsize size;
1769 
1770   g_return_val_if_fail (gst_buffer_get_size (buffer) >= 12,
1771       GST_CLOCK_TIME_NONE);
1772 
1773   gst_buffer_map (buffer, &map, GST_MAP_READ);
1774   data = map.data;
1775   size = map.size;
1776 
1777   type = data[0];
1778 
1779   if (type != 9 && type != 8 && type != 18) {
1780     GST_WARNING_OBJECT (demux, "Unsupported tag type %u", data[0]);
1781     goto exit;
1782   }
1783 
1784   if (type == 9)
1785     demux->has_video = TRUE;
1786   else if (type == 8)
1787     demux->has_audio = TRUE;
1788 
1789   tag_data_size = GST_READ_UINT24_BE (data + 1);
1790 
1791   if (size >= tag_data_size + 11 + 4) {
1792     if (GST_READ_UINT32_BE (data + tag_data_size + 11) != tag_data_size + 11) {
1793       GST_WARNING_OBJECT (demux, "Invalid tag size");
1794       goto exit;
1795     }
1796   }
1797 
1798   if (tag_size)
1799     *tag_size = tag_data_size + 11 + 4;
1800 
1801   data += 4;
1802 
1803   GST_LOG_OBJECT (demux, "dts bytes %02X %02X %02X %02X", data[0], data[1],
1804       data[2], data[3]);
1805 
1806   /* Grab timestamp of tag tag */
1807   dts = GST_READ_UINT24_BE (data);
1808   /* read the dts extension to 32 bits integer */
1809   dts_ext = GST_READ_UINT8 (data + 3);
1810   /* Combine them */
1811   dts |= dts_ext << 24;
1812 
1813   if (type == 9) {
1814     data += 7;
1815 
1816     keyframe = ((data[0] >> 4) == 1);
1817   }
1818 
1819   ret = dts * GST_MSECOND;
1820   GST_LOG_OBJECT (demux, "dts: %" GST_TIME_FORMAT, GST_TIME_ARGS (ret));
1821 
1822   if (index && !demux->indexed && (type == 9 || (type == 8
1823               && !demux->has_video))) {
1824     gst_flv_demux_parse_and_add_index_entry (demux, ret, demux->offset,
1825         keyframe);
1826   }
1827 
1828   if (demux->duration == GST_CLOCK_TIME_NONE || demux->duration < ret)
1829     demux->duration = ret;
1830 
1831 exit:
1832   gst_buffer_unmap (buffer, &map);
1833   return ret;
1834 }
1835 
1836 static GstFlowReturn
gst_flv_demux_parse_tag_type(GstFlvDemux * demux,GstBuffer * buffer)1837 gst_flv_demux_parse_tag_type (GstFlvDemux * demux, GstBuffer * buffer)
1838 {
1839   GstFlowReturn ret = GST_FLOW_OK;
1840   guint8 tag_type = 0;
1841   GstMapInfo map;
1842 
1843   g_return_val_if_fail (gst_buffer_get_size (buffer) >= 4, GST_FLOW_ERROR);
1844 
1845   gst_buffer_map (buffer, &map, GST_MAP_READ);
1846 
1847   tag_type = map.data[0];
1848 
1849   /* Tag size is 1 byte of type + 3 bytes of size + 7 bytes + tag data size +
1850    * 4 bytes of previous tag size */
1851   demux->tag_data_size = GST_READ_UINT24_BE (map.data + 1);
1852   demux->tag_size = demux->tag_data_size + 11;
1853 
1854   GST_LOG_OBJECT (demux, "tag data size is %" G_GUINT64_FORMAT,
1855       demux->tag_data_size);
1856 
1857   gst_buffer_unmap (buffer, &map);
1858 
1859   switch (tag_type) {
1860     case 9:
1861       demux->state = FLV_STATE_TAG_VIDEO;
1862       demux->has_video = TRUE;
1863       break;
1864     case 8:
1865       demux->state = FLV_STATE_TAG_AUDIO;
1866       demux->has_audio = TRUE;
1867       break;
1868     case 18:
1869       demux->state = FLV_STATE_TAG_SCRIPT;
1870       break;
1871     default:
1872       GST_WARNING_OBJECT (demux, "unsupported tag type %u", tag_type);
1873       demux->state = FLV_STATE_SKIP;
1874   }
1875 
1876   return ret;
1877 }
1878 
1879 static GstFlowReturn
gst_flv_demux_parse_header(GstFlvDemux * demux,GstBuffer * buffer)1880 gst_flv_demux_parse_header (GstFlvDemux * demux, GstBuffer * buffer)
1881 {
1882   GstFlowReturn ret = GST_FLOW_OK;
1883   GstMapInfo map;
1884 
1885   g_return_val_if_fail (gst_buffer_get_size (buffer) >= 9, GST_FLOW_ERROR);
1886 
1887   gst_buffer_map (buffer, &map, GST_MAP_READ);
1888 
1889   /* Check for the FLV tag */
1890   if (map.data[0] == 'F' && map.data[1] == 'L' && map.data[2] == 'V') {
1891     GST_DEBUG_OBJECT (demux, "FLV header detected");
1892   } else {
1893     if (G_UNLIKELY (demux->strict)) {
1894       GST_WARNING_OBJECT (demux, "invalid header tag detected");
1895       ret = GST_FLOW_EOS;
1896       goto beach;
1897     }
1898   }
1899 
1900   if (map.data[3] == '1') {
1901     GST_DEBUG_OBJECT (demux, "FLV version 1 detected");
1902   } else {
1903     if (G_UNLIKELY (demux->strict)) {
1904       GST_WARNING_OBJECT (demux, "invalid header version detected");
1905       ret = GST_FLOW_EOS;
1906       goto beach;
1907     }
1908 
1909   }
1910 
1911   /* Now look at audio/video flags */
1912   {
1913     guint8 flags = map.data[4];
1914 
1915     demux->has_video = demux->has_audio = FALSE;
1916 
1917     if (flags & 1) {
1918       GST_DEBUG_OBJECT (demux, "there is a video stream");
1919       demux->has_video = TRUE;
1920     }
1921     if (flags & 4) {
1922       GST_DEBUG_OBJECT (demux, "there is an audio stream");
1923       demux->has_audio = TRUE;
1924     }
1925   }
1926 
1927   /* do a one-time seekability check */
1928   gst_flv_demux_check_seekability (demux);
1929 
1930   /* We don't care about the rest */
1931   demux->need_header = FALSE;
1932 
1933 beach:
1934   gst_buffer_unmap (buffer, &map);
1935   return ret;
1936 }
1937 
1938 
1939 static void
gst_flv_demux_flush(GstFlvDemux * demux,gboolean discont)1940 gst_flv_demux_flush (GstFlvDemux * demux, gboolean discont)
1941 {
1942   GST_DEBUG_OBJECT (demux, "flushing queued data in the FLV demuxer");
1943 
1944   gst_adapter_clear (demux->adapter);
1945 
1946   demux->audio_need_discont = TRUE;
1947   demux->video_need_discont = TRUE;
1948 
1949   demux->flushing = FALSE;
1950 
1951   /* Only in push mode and if we're not during a seek */
1952   if (!demux->random_access && demux->state != FLV_STATE_SEEK) {
1953     /* After a flush we expect a tag_type */
1954     demux->state = FLV_STATE_TAG_TYPE;
1955     /* We reset the offset and will get one from first push */
1956     demux->offset = 0;
1957   }
1958 }
1959 
1960 static void
gst_flv_demux_cleanup(GstFlvDemux * demux)1961 gst_flv_demux_cleanup (GstFlvDemux * demux)
1962 {
1963   GST_DEBUG_OBJECT (demux, "cleaning up FLV demuxer");
1964 
1965   demux->state = FLV_STATE_HEADER;
1966 
1967   demux->have_group_id = FALSE;
1968   demux->group_id = G_MAXUINT;
1969 
1970   demux->flushing = FALSE;
1971   demux->need_header = TRUE;
1972   demux->audio_need_segment = TRUE;
1973   demux->video_need_segment = TRUE;
1974   demux->audio_need_discont = TRUE;
1975   demux->video_need_discont = TRUE;
1976 
1977   demux->has_audio = FALSE;
1978   demux->has_video = FALSE;
1979   demux->got_par = FALSE;
1980 
1981   demux->indexed = FALSE;
1982   demux->upstream_seekable = FALSE;
1983   demux->file_size = 0;
1984 
1985   demux->index_max_pos = 0;
1986   demux->index_max_time = 0;
1987 
1988   demux->audio_start = demux->video_start = GST_CLOCK_TIME_NONE;
1989   demux->last_audio_pts = demux->last_video_dts = 0;
1990   demux->audio_time_offset = demux->video_time_offset = 0;
1991 
1992   demux->no_more_pads = FALSE;
1993 
1994 #ifndef GST_DISABLE_DEBUG
1995   demux->no_audio_warned = FALSE;
1996   demux->no_video_warned = FALSE;
1997 #endif
1998 
1999   gst_segment_init (&demux->segment, GST_FORMAT_TIME);
2000 
2001   demux->w = demux->h = 0;
2002   demux->framerate = 0.0;
2003   demux->par_x = demux->par_y = 1;
2004   demux->video_offset = 0;
2005   demux->audio_offset = 0;
2006   demux->offset = demux->cur_tag_offset = 0;
2007   demux->tag_size = demux->tag_data_size = 0;
2008   demux->duration = GST_CLOCK_TIME_NONE;
2009 
2010   if (demux->new_seg_event) {
2011     gst_event_unref (demux->new_seg_event);
2012     demux->new_seg_event = NULL;
2013   }
2014 
2015   gst_adapter_clear (demux->adapter);
2016 
2017   if (demux->audio_codec_data) {
2018     gst_buffer_unref (demux->audio_codec_data);
2019     demux->audio_codec_data = NULL;
2020   }
2021 
2022   if (demux->video_codec_data) {
2023     gst_buffer_unref (demux->video_codec_data);
2024     demux->video_codec_data = NULL;
2025   }
2026 
2027   if (demux->audio_pad) {
2028     gst_flow_combiner_remove_pad (demux->flowcombiner, demux->audio_pad);
2029     gst_element_remove_pad (GST_ELEMENT (demux), demux->audio_pad);
2030     gst_object_unref (demux->audio_pad);
2031     demux->audio_pad = NULL;
2032   }
2033 
2034   if (demux->video_pad) {
2035     gst_flow_combiner_remove_pad (demux->flowcombiner, demux->video_pad);
2036     gst_element_remove_pad (GST_ELEMENT (demux), demux->video_pad);
2037     gst_object_unref (demux->video_pad);
2038     demux->video_pad = NULL;
2039   }
2040 
2041   if (demux->times) {
2042     g_array_free (demux->times, TRUE);
2043     demux->times = NULL;
2044   }
2045 
2046   if (demux->filepositions) {
2047     g_array_free (demux->filepositions, TRUE);
2048     demux->filepositions = NULL;
2049   }
2050 
2051   gst_flv_demux_clear_tags (demux);
2052 }
2053 
2054 /*
2055  * Create and push a flushing seek event upstream
2056  */
2057 static gboolean
flv_demux_seek_to_offset(GstFlvDemux * demux,guint64 offset)2058 flv_demux_seek_to_offset (GstFlvDemux * demux, guint64 offset)
2059 {
2060   GstEvent *event;
2061   gboolean res = 0;
2062 
2063   GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
2064 
2065   event =
2066       gst_event_new_seek (1.0, GST_FORMAT_BYTES,
2067       GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
2068       GST_SEEK_TYPE_NONE, -1);
2069 
2070   res = gst_pad_push_event (demux->sinkpad, event);
2071 
2072   if (res)
2073     demux->offset = offset;
2074   return res;
2075 }
2076 
2077 static GstFlowReturn
gst_flv_demux_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)2078 gst_flv_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
2079 {
2080   GstFlowReturn ret = GST_FLOW_OK;
2081   GstFlvDemux *demux = NULL;
2082 
2083   demux = GST_FLV_DEMUX (parent);
2084 
2085   GST_LOG_OBJECT (demux,
2086       "received buffer of %" G_GSIZE_FORMAT " bytes at offset %"
2087       G_GUINT64_FORMAT, gst_buffer_get_size (buffer),
2088       GST_BUFFER_OFFSET (buffer));
2089 
2090   if (G_UNLIKELY (GST_BUFFER_OFFSET (buffer) == 0)) {
2091     GST_DEBUG_OBJECT (demux, "beginning of file, expect header");
2092     demux->state = FLV_STATE_HEADER;
2093     demux->offset = 0;
2094   }
2095 
2096   if (G_UNLIKELY (demux->offset == 0 && GST_BUFFER_OFFSET (buffer) != 0)) {
2097     GST_DEBUG_OBJECT (demux, "offset was zero, synchronizing with buffer's");
2098     demux->offset = GST_BUFFER_OFFSET (buffer);
2099   }
2100 
2101   if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
2102     GST_DEBUG_OBJECT (demux, "Discontinuity");
2103     gst_adapter_clear (demux->adapter);
2104   }
2105 
2106   gst_adapter_push (demux->adapter, buffer);
2107 
2108   if (demux->seeking) {
2109     demux->state = FLV_STATE_SEEK;
2110     GST_OBJECT_LOCK (demux);
2111     demux->seeking = FALSE;
2112     GST_OBJECT_UNLOCK (demux);
2113   }
2114 
2115 parse:
2116   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
2117     GST_DEBUG_OBJECT (demux, "got flow return %s", gst_flow_get_name (ret));
2118     goto beach;
2119   }
2120 
2121   if (G_UNLIKELY (demux->flushing)) {
2122     GST_DEBUG_OBJECT (demux, "we are now flushing, exiting parser loop");
2123     ret = GST_FLOW_FLUSHING;
2124     goto beach;
2125   }
2126 
2127   switch (demux->state) {
2128     case FLV_STATE_HEADER:
2129     {
2130       if (gst_adapter_available (demux->adapter) >= FLV_HEADER_SIZE) {
2131         GstBuffer *buffer;
2132 
2133         buffer = gst_adapter_take_buffer (demux->adapter, FLV_HEADER_SIZE);
2134 
2135         ret = gst_flv_demux_parse_header (demux, buffer);
2136 
2137         gst_buffer_unref (buffer);
2138         demux->offset += FLV_HEADER_SIZE;
2139 
2140         demux->state = FLV_STATE_TAG_TYPE;
2141         goto parse;
2142       } else {
2143         goto beach;
2144       }
2145     }
2146     case FLV_STATE_TAG_TYPE:
2147     {
2148       if (gst_adapter_available (demux->adapter) >= FLV_TAG_TYPE_SIZE) {
2149         GstBuffer *buffer;
2150 
2151         /* Remember the tag offset in bytes */
2152         demux->cur_tag_offset = demux->offset;
2153 
2154         buffer = gst_adapter_take_buffer (demux->adapter, FLV_TAG_TYPE_SIZE);
2155 
2156         ret = gst_flv_demux_parse_tag_type (demux, buffer);
2157 
2158         gst_buffer_unref (buffer);
2159         demux->offset += FLV_TAG_TYPE_SIZE;
2160 
2161         /* last tag is not an index => no index/don't know where the index is
2162          * seek back to the beginning */
2163         if (demux->seek_event && demux->state != FLV_STATE_TAG_SCRIPT)
2164           goto no_index;
2165 
2166         goto parse;
2167       } else {
2168         goto beach;
2169       }
2170     }
2171     case FLV_STATE_TAG_VIDEO:
2172     {
2173       if (gst_adapter_available (demux->adapter) >= demux->tag_size) {
2174         GstBuffer *buffer;
2175 
2176         buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size);
2177 
2178         ret = gst_flv_demux_parse_tag_video (demux, buffer);
2179 
2180         gst_buffer_unref (buffer);
2181         demux->offset += demux->tag_size;
2182 
2183         demux->state = FLV_STATE_TAG_TYPE;
2184         goto parse;
2185       } else {
2186         goto beach;
2187       }
2188     }
2189     case FLV_STATE_TAG_AUDIO:
2190     {
2191       if (gst_adapter_available (demux->adapter) >= demux->tag_size) {
2192         GstBuffer *buffer;
2193 
2194         buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size);
2195 
2196         ret = gst_flv_demux_parse_tag_audio (demux, buffer);
2197 
2198         gst_buffer_unref (buffer);
2199         demux->offset += demux->tag_size;
2200 
2201         demux->state = FLV_STATE_TAG_TYPE;
2202         goto parse;
2203       } else {
2204         goto beach;
2205       }
2206     }
2207     case FLV_STATE_TAG_SCRIPT:
2208     {
2209       if (gst_adapter_available (demux->adapter) >= demux->tag_size) {
2210         GstBuffer *buffer;
2211 
2212         buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size);
2213 
2214         ret = gst_flv_demux_parse_tag_script (demux, buffer);
2215 
2216         gst_buffer_unref (buffer);
2217         demux->offset += demux->tag_size;
2218 
2219         demux->state = FLV_STATE_TAG_TYPE;
2220 
2221         /* if there's a seek event we're here for the index so if we don't have it
2222          * we seek back to the beginning */
2223         if (demux->seek_event) {
2224           if (demux->indexed)
2225             demux->state = FLV_STATE_SEEK;
2226           else
2227             goto no_index;
2228         }
2229 
2230         goto parse;
2231       } else {
2232         goto beach;
2233       }
2234     }
2235     case FLV_STATE_SEEK:
2236     {
2237       GstEvent *event;
2238 
2239       ret = GST_FLOW_OK;
2240 
2241       if (!demux->indexed) {
2242         if (demux->offset == demux->file_size - sizeof (guint32)) {
2243           guint64 seek_offset;
2244           guint8 *data;
2245 
2246           data = gst_adapter_take (demux->adapter, 4);
2247           if (!data)
2248             goto no_index;
2249 
2250           seek_offset = demux->file_size - sizeof (guint32) -
2251               GST_READ_UINT32_BE (data);
2252           g_free (data);
2253 
2254           GST_INFO_OBJECT (demux,
2255               "Seeking to beginning of last tag at %" G_GUINT64_FORMAT,
2256               seek_offset);
2257           demux->state = FLV_STATE_TAG_TYPE;
2258           flv_demux_seek_to_offset (demux, seek_offset);
2259           goto beach;
2260         } else
2261           goto no_index;
2262       }
2263 
2264       GST_OBJECT_LOCK (demux);
2265       event = demux->seek_event;
2266       demux->seek_event = NULL;
2267       GST_OBJECT_UNLOCK (demux);
2268 
2269       /* calculate and perform seek */
2270       if (!flv_demux_handle_seek_push (demux, event))
2271         goto seek_failed;
2272 
2273       gst_event_unref (event);
2274       demux->state = FLV_STATE_TAG_TYPE;
2275       goto beach;
2276     }
2277     case FLV_STATE_SKIP:
2278       /* Skip unknown tags (set in _parse_tag_type()) */
2279       if (gst_adapter_available (demux->adapter) >= demux->tag_size) {
2280         gst_adapter_flush (demux->adapter, demux->tag_size);
2281         demux->offset += demux->tag_size;
2282         demux->state = FLV_STATE_TAG_TYPE;
2283         goto parse;
2284       } else {
2285         goto beach;
2286       }
2287     default:
2288       GST_DEBUG_OBJECT (demux, "unexpected demuxer state");
2289   }
2290 
2291 beach:
2292   return ret;
2293 
2294 /* ERRORS */
2295 no_index:
2296   {
2297     GST_OBJECT_LOCK (demux);
2298     demux->seeking = FALSE;
2299     gst_event_unref (demux->seek_event);
2300     demux->seek_event = NULL;
2301     GST_OBJECT_UNLOCK (demux);
2302     GST_WARNING_OBJECT (demux,
2303         "failed to find an index, seeking back to beginning");
2304     flv_demux_seek_to_offset (demux, 0);
2305     return GST_FLOW_OK;
2306   }
2307 seek_failed:
2308   {
2309     GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("seek failed"));
2310     return GST_FLOW_ERROR;
2311   }
2312 
2313 }
2314 
2315 static GstFlowReturn
gst_flv_demux_pull_range(GstFlvDemux * demux,GstPad * pad,guint64 offset,guint size,GstBuffer ** buffer)2316 gst_flv_demux_pull_range (GstFlvDemux * demux, GstPad * pad, guint64 offset,
2317     guint size, GstBuffer ** buffer)
2318 {
2319   GstFlowReturn ret;
2320 
2321   ret = gst_pad_pull_range (pad, offset, size, buffer);
2322   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
2323     GST_WARNING_OBJECT (demux,
2324         "failed when pulling %d bytes from offset %" G_GUINT64_FORMAT ": %s",
2325         size, offset, gst_flow_get_name (ret));
2326     *buffer = NULL;
2327     return ret;
2328   }
2329 
2330   if (G_UNLIKELY (*buffer && gst_buffer_get_size (*buffer) != size)) {
2331     GST_WARNING_OBJECT (demux,
2332         "partial pull got %" G_GSIZE_FORMAT " when expecting %d from offset %"
2333         G_GUINT64_FORMAT, gst_buffer_get_size (*buffer), size, offset);
2334     gst_buffer_unref (*buffer);
2335     ret = GST_FLOW_EOS;
2336     *buffer = NULL;
2337     return ret;
2338   }
2339 
2340   return ret;
2341 }
2342 
2343 static GstFlowReturn
gst_flv_demux_pull_tag(GstPad * pad,GstFlvDemux * demux)2344 gst_flv_demux_pull_tag (GstPad * pad, GstFlvDemux * demux)
2345 {
2346   GstBuffer *buffer = NULL;
2347   GstFlowReturn ret = GST_FLOW_OK;
2348 
2349   /* Store tag offset */
2350   demux->cur_tag_offset = demux->offset;
2351 
2352   /* Get the first 4 bytes to identify tag type and size */
2353   if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset,
2354                   FLV_TAG_TYPE_SIZE, &buffer)) != GST_FLOW_OK))
2355     goto beach;
2356 
2357   /* Identify tag type */
2358   ret = gst_flv_demux_parse_tag_type (demux, buffer);
2359 
2360   gst_buffer_unref (buffer);
2361 
2362   if (G_UNLIKELY (ret != GST_FLOW_OK))
2363     goto beach;
2364 
2365   /* Jump over tag type + size */
2366   demux->offset += FLV_TAG_TYPE_SIZE;
2367 
2368   /* Pull the whole tag */
2369   buffer = NULL;
2370   if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset,
2371                   demux->tag_size, &buffer)) != GST_FLOW_OK))
2372     goto beach;
2373 
2374   switch (demux->state) {
2375     case FLV_STATE_TAG_VIDEO:
2376       ret = gst_flv_demux_parse_tag_video (demux, buffer);
2377       break;
2378     case FLV_STATE_TAG_AUDIO:
2379       ret = gst_flv_demux_parse_tag_audio (demux, buffer);
2380       break;
2381     case FLV_STATE_TAG_SCRIPT:
2382       ret = gst_flv_demux_parse_tag_script (demux, buffer);
2383       break;
2384     default:
2385       GST_WARNING_OBJECT (demux, "unexpected state %d", demux->state);
2386   }
2387 
2388   gst_buffer_unref (buffer);
2389 
2390   /* Jump over that part we've just parsed */
2391   demux->offset += demux->tag_size;
2392 
2393   /* Make sure we reinitialize the tag size */
2394   demux->tag_size = 0;
2395 
2396   /* Ready for the next tag */
2397   demux->state = FLV_STATE_TAG_TYPE;
2398 
2399   if (G_UNLIKELY (ret == GST_FLOW_NOT_LINKED)) {
2400     GST_WARNING_OBJECT (demux, "parsing this tag returned not-linked and "
2401         "neither video nor audio are linked");
2402   }
2403 
2404 beach:
2405   return ret;
2406 }
2407 
2408 static GstFlowReturn
gst_flv_demux_pull_header(GstPad * pad,GstFlvDemux * demux)2409 gst_flv_demux_pull_header (GstPad * pad, GstFlvDemux * demux)
2410 {
2411   GstBuffer *buffer = NULL;
2412   GstFlowReturn ret = GST_FLOW_OK;
2413 
2414   /* Get the first 9 bytes */
2415   if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset,
2416                   FLV_HEADER_SIZE, &buffer)) != GST_FLOW_OK))
2417     goto beach;
2418 
2419   ret = gst_flv_demux_parse_header (demux, buffer);
2420 
2421   gst_buffer_unref (buffer);
2422 
2423   /* Jump over the header now */
2424   demux->offset += FLV_HEADER_SIZE;
2425   demux->state = FLV_STATE_TAG_TYPE;
2426 
2427 beach:
2428   return ret;
2429 }
2430 
2431 static void
gst_flv_demux_move_to_offset(GstFlvDemux * demux,gint64 offset,gboolean reset)2432 gst_flv_demux_move_to_offset (GstFlvDemux * demux, gint64 offset,
2433     gboolean reset)
2434 {
2435   demux->offset = offset;
2436 
2437   /* Tell all the stream we moved to a different position (discont) */
2438   demux->audio_need_discont = TRUE;
2439   demux->video_need_discont = TRUE;
2440 
2441   /* next section setup */
2442   demux->from_offset = -1;
2443   demux->audio_done = demux->video_done = FALSE;
2444   demux->audio_first_ts = demux->video_first_ts = GST_CLOCK_TIME_NONE;
2445 
2446   if (reset) {
2447     demux->from_offset = -1;
2448     demux->to_offset = G_MAXINT64;
2449   }
2450 
2451   /* If we seeked at the beginning of the file parse the header again */
2452   if (G_UNLIKELY (!demux->offset)) {
2453     demux->state = FLV_STATE_HEADER;
2454   } else {                      /* or parse a tag */
2455     demux->state = FLV_STATE_TAG_TYPE;
2456   }
2457 }
2458 
2459 static GstFlowReturn
gst_flv_demux_seek_to_prev_keyframe(GstFlvDemux * demux)2460 gst_flv_demux_seek_to_prev_keyframe (GstFlvDemux * demux)
2461 {
2462   GstFlowReturn ret = GST_FLOW_EOS;
2463   GstIndex *index;
2464   GstIndexEntry *entry = NULL;
2465 
2466   GST_DEBUG_OBJECT (demux,
2467       "terminated section started at offset %" G_GINT64_FORMAT,
2468       demux->from_offset);
2469 
2470   /* we are done if we got all audio and video */
2471   if ((!GST_CLOCK_TIME_IS_VALID (demux->audio_first_ts) ||
2472           demux->audio_first_ts < demux->segment.start) &&
2473       (!GST_CLOCK_TIME_IS_VALID (demux->video_first_ts) ||
2474           demux->video_first_ts < demux->segment.start))
2475     goto done;
2476 
2477   if (demux->from_offset <= 0)
2478     goto done;
2479 
2480   GST_DEBUG_OBJECT (demux, "locating previous position");
2481 
2482   index = gst_flv_demux_get_index (GST_ELEMENT (demux));
2483 
2484   /* locate index entry before previous start position */
2485   if (index) {
2486     entry = gst_index_get_assoc_entry (index, demux->index_id,
2487         GST_INDEX_LOOKUP_BEFORE, GST_ASSOCIATION_FLAG_KEY_UNIT,
2488         GST_FORMAT_BYTES, demux->from_offset - 1);
2489 
2490     if (entry) {
2491       gint64 bytes = 0, time = 0;
2492 
2493       gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes);
2494       gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
2495 
2496       GST_DEBUG_OBJECT (demux, "found index entry for %" G_GINT64_FORMAT
2497           " at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT,
2498           demux->offset - 1, GST_TIME_ARGS (time), bytes);
2499 
2500       /* setup for next section */
2501       demux->to_offset = demux->from_offset;
2502       gst_flv_demux_move_to_offset (demux, bytes, FALSE);
2503       ret = GST_FLOW_OK;
2504     }
2505 
2506     gst_object_unref (index);
2507   }
2508 
2509 done:
2510   return ret;
2511 }
2512 
2513 static GstFlowReturn
gst_flv_demux_create_index(GstFlvDemux * demux,gint64 pos,GstClockTime ts)2514 gst_flv_demux_create_index (GstFlvDemux * demux, gint64 pos, GstClockTime ts)
2515 {
2516   gint64 size;
2517   size_t tag_size;
2518   guint64 old_offset;
2519   GstBuffer *buffer;
2520   GstClockTime tag_time;
2521   GstFlowReturn ret = GST_FLOW_OK;
2522 
2523   if (!gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &size))
2524     return GST_FLOW_OK;
2525 
2526   GST_DEBUG_OBJECT (demux, "building index at %" G_GINT64_FORMAT
2527       " looking for time %" GST_TIME_FORMAT, pos, GST_TIME_ARGS (ts));
2528 
2529   old_offset = demux->offset;
2530   demux->offset = pos;
2531 
2532   buffer = NULL;
2533   while ((ret = gst_flv_demux_pull_range (demux, demux->sinkpad, demux->offset,
2534               12, &buffer)) == GST_FLOW_OK) {
2535     tag_time =
2536         gst_flv_demux_parse_tag_timestamp (demux, TRUE, buffer, &tag_size);
2537 
2538     gst_buffer_unref (buffer);
2539     buffer = NULL;
2540 
2541     if (G_UNLIKELY (tag_time == GST_CLOCK_TIME_NONE || tag_time > ts))
2542       goto exit;
2543 
2544     demux->offset += tag_size;
2545   }
2546 
2547   if (ret == GST_FLOW_EOS) {
2548     /* file ran out, so mark we have complete index */
2549     demux->indexed = TRUE;
2550     ret = GST_FLOW_OK;
2551   }
2552 
2553 exit:
2554   demux->offset = old_offset;
2555 
2556   return ret;
2557 }
2558 
2559 static gint64
gst_flv_demux_get_metadata(GstFlvDemux * demux)2560 gst_flv_demux_get_metadata (GstFlvDemux * demux)
2561 {
2562   gint64 ret = 0, offset;
2563   size_t tag_size, size;
2564   GstBuffer *buffer = NULL;
2565   GstMapInfo map;
2566 
2567   if (!gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &offset))
2568     goto exit;
2569 
2570   ret = offset;
2571   GST_DEBUG_OBJECT (demux, "upstream size: %" G_GINT64_FORMAT, offset);
2572   if (G_UNLIKELY (offset < 4))
2573     goto exit;
2574 
2575   offset -= 4;
2576   if (GST_FLOW_OK != gst_flv_demux_pull_range (demux, demux->sinkpad, offset,
2577           4, &buffer))
2578     goto exit;
2579 
2580   gst_buffer_map (buffer, &map, GST_MAP_READ);
2581   tag_size = GST_READ_UINT32_BE (map.data);
2582   gst_buffer_unmap (buffer, &map);
2583   GST_DEBUG_OBJECT (demux, "last tag size: %" G_GSIZE_FORMAT, tag_size);
2584   gst_buffer_unref (buffer);
2585   buffer = NULL;
2586 
2587   if (G_UNLIKELY (offset < tag_size))
2588     goto exit;
2589 
2590   offset -= tag_size;
2591   if (GST_FLOW_OK != gst_flv_demux_pull_range (demux, demux->sinkpad, offset,
2592           12, &buffer))
2593     goto exit;
2594 
2595   /* a consistency check */
2596   gst_buffer_map (buffer, &map, GST_MAP_READ);
2597   size = GST_READ_UINT24_BE (map.data + 1);
2598   if (size != tag_size - 11) {
2599     gst_buffer_unmap (buffer, &map);
2600     GST_DEBUG_OBJECT (demux,
2601         "tag size %" G_GSIZE_FORMAT ", expected %" G_GSIZE_FORMAT
2602         ", corrupt or truncated file", size, tag_size - 11);
2603     goto exit;
2604   }
2605 
2606   /* try to update duration with timestamp in any case */
2607   gst_flv_demux_parse_tag_timestamp (demux, FALSE, buffer, &size);
2608 
2609   /* maybe get some more metadata */
2610   if (map.data[0] == 18) {
2611     gst_buffer_unmap (buffer, &map);
2612     gst_buffer_unref (buffer);
2613     buffer = NULL;
2614     GST_DEBUG_OBJECT (demux, "script tag, pulling it to parse");
2615     offset += 4;
2616     if (GST_FLOW_OK == gst_flv_demux_pull_range (demux, demux->sinkpad, offset,
2617             tag_size, &buffer))
2618       gst_flv_demux_parse_tag_script (demux, buffer);
2619   } else {
2620     gst_buffer_unmap (buffer, &map);
2621   }
2622 
2623 exit:
2624   if (buffer)
2625     gst_buffer_unref (buffer);
2626 
2627   return ret;
2628 }
2629 
2630 static void
gst_flv_demux_loop(GstPad * pad)2631 gst_flv_demux_loop (GstPad * pad)
2632 {
2633   GstFlvDemux *demux = NULL;
2634   GstFlowReturn ret = GST_FLOW_OK;
2635 
2636   demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
2637 
2638   /* pull in data */
2639   switch (demux->state) {
2640     case FLV_STATE_TAG_TYPE:
2641       if (demux->from_offset == -1)
2642         demux->from_offset = demux->offset;
2643       ret = gst_flv_demux_pull_tag (pad, demux);
2644       /* if we have seen real data, we probably passed a possible metadata
2645        * header located at start.  So if we do not yet have an index,
2646        * try to pick up metadata (index, duration) at the end */
2647       if (G_UNLIKELY (!demux->file_size && !demux->indexed &&
2648               (demux->has_video || demux->has_audio)))
2649         demux->file_size = gst_flv_demux_get_metadata (demux);
2650       break;
2651     case FLV_STATE_DONE:
2652       ret = GST_FLOW_EOS;
2653       break;
2654     case FLV_STATE_SEEK:
2655       /* seek issued with insufficient index;
2656        * scan for index in task thread from current maximum offset to
2657        * desired time and then perform seek */
2658       /* TODO maybe some buffering message or so to indicate scan progress */
2659       ret = gst_flv_demux_create_index (demux, demux->index_max_pos,
2660           demux->seek_time);
2661       if (ret != GST_FLOW_OK)
2662         goto pause;
2663       /* position and state arranged by seek,
2664        * also unrefs event */
2665       gst_flv_demux_handle_seek_pull (demux, demux->seek_event, FALSE);
2666       demux->seek_event = NULL;
2667       break;
2668     default:
2669       ret = gst_flv_demux_pull_header (pad, demux);
2670       /* index scans start after header */
2671       demux->index_max_pos = demux->offset;
2672       break;
2673   }
2674 
2675   if (demux->segment.rate < 0.0) {
2676     /* check end of section */
2677     if ((gint64) demux->offset >= demux->to_offset ||
2678         demux->segment.position >= demux->segment.stop + 2 * GST_SECOND ||
2679         (demux->audio_done && demux->video_done))
2680       ret = gst_flv_demux_seek_to_prev_keyframe (demux);
2681   } else {
2682     /* check EOS condition */
2683     if ((demux->segment.stop != -1) &&
2684         (demux->segment.position >= demux->segment.stop)) {
2685       ret = GST_FLOW_EOS;
2686     }
2687   }
2688 
2689   /* pause if something went wrong or at end */
2690   if (G_UNLIKELY (ret != GST_FLOW_OK) && !(ret == GST_FLOW_NOT_LINKED
2691           && !demux->no_more_pads))
2692     goto pause;
2693 
2694   gst_object_unref (demux);
2695 
2696   return;
2697 
2698 pause:
2699   {
2700     const gchar *reason = gst_flow_get_name (ret);
2701 
2702     GST_LOG_OBJECT (demux, "pausing task, reason %s", reason);
2703     gst_pad_pause_task (pad);
2704 
2705     if (ret == GST_FLOW_EOS) {
2706       /* handle end-of-stream/segment */
2707       /* so align our position with the end of it, if there is one
2708        * this ensures a subsequent will arrive at correct base/acc time */
2709       if (demux->segment.rate > 0.0 &&
2710           GST_CLOCK_TIME_IS_VALID (demux->segment.stop))
2711         demux->segment.position = demux->segment.stop;
2712       else if (demux->segment.rate < 0.0)
2713         demux->segment.position = demux->segment.start;
2714 
2715       /* perform EOS logic */
2716       if (!demux->no_more_pads) {
2717         gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
2718         demux->no_more_pads = TRUE;
2719       }
2720 
2721       if (demux->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
2722         gint64 stop;
2723 
2724         /* for segment playback we need to post when (in stream time)
2725          * we stopped, this is either stop (when set) or the duration. */
2726         if ((stop = demux->segment.stop) == -1)
2727           stop = demux->segment.duration;
2728 
2729         if (demux->segment.rate >= 0) {
2730           GST_LOG_OBJECT (demux, "Sending segment done, at end of segment");
2731           gst_element_post_message (GST_ELEMENT_CAST (demux),
2732               gst_message_new_segment_done (GST_OBJECT_CAST (demux),
2733                   GST_FORMAT_TIME, stop));
2734           gst_flv_demux_push_src_event (demux,
2735               gst_event_new_segment_done (GST_FORMAT_TIME, stop));
2736         } else {                /* Reverse playback */
2737           GST_LOG_OBJECT (demux, "Sending segment done, at beginning of "
2738               "segment");
2739           gst_element_post_message (GST_ELEMENT_CAST (demux),
2740               gst_message_new_segment_done (GST_OBJECT_CAST (demux),
2741                   GST_FORMAT_TIME, demux->segment.start));
2742           gst_flv_demux_push_src_event (demux,
2743               gst_event_new_segment_done (GST_FORMAT_TIME,
2744                   demux->segment.start));
2745         }
2746       } else {
2747         /* normal playback, send EOS to all linked pads */
2748         if (!demux->no_more_pads) {
2749           gst_element_no_more_pads (GST_ELEMENT (demux));
2750           demux->no_more_pads = TRUE;
2751         }
2752 
2753         GST_LOG_OBJECT (demux, "Sending EOS, at end of stream");
2754         if (!demux->audio_pad && !demux->video_pad)
2755           GST_ELEMENT_ERROR (demux, STREAM, FAILED,
2756               ("Internal data stream error."), ("Got EOS before any data"));
2757         else if (!gst_flv_demux_push_src_event (demux, gst_event_new_eos ()))
2758           GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
2759       }
2760     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
2761       GST_ELEMENT_FLOW_ERROR (demux, ret);
2762       gst_flv_demux_push_src_event (demux, gst_event_new_eos ());
2763     }
2764     gst_object_unref (demux);
2765     return;
2766   }
2767 }
2768 
2769 static guint64
gst_flv_demux_find_offset(GstFlvDemux * demux,GstSegment * segment,GstSeekFlags seek_flags)2770 gst_flv_demux_find_offset (GstFlvDemux * demux, GstSegment * segment,
2771     GstSeekFlags seek_flags)
2772 {
2773   gint64 bytes = 0;
2774   gint64 time = 0;
2775   GstIndex *index;
2776   GstIndexEntry *entry;
2777 
2778   g_return_val_if_fail (segment != NULL, 0);
2779 
2780   time = segment->position;
2781 
2782   index = gst_flv_demux_get_index (GST_ELEMENT (demux));
2783 
2784   if (index) {
2785     /* Let's check if we have an index entry for that seek time */
2786     entry = gst_index_get_assoc_entry (index, demux->index_id,
2787         seek_flags & GST_SEEK_FLAG_SNAP_AFTER ?
2788         GST_INDEX_LOOKUP_AFTER : GST_INDEX_LOOKUP_BEFORE,
2789         GST_ASSOCIATION_FLAG_KEY_UNIT, GST_FORMAT_TIME, time);
2790 
2791     if (entry) {
2792       gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes);
2793       gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
2794 
2795       GST_DEBUG_OBJECT (demux, "found index entry for %" GST_TIME_FORMAT
2796           " at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT,
2797           GST_TIME_ARGS (segment->position), GST_TIME_ARGS (time), bytes);
2798 
2799       /* Key frame seeking */
2800       if (seek_flags & GST_SEEK_FLAG_KEY_UNIT) {
2801         /* Adjust the segment so that the keyframe fits in */
2802         segment->start = segment->time = time;
2803         segment->position = time;
2804       }
2805     } else {
2806       GST_DEBUG_OBJECT (demux, "no index entry found for %" GST_TIME_FORMAT,
2807           GST_TIME_ARGS (segment->start));
2808     }
2809 
2810     gst_object_unref (index);
2811   }
2812 
2813   return bytes;
2814 }
2815 
2816 static gboolean
flv_demux_handle_seek_push(GstFlvDemux * demux,GstEvent * event)2817 flv_demux_handle_seek_push (GstFlvDemux * demux, GstEvent * event)
2818 {
2819   GstFormat format;
2820   GstSeekFlags flags;
2821   GstSeekType start_type, stop_type;
2822   gint64 start, stop;
2823   gdouble rate;
2824   gboolean update, flush, ret;
2825   GstSegment seeksegment;
2826 
2827   gst_event_parse_seek (event, &rate, &format, &flags,
2828       &start_type, &start, &stop_type, &stop);
2829 
2830   if (format != GST_FORMAT_TIME)
2831     goto wrong_format;
2832 
2833   flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
2834 
2835   /* Work on a copy until we are sure the seek succeeded. */
2836   memcpy (&seeksegment, &demux->segment, sizeof (GstSegment));
2837 
2838   GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT,
2839       &demux->segment);
2840 
2841   /* Apply the seek to our segment */
2842   gst_segment_do_seek (&seeksegment, rate, format, flags,
2843       start_type, start, stop_type, stop, &update);
2844 
2845   GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT,
2846       &seeksegment);
2847 
2848   if (flush || seeksegment.position != demux->segment.position) {
2849     /* Do the actual seeking */
2850     guint64 offset = gst_flv_demux_find_offset (demux, &seeksegment, flags);
2851 
2852     GST_DEBUG_OBJECT (demux, "generating an upstream seek at position %"
2853         G_GUINT64_FORMAT, offset);
2854     ret = gst_pad_push_event (demux->sinkpad,
2855         gst_event_new_seek (seeksegment.rate, GST_FORMAT_BYTES,
2856             flags | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET,
2857             offset, GST_SEEK_TYPE_NONE, 0));
2858     if (G_UNLIKELY (!ret)) {
2859       GST_WARNING_OBJECT (demux, "upstream seek failed");
2860     }
2861 
2862     gst_flow_combiner_reset (demux->flowcombiner);
2863     /* Tell all the stream we moved to a different position (discont) */
2864     demux->audio_need_discont = TRUE;
2865     demux->video_need_discont = TRUE;
2866   } else {
2867     ret = TRUE;
2868   }
2869 
2870   if (ret) {
2871     /* Ok seek succeeded, take the newly configured segment */
2872     memcpy (&demux->segment, &seeksegment, sizeof (GstSegment));
2873 
2874     /* Tell all the stream a new segment is needed */
2875     demux->audio_need_segment = TRUE;
2876     demux->video_need_segment = TRUE;
2877     /* Clean any potential newsegment event kept for the streams. The first
2878      * stream needing a new segment will create a new one. */
2879     if (G_UNLIKELY (demux->new_seg_event)) {
2880       gst_event_unref (demux->new_seg_event);
2881       demux->new_seg_event = NULL;
2882     }
2883     GST_DEBUG_OBJECT (demux, "preparing newsegment from %"
2884         GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
2885         GST_TIME_ARGS (demux->segment.start),
2886         GST_TIME_ARGS (demux->segment.stop));
2887     demux->new_seg_event = gst_event_new_segment (&demux->segment);
2888     gst_event_unref (event);
2889   } else {
2890     ret = gst_pad_push_event (demux->sinkpad, event);
2891   }
2892 
2893   return ret;
2894 
2895 /* ERRORS */
2896 wrong_format:
2897   {
2898     GST_WARNING_OBJECT (demux, "we only support seeking in TIME format");
2899     gst_event_unref (event);
2900     return FALSE;
2901   }
2902 }
2903 
2904 static gboolean
gst_flv_demux_handle_seek_push(GstFlvDemux * demux,GstEvent * event)2905 gst_flv_demux_handle_seek_push (GstFlvDemux * demux, GstEvent * event)
2906 {
2907   GstFormat format;
2908 
2909   gst_event_parse_seek (event, NULL, &format, NULL, NULL, NULL, NULL, NULL);
2910 
2911   if (format != GST_FORMAT_TIME) {
2912     GST_WARNING_OBJECT (demux, "we only support seeking in TIME format");
2913     gst_event_unref (event);
2914     return FALSE;
2915   }
2916 
2917   /* First try upstream */
2918   if (gst_pad_push_event (demux->sinkpad, gst_event_ref (event))) {
2919     GST_DEBUG_OBJECT (demux, "Upstream successfully seeked");
2920     gst_event_unref (event);
2921     return TRUE;
2922   }
2923 
2924   if (!demux->indexed) {
2925     guint64 seek_offset = 0;
2926     gboolean building_index;
2927 
2928     GST_OBJECT_LOCK (demux);
2929     /* handle the seek in the chain function */
2930     demux->seeking = TRUE;
2931     demux->state = FLV_STATE_SEEK;
2932 
2933     /* copy the event */
2934     if (demux->seek_event)
2935       gst_event_unref (demux->seek_event);
2936     demux->seek_event = gst_event_ref (event);
2937 
2938     /* set the building_index flag so that only one thread can setup the
2939      * structures for index seeking. */
2940     building_index = demux->building_index;
2941     if (!building_index) {
2942       demux->building_index = TRUE;
2943       if (!demux->file_size
2944           && !gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES,
2945               &demux->file_size)) {
2946         GST_WARNING_OBJECT (demux, "Failed to query upstream file size");
2947         GST_OBJECT_UNLOCK (demux);
2948         return FALSE;
2949       }
2950 
2951       /* we hope the last tag is a scriptdataobject containing an index
2952        * the size of the last tag is given in the last guint32 bits
2953        * then we seek to the beginning of the tag, parse it and hopefully obtain an index */
2954       seek_offset = demux->file_size - sizeof (guint32);
2955       GST_DEBUG_OBJECT (demux,
2956           "File size obtained, seeking to %" G_GUINT64_FORMAT, seek_offset);
2957     }
2958     GST_OBJECT_UNLOCK (demux);
2959 
2960     if (!building_index) {
2961       GST_INFO_OBJECT (demux, "Seeking to last 4 bytes at %" G_GUINT64_FORMAT,
2962           seek_offset);
2963       return flv_demux_seek_to_offset (demux, seek_offset);
2964     }
2965 
2966     /* FIXME: we have to always return true so that we don't block the seek
2967      * thread.
2968      * Note: maybe it is OK to return true if we're still building the index */
2969     return TRUE;
2970   }
2971 
2972   return flv_demux_handle_seek_push (demux, event);
2973 }
2974 
2975 static gboolean
gst_flv_demux_handle_seek_pull(GstFlvDemux * demux,GstEvent * event,gboolean seeking)2976 gst_flv_demux_handle_seek_pull (GstFlvDemux * demux, GstEvent * event,
2977     gboolean seeking)
2978 {
2979   GstFormat format;
2980   GstSeekFlags flags;
2981   GstSeekType start_type, stop_type;
2982   gint64 start, stop;
2983   gdouble rate;
2984   gboolean update, flush, ret = FALSE;
2985   GstSegment seeksegment;
2986 
2987   gst_event_parse_seek (event, &rate, &format, &flags,
2988       &start_type, &start, &stop_type, &stop);
2989 
2990   if (format != GST_FORMAT_TIME)
2991     goto wrong_format;
2992 
2993   /* mark seeking thread entering flushing/pausing */
2994   GST_OBJECT_LOCK (demux);
2995   if (seeking)
2996     demux->seeking = seeking;
2997   GST_OBJECT_UNLOCK (demux);
2998 
2999   flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
3000 
3001   if (flush) {
3002     /* Flush start up and downstream to make sure data flow and loops are
3003        idle */
3004     gst_flv_demux_push_src_event (demux, gst_event_new_flush_start ());
3005     gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ());
3006   } else {
3007     /* Pause the pulling task */
3008     gst_pad_pause_task (demux->sinkpad);
3009   }
3010 
3011   /* Take the stream lock */
3012   GST_PAD_STREAM_LOCK (demux->sinkpad);
3013 
3014   if (flush) {
3015     /* Stop flushing upstream we need to pull */
3016     gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop (TRUE));
3017   }
3018 
3019   /* Work on a copy until we are sure the seek succeeded. */
3020   memcpy (&seeksegment, &demux->segment, sizeof (GstSegment));
3021 
3022   GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT,
3023       &demux->segment);
3024 
3025   /* Apply the seek to our segment */
3026   gst_segment_do_seek (&seeksegment, rate, format, flags,
3027       start_type, start, stop_type, stop, &update);
3028 
3029   GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT,
3030       &seeksegment);
3031 
3032   if (flush || seeksegment.position != demux->segment.position) {
3033     /* Do the actual seeking */
3034     /* index is reliable if it is complete or we do not go to far ahead */
3035     if (seeking && !demux->indexed &&
3036         seeksegment.position > demux->index_max_time + 10 * GST_SECOND) {
3037       GST_DEBUG_OBJECT (demux, "delaying seek to post-scan; "
3038           " index only up to %" GST_TIME_FORMAT,
3039           GST_TIME_ARGS (demux->index_max_time));
3040       /* stop flushing for now */
3041       if (flush)
3042         gst_flv_demux_push_src_event (demux, gst_event_new_flush_stop (TRUE));
3043       /* delegate scanning and index building to task thread to avoid
3044        * occupying main (UI) loop */
3045       if (demux->seek_event)
3046         gst_event_unref (demux->seek_event);
3047       demux->seek_event = gst_event_ref (event);
3048       demux->seek_time = seeksegment.position;
3049       demux->state = FLV_STATE_SEEK;
3050       /* do not know about succes yet, but we did care and handled it */
3051       ret = TRUE;
3052       goto exit;
3053     }
3054 
3055     /* now index should be as reliable as it can be for current purpose */
3056     gst_flv_demux_move_to_offset (demux,
3057         gst_flv_demux_find_offset (demux, &seeksegment, flags), TRUE);
3058     ret = TRUE;
3059   } else {
3060     ret = TRUE;
3061   }
3062 
3063   if (flush) {
3064     /* Stop flushing, the sinks are at time 0 now */
3065     gst_flv_demux_push_src_event (demux, gst_event_new_flush_stop (TRUE));
3066   }
3067 
3068   if (ret) {
3069     /* Ok seek succeeded, take the newly configured segment */
3070     memcpy (&demux->segment, &seeksegment, sizeof (GstSegment));
3071 
3072     /* Notify about the start of a new segment */
3073     if (demux->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
3074       gst_element_post_message (GST_ELEMENT (demux),
3075           gst_message_new_segment_start (GST_OBJECT (demux),
3076               demux->segment.format, demux->segment.position));
3077     }
3078 
3079     gst_flow_combiner_reset (demux->flowcombiner);
3080     /* Tell all the stream a new segment is needed */
3081     demux->audio_need_segment = TRUE;
3082     demux->video_need_segment = TRUE;
3083     /* Clean any potential newsegment event kept for the streams. The first
3084      * stream needing a new segment will create a new one. */
3085     if (G_UNLIKELY (demux->new_seg_event)) {
3086       gst_event_unref (demux->new_seg_event);
3087       demux->new_seg_event = NULL;
3088     }
3089     GST_DEBUG_OBJECT (demux, "preparing newsegment from %"
3090         GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
3091         GST_TIME_ARGS (demux->segment.start),
3092         GST_TIME_ARGS (demux->segment.stop));
3093     demux->new_seg_event = gst_event_new_segment (&demux->segment);
3094   }
3095 
3096 exit:
3097   GST_OBJECT_LOCK (demux);
3098   seeking = demux->seeking && !seeking;
3099   demux->seeking = FALSE;
3100   GST_OBJECT_UNLOCK (demux);
3101 
3102   /* if we detect an external seek having started (and possibly already having
3103    * flushed), do not restart task to give it a chance.
3104    * Otherwise external one's flushing will take care to pause task */
3105   if (seeking) {
3106     gst_pad_pause_task (demux->sinkpad);
3107   } else {
3108     gst_pad_start_task (demux->sinkpad,
3109         (GstTaskFunction) gst_flv_demux_loop, demux->sinkpad, NULL);
3110   }
3111 
3112   GST_PAD_STREAM_UNLOCK (demux->sinkpad);
3113 
3114   gst_event_unref (event);
3115   return ret;
3116 
3117   /* ERRORS */
3118 wrong_format:
3119   {
3120     GST_WARNING_OBJECT (demux, "we only support seeking in TIME format");
3121     gst_event_unref (event);
3122     return ret;
3123   }
3124 }
3125 
3126 /* If we can pull that's prefered */
3127 static gboolean
gst_flv_demux_sink_activate(GstPad * sinkpad,GstObject * parent)3128 gst_flv_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
3129 {
3130   GstQuery *query;
3131   gboolean pull_mode;
3132 
3133   query = gst_query_new_scheduling ();
3134 
3135   if (!gst_pad_peer_query (sinkpad, query)) {
3136     gst_query_unref (query);
3137     goto activate_push;
3138   }
3139 
3140   pull_mode = gst_query_has_scheduling_mode_with_flags (query,
3141       GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
3142   gst_query_unref (query);
3143 
3144   if (!pull_mode)
3145     goto activate_push;
3146 
3147   GST_DEBUG_OBJECT (sinkpad, "activating pull");
3148   return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
3149 
3150 activate_push:
3151   {
3152     GST_DEBUG_OBJECT (sinkpad, "activating push");
3153     return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
3154   }
3155 }
3156 
3157 static gboolean
gst_flv_demux_sink_activate_mode(GstPad * sinkpad,GstObject * parent,GstPadMode mode,gboolean active)3158 gst_flv_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
3159     GstPadMode mode, gboolean active)
3160 {
3161   gboolean res;
3162   GstFlvDemux *demux;
3163 
3164   demux = GST_FLV_DEMUX (parent);
3165 
3166   switch (mode) {
3167     case GST_PAD_MODE_PUSH:
3168       demux->random_access = FALSE;
3169       res = TRUE;
3170       break;
3171     case GST_PAD_MODE_PULL:
3172       if (active) {
3173         demux->random_access = TRUE;
3174         res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_flv_demux_loop,
3175             sinkpad, NULL);
3176       } else {
3177         demux->random_access = FALSE;
3178         res = gst_pad_stop_task (sinkpad);
3179       }
3180       break;
3181     default:
3182       res = FALSE;
3183       break;
3184   }
3185   return res;
3186 }
3187 
3188 static gboolean
gst_flv_demux_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)3189 gst_flv_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
3190 {
3191   GstFlvDemux *demux;
3192   gboolean ret = FALSE;
3193 
3194   demux = GST_FLV_DEMUX (parent);
3195 
3196   GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event));
3197 
3198   switch (GST_EVENT_TYPE (event)) {
3199     case GST_EVENT_FLUSH_START:
3200       GST_DEBUG_OBJECT (demux, "trying to force chain function to exit");
3201       demux->flushing = TRUE;
3202       ret = gst_flv_demux_push_src_event (demux, event);
3203       break;
3204     case GST_EVENT_FLUSH_STOP:
3205       GST_DEBUG_OBJECT (demux, "flushing FLV demuxer");
3206       gst_flv_demux_flush (demux, TRUE);
3207       ret = gst_flv_demux_push_src_event (demux, event);
3208       break;
3209     case GST_EVENT_EOS:
3210     {
3211       GstIndex *index;
3212 
3213       GST_DEBUG_OBJECT (demux, "received EOS");
3214 
3215       index = gst_flv_demux_get_index (GST_ELEMENT (demux));
3216 
3217       if (index) {
3218         GST_DEBUG_OBJECT (demux, "committing index");
3219         gst_index_commit (index, demux->index_id);
3220         gst_object_unref (index);
3221       }
3222 
3223       if (!demux->audio_pad && !demux->video_pad) {
3224         GST_ELEMENT_ERROR (demux, STREAM, FAILED,
3225             ("Internal data stream error."), ("Got EOS before any data"));
3226         gst_event_unref (event);
3227       } else {
3228         if (!demux->no_more_pads) {
3229           gst_element_no_more_pads (GST_ELEMENT (demux));
3230           demux->no_more_pads = TRUE;
3231         }
3232 
3233         if (!gst_flv_demux_push_src_event (demux, event))
3234           GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
3235       }
3236       ret = TRUE;
3237       break;
3238     }
3239     case GST_EVENT_SEGMENT:
3240     {
3241       GstSegment in_segment;
3242 
3243       GST_DEBUG_OBJECT (demux, "received new segment");
3244 
3245       gst_event_copy_segment (event, &in_segment);
3246 
3247       if (in_segment.format == GST_FORMAT_TIME) {
3248         /* time segment, this is perfect, copy over the values. */
3249         memcpy (&demux->segment, &in_segment, sizeof (in_segment));
3250 
3251         GST_DEBUG_OBJECT (demux, "NEWSEGMENT: %" GST_SEGMENT_FORMAT,
3252             &demux->segment);
3253 
3254         /* and forward */
3255         ret = gst_flv_demux_push_src_event (demux, event);
3256       } else {
3257         /* non-time format */
3258         demux->audio_need_segment = TRUE;
3259         demux->video_need_segment = TRUE;
3260         ret = TRUE;
3261         gst_event_unref (event);
3262         if (demux->new_seg_event) {
3263           gst_event_unref (demux->new_seg_event);
3264           demux->new_seg_event = NULL;
3265         }
3266       }
3267       gst_flow_combiner_reset (demux->flowcombiner);
3268       break;
3269     }
3270     default:
3271       ret = gst_pad_event_default (pad, parent, event);
3272       break;
3273   }
3274 
3275   return ret;
3276 }
3277 
3278 static gboolean
gst_flv_demux_src_event(GstPad * pad,GstObject * parent,GstEvent * event)3279 gst_flv_demux_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
3280 {
3281   GstFlvDemux *demux;
3282   gboolean ret = FALSE;
3283 
3284   demux = GST_FLV_DEMUX (parent);
3285 
3286   GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event));
3287 
3288   switch (GST_EVENT_TYPE (event)) {
3289     case GST_EVENT_SEEK:
3290       /* Try to push upstream first */
3291       gst_event_ref (event);
3292       ret = gst_pad_push_event (demux->sinkpad, event);
3293       if (ret) {
3294         gst_event_unref (event);
3295         break;
3296       }
3297       if (demux->random_access) {
3298         ret = gst_flv_demux_handle_seek_pull (demux, event, TRUE);
3299       } else {
3300         ret = gst_flv_demux_handle_seek_push (demux, event);
3301       }
3302       break;
3303     default:
3304       ret = gst_pad_push_event (demux->sinkpad, event);
3305       break;
3306   }
3307 
3308   return ret;
3309 }
3310 
3311 static gboolean
gst_flv_demux_query(GstPad * pad,GstObject * parent,GstQuery * query)3312 gst_flv_demux_query (GstPad * pad, GstObject * parent, GstQuery * query)
3313 {
3314   gboolean res = TRUE;
3315   GstFlvDemux *demux;
3316 
3317   demux = GST_FLV_DEMUX (parent);
3318 
3319   switch (GST_QUERY_TYPE (query)) {
3320     case GST_QUERY_DURATION:
3321     {
3322       GstFormat format;
3323 
3324       gst_query_parse_duration (query, &format, NULL);
3325 
3326       /* duration is time only */
3327       if (format != GST_FORMAT_TIME) {
3328         GST_DEBUG_OBJECT (demux, "duration query only supported for time "
3329             "format");
3330         res = FALSE;
3331         goto beach;
3332       }
3333 
3334       /* Try to push upstream first */
3335       res = gst_pad_peer_query (demux->sinkpad, query);
3336       if (res)
3337         goto beach;
3338 
3339       GST_DEBUG_OBJECT (pad, "duration query, replying %" GST_TIME_FORMAT,
3340           GST_TIME_ARGS (demux->duration));
3341 
3342       gst_query_set_duration (query, GST_FORMAT_TIME, demux->duration);
3343       res = TRUE;
3344       break;
3345     }
3346     case GST_QUERY_POSITION:
3347     {
3348       GstFormat format;
3349 
3350       gst_query_parse_position (query, &format, NULL);
3351 
3352       /* position is time only */
3353       if (format != GST_FORMAT_TIME) {
3354         GST_DEBUG_OBJECT (demux, "position query only supported for time "
3355             "format");
3356         res = FALSE;
3357         goto beach;
3358       }
3359 
3360       GST_DEBUG_OBJECT (pad, "position query, replying %" GST_TIME_FORMAT,
3361           GST_TIME_ARGS (demux->segment.position));
3362 
3363       gst_query_set_position (query, GST_FORMAT_TIME, demux->segment.position);
3364 
3365       break;
3366     }
3367 
3368     case GST_QUERY_SEEKING:{
3369       GstFormat fmt;
3370 
3371       gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
3372 
3373       /* First ask upstream */
3374       if (fmt == GST_FORMAT_TIME && gst_pad_peer_query (demux->sinkpad, query)) {
3375         gboolean seekable;
3376 
3377         gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
3378         if (seekable) {
3379           res = TRUE;
3380           break;
3381         }
3382       }
3383       res = TRUE;
3384       /* FIXME, check index this way is not thread safe */
3385       if (fmt != GST_FORMAT_TIME || !demux->index) {
3386         gst_query_set_seeking (query, fmt, FALSE, -1, -1);
3387       } else if (demux->random_access) {
3388         gst_query_set_seeking (query, GST_FORMAT_TIME, TRUE, 0,
3389             demux->duration);
3390       } else {
3391         GstQuery *peerquery = gst_query_new_seeking (GST_FORMAT_BYTES);
3392         gboolean seekable = gst_pad_peer_query (demux->sinkpad, peerquery);
3393 
3394         if (seekable)
3395           gst_query_parse_seeking (peerquery, NULL, &seekable, NULL, NULL);
3396         gst_query_unref (peerquery);
3397 
3398         if (seekable)
3399           gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0,
3400               demux->duration);
3401         else
3402           gst_query_set_seeking (query, GST_FORMAT_TIME, FALSE, -1, -1);
3403       }
3404       break;
3405     }
3406     case GST_QUERY_SEGMENT:
3407     {
3408       GstFormat format;
3409       gint64 start, stop;
3410 
3411       format = demux->segment.format;
3412 
3413       start =
3414           gst_segment_to_stream_time (&demux->segment, format,
3415           demux->segment.start);
3416       if ((stop = demux->segment.stop) == -1)
3417         stop = demux->segment.duration;
3418       else
3419         stop = gst_segment_to_stream_time (&demux->segment, format, stop);
3420 
3421       gst_query_set_segment (query, demux->segment.rate, format, start, stop);
3422       res = TRUE;
3423       break;
3424     }
3425     case GST_QUERY_LATENCY:
3426     default:
3427       res = gst_pad_query_default (pad, parent, query);
3428       break;
3429   }
3430 
3431 beach:
3432 
3433   return res;
3434 }
3435 
3436 static GstStateChangeReturn
gst_flv_demux_change_state(GstElement * element,GstStateChange transition)3437 gst_flv_demux_change_state (GstElement * element, GstStateChange transition)
3438 {
3439   GstFlvDemux *demux;
3440   GstStateChangeReturn ret;
3441 
3442   demux = GST_FLV_DEMUX (element);
3443 
3444   switch (transition) {
3445     case GST_STATE_CHANGE_READY_TO_PAUSED:
3446       /* If this is our own index destroy it as the
3447        * old entries might be wrong for the new stream */
3448       if (demux->own_index) {
3449         gst_object_unref (demux->index);
3450         demux->index = NULL;
3451         demux->own_index = FALSE;
3452       }
3453 
3454       /* If no index was created, generate one */
3455       if (G_UNLIKELY (!demux->index)) {
3456         GST_DEBUG_OBJECT (demux, "no index provided creating our own");
3457 
3458         demux->index = g_object_new (gst_mem_index_get_type (), NULL);
3459 
3460         gst_index_get_writer_id (demux->index, GST_OBJECT (demux),
3461             &demux->index_id);
3462         demux->own_index = TRUE;
3463       }
3464       gst_flv_demux_cleanup (demux);
3465       break;
3466     default:
3467       break;
3468   }
3469 
3470   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3471   if (ret == GST_STATE_CHANGE_FAILURE)
3472     return ret;
3473 
3474   switch (transition) {
3475     case GST_STATE_CHANGE_PAUSED_TO_READY:
3476       gst_flv_demux_cleanup (demux);
3477       break;
3478     default:
3479       break;
3480   }
3481 
3482   return ret;
3483 }
3484 
3485 #if 0
3486 static void
3487 gst_flv_demux_set_index (GstElement * element, GstIndex * index)
3488 {
3489   GstFlvDemux *demux = GST_FLV_DEMUX (element);
3490   GstIndex *old_index;
3491 
3492   GST_OBJECT_LOCK (demux);
3493 
3494   old_index = demux->index;
3495 
3496   if (index) {
3497     demux->index = gst_object_ref (index);
3498     demux->own_index = FALSE;
3499   } else
3500     demux->index = NULL;
3501 
3502   if (old_index)
3503     gst_object_unref (demux->index);
3504 
3505   gst_object_ref (index);
3506 
3507   GST_OBJECT_UNLOCK (demux);
3508 
3509   /* object lock might be taken again */
3510   if (index)
3511     gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
3512 
3513   GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT, demux->index);
3514 
3515   gst_object_unref (index);
3516 }
3517 #endif
3518 
3519 static GstIndex *
gst_flv_demux_get_index(GstElement * element)3520 gst_flv_demux_get_index (GstElement * element)
3521 {
3522   GstIndex *result = NULL;
3523 
3524   GstFlvDemux *demux = GST_FLV_DEMUX (element);
3525 
3526   GST_OBJECT_LOCK (demux);
3527   if (demux->index)
3528     result = gst_object_ref (demux->index);
3529   GST_OBJECT_UNLOCK (demux);
3530 
3531   return result;
3532 }
3533 
3534 static void
gst_flv_demux_dispose(GObject * object)3535 gst_flv_demux_dispose (GObject * object)
3536 {
3537   GstFlvDemux *demux = GST_FLV_DEMUX (object);
3538 
3539   GST_DEBUG_OBJECT (demux, "disposing FLV demuxer");
3540 
3541   if (demux->adapter) {
3542     gst_adapter_clear (demux->adapter);
3543     g_object_unref (demux->adapter);
3544     demux->adapter = NULL;
3545   }
3546 
3547   if (demux->taglist) {
3548     gst_tag_list_unref (demux->taglist);
3549     demux->taglist = NULL;
3550   }
3551 
3552   if (demux->audio_tags) {
3553     gst_tag_list_unref (demux->audio_tags);
3554     demux->audio_tags = NULL;
3555   }
3556 
3557   if (demux->video_tags) {
3558     gst_tag_list_unref (demux->video_tags);
3559     demux->video_tags = NULL;
3560   }
3561 
3562   if (demux->flowcombiner) {
3563     gst_flow_combiner_free (demux->flowcombiner);
3564     demux->flowcombiner = NULL;
3565   }
3566 
3567   if (demux->new_seg_event) {
3568     gst_event_unref (demux->new_seg_event);
3569     demux->new_seg_event = NULL;
3570   }
3571 
3572   if (demux->audio_codec_data) {
3573     gst_buffer_unref (demux->audio_codec_data);
3574     demux->audio_codec_data = NULL;
3575   }
3576 
3577   if (demux->video_codec_data) {
3578     gst_buffer_unref (demux->video_codec_data);
3579     demux->video_codec_data = NULL;
3580   }
3581 
3582   if (demux->audio_pad) {
3583     gst_object_unref (demux->audio_pad);
3584     demux->audio_pad = NULL;
3585   }
3586 
3587   if (demux->video_pad) {
3588     gst_object_unref (demux->video_pad);
3589     demux->video_pad = NULL;
3590   }
3591 
3592   if (demux->index) {
3593     gst_object_unref (demux->index);
3594     demux->index = NULL;
3595   }
3596 
3597   if (demux->times) {
3598     g_array_free (demux->times, TRUE);
3599     demux->times = NULL;
3600   }
3601 
3602   if (demux->filepositions) {
3603     g_array_free (demux->filepositions, TRUE);
3604     demux->filepositions = NULL;
3605   }
3606 
3607   GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
3608 }
3609 
3610 static void
gst_flv_demux_class_init(GstFlvDemuxClass * klass)3611 gst_flv_demux_class_init (GstFlvDemuxClass * klass)
3612 {
3613   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
3614   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3615 
3616   gobject_class->dispose = gst_flv_demux_dispose;
3617 
3618   gstelement_class->change_state =
3619       GST_DEBUG_FUNCPTR (gst_flv_demux_change_state);
3620 
3621 #if 0
3622   gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_flv_demux_set_index);
3623   gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_flv_demux_get_index);
3624 #endif
3625 
3626   gst_element_class_add_static_pad_template (gstelement_class,
3627       &flv_sink_template);
3628   gst_element_class_add_static_pad_template (gstelement_class,
3629       &audio_src_template);
3630   gst_element_class_add_static_pad_template (gstelement_class,
3631       &video_src_template);
3632   gst_element_class_set_static_metadata (gstelement_class, "FLV Demuxer",
3633       "Codec/Demuxer", "Demux FLV feeds into digital streams",
3634       "Julien Moutte <julien@moutte.net>");
3635 }
3636 
3637 static void
gst_flv_demux_init(GstFlvDemux * demux)3638 gst_flv_demux_init (GstFlvDemux * demux)
3639 {
3640   demux->sinkpad =
3641       gst_pad_new_from_static_template (&flv_sink_template, "sink");
3642 
3643   gst_pad_set_event_function (demux->sinkpad,
3644       GST_DEBUG_FUNCPTR (gst_flv_demux_sink_event));
3645   gst_pad_set_chain_function (demux->sinkpad,
3646       GST_DEBUG_FUNCPTR (gst_flv_demux_chain));
3647   gst_pad_set_activate_function (demux->sinkpad,
3648       GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate));
3649   gst_pad_set_activatemode_function (demux->sinkpad,
3650       GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate_mode));
3651 
3652   gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
3653 
3654   demux->adapter = gst_adapter_new ();
3655   demux->flowcombiner = gst_flow_combiner_new ();
3656 
3657   demux->own_index = FALSE;
3658 
3659   GST_OBJECT_FLAG_SET (demux, GST_ELEMENT_FLAG_INDEXABLE);
3660 
3661   gst_flv_demux_cleanup (demux);
3662 }
3663 
3664 static gboolean
plugin_init(GstPlugin * plugin)3665 plugin_init (GstPlugin * plugin)
3666 {
3667   GST_DEBUG_CATEGORY_INIT (flvdemux_debug, "flvdemux", 0, "FLV demuxer");
3668 
3669   if (!gst_element_register (plugin, "flvdemux", GST_RANK_PRIMARY,
3670           gst_flv_demux_get_type ()) ||
3671       !gst_element_register (plugin, "flvmux", GST_RANK_PRIMARY,
3672           gst_flv_mux_get_type ()))
3673     return FALSE;
3674 
3675   return TRUE;
3676 }
3677 
3678 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
3679     flv, "FLV muxing and demuxing plugin",
3680     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
3681