2 * Copyright (C) <2007> Julien Moutte <julien@moutte.net>
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.
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.
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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-flvdemux
23 * flvdemux demuxes an FLV file into the different contained streams.
26 * <title>Example launch line</title>
28 * gst-launch -v filesrc location=/path/to/flv ! flvdemux ! audioconvert ! autoaudiosink
29 * ]| This pipeline demuxes an FLV file and outputs the contained raw audio streams.
37 #include "gstflvdemux.h"
38 #include "gstflvparse.h"
39 #include "gstflvmux.h"
42 #include <gst/base/gstbytereader.h>
44 static GstStaticPadTemplate flv_sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
47 GST_STATIC_CAPS ("video/x-flv")
50 static GstStaticPadTemplate audio_src_template =
51 GST_STATIC_PAD_TEMPLATE ("audio",
56 static GstStaticPadTemplate video_src_template =
57 GST_STATIC_PAD_TEMPLATE ("video",
62 GST_DEBUG_CATEGORY (flvdemux_debug);
63 #define GST_CAT_DEFAULT flvdemux_debug
65 GST_BOILERPLATE (GstFLVDemux, gst_flv_demux, GstElement, GST_TYPE_ELEMENT);
67 /* 9 bytes of header + 4 bytes of first previous tag size */
68 #define FLV_HEADER_SIZE 13
69 /* 1 byte of tag type + 3 bytes of tag data size */
70 #define FLV_TAG_TYPE_SIZE 4
72 static gboolean flv_demux_handle_seek_push (GstFLVDemux * demux,
74 static gboolean gst_flv_demux_handle_seek_pull (GstFLVDemux * demux,
75 GstEvent * event, gboolean seeking);
78 gst_flv_demux_flush (GstFLVDemux * demux, gboolean discont)
80 GST_DEBUG_OBJECT (demux, "flushing queued data in the FLV demuxer");
82 gst_adapter_clear (demux->adapter);
84 demux->audio_need_discont = TRUE;
85 demux->video_need_discont = TRUE;
87 demux->flushing = FALSE;
89 /* Only in push mode and if we're not during a seek */
90 if (!demux->random_access && demux->state != FLV_STATE_SEEK) {
91 /* After a flush we expect a tag_type */
92 demux->state = FLV_STATE_TAG_TYPE;
93 /* We reset the offset and will get one from first push */
99 gst_flv_demux_cleanup (GstFLVDemux * demux)
101 GST_DEBUG_OBJECT (demux, "cleaning up FLV demuxer");
103 demux->state = FLV_STATE_HEADER;
105 demux->flushing = FALSE;
106 demux->need_header = TRUE;
107 demux->audio_need_segment = TRUE;
108 demux->video_need_segment = TRUE;
109 demux->audio_need_discont = TRUE;
110 demux->video_need_discont = TRUE;
112 /* By default we consider them as linked */
113 demux->audio_linked = TRUE;
114 demux->video_linked = TRUE;
116 demux->has_audio = FALSE;
117 demux->has_video = FALSE;
118 demux->push_tags = FALSE;
119 demux->got_par = FALSE;
121 demux->indexed = FALSE;
122 demux->file_size = 0;
124 demux->index_max_pos = 0;
125 demux->index_max_time = 0;
127 demux->audio_start = demux->video_start = GST_CLOCK_TIME_NONE;
129 demux->no_more_pads = FALSE;
131 gst_segment_init (&demux->segment, GST_FORMAT_TIME);
133 demux->w = demux->h = 0;
134 demux->par_x = demux->par_y = 1;
135 demux->video_offset = 0;
136 demux->audio_offset = 0;
137 demux->offset = demux->cur_tag_offset = 0;
138 demux->tag_size = demux->tag_data_size = 0;
139 demux->duration = GST_CLOCK_TIME_NONE;
141 if (demux->new_seg_event) {
142 gst_event_unref (demux->new_seg_event);
143 demux->new_seg_event = NULL;
146 if (demux->close_seg_event) {
147 gst_event_unref (demux->close_seg_event);
148 demux->close_seg_event = NULL;
151 gst_adapter_clear (demux->adapter);
153 if (demux->audio_codec_data) {
154 gst_buffer_unref (demux->audio_codec_data);
155 demux->audio_codec_data = NULL;
158 if (demux->video_codec_data) {
159 gst_buffer_unref (demux->video_codec_data);
160 demux->video_codec_data = NULL;
163 if (demux->audio_pad) {
164 gst_element_remove_pad (GST_ELEMENT (demux), demux->audio_pad);
165 gst_object_unref (demux->audio_pad);
166 demux->audio_pad = NULL;
169 if (demux->video_pad) {
170 gst_element_remove_pad (GST_ELEMENT (demux), demux->video_pad);
171 gst_object_unref (demux->video_pad);
172 demux->video_pad = NULL;
176 g_array_free (demux->times, TRUE);
180 if (demux->filepositions) {
181 g_array_free (demux->filepositions, TRUE);
182 demux->filepositions = NULL;
187 * Create and push a flushing seek event upstream
190 flv_demux_seek_to_offset (GstFLVDemux * demux, guint64 offset)
195 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
198 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
199 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
200 GST_SEEK_TYPE_NONE, -1);
202 res = gst_pad_push_event (demux->sinkpad, event);
205 demux->offset = offset;
210 gst_flv_demux_chain (GstPad * pad, GstBuffer * buffer)
212 GstFlowReturn ret = GST_FLOW_OK;
213 GstFLVDemux *demux = NULL;
215 demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
217 GST_LOG_OBJECT (demux, "received buffer of %d bytes at offset %"
218 G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer));
220 if (G_UNLIKELY (GST_BUFFER_OFFSET (buffer) == 0)) {
221 GST_DEBUG_OBJECT (demux, "beginning of file, expect header");
222 demux->state = FLV_STATE_HEADER;
226 if (G_UNLIKELY (demux->offset == 0 && GST_BUFFER_OFFSET (buffer) != 0)) {
227 GST_DEBUG_OBJECT (demux, "offset was zero, synchronizing with buffer's");
228 demux->offset = GST_BUFFER_OFFSET (buffer);
231 gst_adapter_push (demux->adapter, buffer);
233 if (demux->seeking) {
234 demux->state = FLV_STATE_SEEK;
235 GST_OBJECT_LOCK (demux);
236 demux->seeking = FALSE;
237 GST_OBJECT_UNLOCK (demux);
241 if (G_UNLIKELY (ret != GST_FLOW_OK)) {
242 if (ret == GST_FLOW_NOT_LINKED && (demux->audio_linked
243 || demux->video_linked)) {
246 GST_DEBUG_OBJECT (demux, "got flow return %s", gst_flow_get_name (ret));
251 if (G_UNLIKELY (demux->flushing)) {
252 GST_DEBUG_OBJECT (demux, "we are now flushing, exiting parser loop");
253 ret = GST_FLOW_WRONG_STATE;
257 switch (demux->state) {
258 case FLV_STATE_HEADER:
260 if (gst_adapter_available (demux->adapter) >= FLV_HEADER_SIZE) {
263 buffer = gst_adapter_take_buffer (demux->adapter, FLV_HEADER_SIZE);
265 ret = gst_flv_parse_header (demux, buffer);
267 gst_buffer_unref (buffer);
268 demux->offset += FLV_HEADER_SIZE;
270 demux->state = FLV_STATE_TAG_TYPE;
276 case FLV_STATE_TAG_TYPE:
278 if (gst_adapter_available (demux->adapter) >= FLV_TAG_TYPE_SIZE) {
281 /* Remember the tag offset in bytes */
282 demux->cur_tag_offset = demux->offset;
284 buffer = gst_adapter_take_buffer (demux->adapter, FLV_TAG_TYPE_SIZE);
286 ret = gst_flv_parse_tag_type (demux, buffer);
288 gst_buffer_unref (buffer);
289 demux->offset += FLV_TAG_TYPE_SIZE;
291 /* last tag is not an index => no index/don't know where the index is
292 * seek back to the beginning */
293 if (demux->seek_event && demux->state != FLV_STATE_TAG_SCRIPT)
301 case FLV_STATE_TAG_VIDEO:
303 if (gst_adapter_available (demux->adapter) >= demux->tag_size) {
306 buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size);
308 ret = gst_flv_parse_tag_video (demux, buffer);
310 gst_buffer_unref (buffer);
311 demux->offset += demux->tag_size;
313 demux->state = FLV_STATE_TAG_TYPE;
319 case FLV_STATE_TAG_AUDIO:
321 if (gst_adapter_available (demux->adapter) >= demux->tag_size) {
324 buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size);
326 ret = gst_flv_parse_tag_audio (demux, buffer);
328 gst_buffer_unref (buffer);
329 demux->offset += demux->tag_size;
331 demux->state = FLV_STATE_TAG_TYPE;
337 case FLV_STATE_TAG_SCRIPT:
339 if (gst_adapter_available (demux->adapter) >= demux->tag_size) {
342 buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size);
344 ret = gst_flv_parse_tag_script (demux, buffer);
346 gst_buffer_unref (buffer);
347 demux->offset += demux->tag_size;
349 demux->state = FLV_STATE_TAG_TYPE;
351 /* if there's a seek event we're here for the index so if we don't have it
352 * we seek back to the beginning */
353 if (demux->seek_event) {
355 demux->state = FLV_STATE_SEEK;
371 if (!demux->indexed) {
372 if (demux->offset == demux->file_size - sizeof (guint32)) {
374 gst_adapter_take_buffer (demux->adapter, sizeof (guint32));
375 GstByteReader *reader = gst_byte_reader_new_from_buffer (buffer);
378 if (!gst_adapter_available (demux->adapter) >= sizeof (guint32)) {
383 demux->file_size - sizeof (guint32) -
384 gst_byte_reader_peek_uint32_be_unchecked (reader);
385 gst_byte_reader_free (reader);
386 gst_buffer_unref (buffer);
388 GST_INFO_OBJECT (demux,
389 "Seeking to beginning of last tag at %" G_GUINT64_FORMAT,
391 demux->state = FLV_STATE_TAG_TYPE;
392 flv_demux_seek_to_offset (demux, seek_offset);
398 GST_OBJECT_LOCK (demux);
399 event = demux->seek_event;
400 demux->seek_event = NULL;
401 GST_OBJECT_UNLOCK (demux);
403 /* calculate and perform seek */
404 if (!flv_demux_handle_seek_push (demux, event))
407 gst_event_unref (event);
408 demux->state = FLV_STATE_TAG_TYPE;
412 GST_DEBUG_OBJECT (demux, "unexpected demuxer state");
416 if (G_UNLIKELY (ret == GST_FLOW_NOT_LINKED)) {
417 /* If either audio or video is linked we return GST_FLOW_OK */
418 if (demux->audio_linked || demux->video_linked) {
423 gst_object_unref (demux);
430 GST_OBJECT_LOCK (demux);
431 demux->seeking = FALSE;
432 gst_event_unref (demux->seek_event);
433 demux->seek_event = NULL;
434 GST_OBJECT_UNLOCK (demux);
435 GST_WARNING_OBJECT (demux,
436 "failed to find an index, seeking back to beginning");
437 flv_demux_seek_to_offset (demux, 0);
442 GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("seek failed"));
443 return GST_FLOW_ERROR;
449 gst_flv_demux_pull_range (GstFLVDemux * demux, GstPad * pad, guint64 offset,
450 guint size, GstBuffer ** buffer)
454 ret = gst_pad_pull_range (pad, offset, size, buffer);
455 if (G_UNLIKELY (ret != GST_FLOW_OK)) {
456 GST_WARNING_OBJECT (demux,
457 "failed when pulling %d bytes from offset %" G_GUINT64_FORMAT ": %s",
458 size, offset, gst_flow_get_name (ret));
463 if (G_UNLIKELY (*buffer && GST_BUFFER_SIZE (*buffer) != size)) {
464 GST_WARNING_OBJECT (demux,
465 "partial pull got %d when expecting %d from offset %" G_GUINT64_FORMAT,
466 GST_BUFFER_SIZE (*buffer), size, offset);
467 gst_buffer_unref (*buffer);
468 ret = GST_FLOW_UNEXPECTED;
477 gst_flv_demux_pull_tag (GstPad * pad, GstFLVDemux * demux)
479 GstBuffer *buffer = NULL;
480 GstFlowReturn ret = GST_FLOW_OK;
482 /* Store tag offset */
483 demux->cur_tag_offset = demux->offset;
485 /* Get the first 4 bytes to identify tag type and size */
486 if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset,
487 FLV_TAG_TYPE_SIZE, &buffer)) != GST_FLOW_OK))
490 /* Identify tag type */
491 ret = gst_flv_parse_tag_type (demux, buffer);
493 gst_buffer_unref (buffer);
495 if (G_UNLIKELY (ret != GST_FLOW_OK))
498 /* Jump over tag type + size */
499 demux->offset += FLV_TAG_TYPE_SIZE;
501 /* Pull the whole tag */
502 if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset,
503 demux->tag_size, &buffer)) != GST_FLOW_OK))
506 switch (demux->state) {
507 case FLV_STATE_TAG_VIDEO:
508 ret = gst_flv_parse_tag_video (demux, buffer);
510 case FLV_STATE_TAG_AUDIO:
511 ret = gst_flv_parse_tag_audio (demux, buffer);
513 case FLV_STATE_TAG_SCRIPT:
514 ret = gst_flv_parse_tag_script (demux, buffer);
517 GST_WARNING_OBJECT (demux, "unexpected state %d", demux->state);
520 gst_buffer_unref (buffer);
522 /* Jump over that part we've just parsed */
523 demux->offset += demux->tag_size;
525 /* Make sure we reinitialize the tag size */
528 /* Ready for the next tag */
529 demux->state = FLV_STATE_TAG_TYPE;
531 if (G_UNLIKELY (ret == GST_FLOW_NOT_LINKED)) {
532 /* If either audio or video is linked we return GST_FLOW_OK */
533 if (demux->audio_linked || demux->video_linked) {
536 GST_WARNING_OBJECT (demux, "parsing this tag returned not-linked and "
537 "neither video nor audio are linked");
546 gst_flv_demux_pull_header (GstPad * pad, GstFLVDemux * demux)
548 GstBuffer *buffer = NULL;
549 GstFlowReturn ret = GST_FLOW_OK;
551 /* Get the first 9 bytes */
552 if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset,
553 FLV_HEADER_SIZE, &buffer)) != GST_FLOW_OK))
556 ret = gst_flv_parse_header (demux, buffer);
558 gst_buffer_unref (buffer);
560 /* Jump over the header now */
561 demux->offset += FLV_HEADER_SIZE;
562 demux->state = FLV_STATE_TAG_TYPE;
569 gst_flv_demux_seek_to_prev_keyframe (GstFLVDemux * demux)
575 gst_flv_demux_push_src_event (GstFLVDemux * demux, GstEvent * event)
579 if (demux->audio_pad)
580 ret |= gst_pad_push_event (demux->audio_pad, gst_event_ref (event));
582 if (demux->video_pad)
583 ret |= gst_pad_push_event (demux->video_pad, gst_event_ref (event));
585 gst_event_unref (event);
591 gst_flv_demux_create_index (GstFLVDemux * demux, gint64 pos, GstClockTime ts)
594 GstFormat fmt = GST_FORMAT_BYTES;
598 GstClockTime tag_time;
599 GstFlowReturn ret = GST_FLOW_OK;
601 if (G_UNLIKELY (!gst_pad_query_peer_duration (demux->sinkpad, &fmt, &size) ||
602 fmt != GST_FORMAT_BYTES))
605 GST_DEBUG_OBJECT (demux, "building index at %" G_GINT64_FORMAT
606 " looking for time %" GST_TIME_FORMAT, pos, GST_TIME_ARGS (ts));
608 old_offset = demux->offset;
611 while ((ret = gst_flv_demux_pull_range (demux, demux->sinkpad, demux->offset,
612 12, &buffer)) == GST_FLOW_OK) {
613 tag_time = gst_flv_parse_tag_timestamp (demux, TRUE, buffer, &tag_size);
615 gst_buffer_unref (buffer);
617 if (G_UNLIKELY (tag_time == GST_CLOCK_TIME_NONE || tag_time > ts))
620 demux->offset += tag_size;
623 if (ret == GST_FLOW_UNEXPECTED) {
624 /* file ran out, so mark we have complete index */
625 demux->indexed = TRUE;
630 demux->offset = old_offset;
636 gst_flv_demux_get_metadata (GstFLVDemux * demux)
639 GstFormat fmt = GST_FORMAT_BYTES;
640 size_t tag_size, size;
641 GstBuffer *buffer = NULL;
643 if (G_UNLIKELY (!gst_pad_query_peer_duration (demux->sinkpad, &fmt, &offset)
644 || fmt != GST_FORMAT_BYTES))
648 GST_DEBUG_OBJECT (demux, "upstream size: %" G_GINT64_FORMAT, offset);
649 if (G_UNLIKELY (offset < 4))
653 if (GST_FLOW_OK != gst_flv_demux_pull_range (demux, demux->sinkpad, offset,
657 tag_size = GST_READ_UINT32_BE (GST_BUFFER_DATA (buffer));
658 GST_DEBUG_OBJECT (demux, "last tag size: %d", tag_size);
659 gst_buffer_unref (buffer);
663 if (GST_FLOW_OK != gst_flv_demux_pull_range (demux, demux->sinkpad, offset,
667 /* a consistency check */
668 size = GST_READ_UINT24_BE (GST_BUFFER_DATA (buffer) + 1);
669 if (size != tag_size - 11) {
670 GST_DEBUG_OBJECT (demux, "tag size %d, expected %d, ",
671 "corrupt or truncated file", size, tag_size - 11);
675 /* try to update duration with timestamp in any case */
676 gst_flv_parse_tag_timestamp (demux, FALSE, buffer, &size);
678 /* maybe get some more metadata */
679 if (GST_BUFFER_DATA (buffer)[0] == 18) {
680 gst_buffer_unref (buffer);
682 GST_DEBUG_OBJECT (demux, "script tag, pulling it to parse");
684 if (GST_FLOW_OK == gst_flv_demux_pull_range (demux, demux->sinkpad, offset,
686 gst_flv_parse_tag_script (demux, buffer);
691 gst_buffer_unref (buffer);
697 gst_flv_demux_loop (GstPad * pad)
699 GstFLVDemux *demux = NULL;
700 GstFlowReturn ret = GST_FLOW_OK;
702 demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
704 if (demux->segment.rate >= 0) {
706 switch (demux->state) {
707 case FLV_STATE_TAG_TYPE:
708 ret = gst_flv_demux_pull_tag (pad, demux);
709 /* if we have seen real data, we probably passed a possible metadata
710 * header located at start. So if we do not yet have an index,
711 * try to pick up metadata (index, duration) at the end */
712 if (G_UNLIKELY (!demux->file_size && !demux->indexed &&
713 (demux->has_video || demux->has_audio)))
714 demux->file_size = gst_flv_demux_get_metadata (demux);
717 ret = GST_FLOW_UNEXPECTED;
720 /* seek issued with insufficient index;
721 * scan for index in task thread from current maximum offset to
722 * desired time and then perform seek */
723 /* TODO maybe some buffering message or so to indicate scan progress */
724 ret = gst_flv_demux_create_index (demux, demux->index_max_pos,
726 if (ret != GST_FLOW_OK)
728 /* position and state arranged by seek,
729 * also unrefs event */
730 gst_flv_demux_handle_seek_pull (demux, demux->seek_event, FALSE);
731 demux->seek_event = NULL;
734 ret = gst_flv_demux_pull_header (pad, demux);
735 /* index scans start after header */
736 demux->index_max_pos = demux->offset;
740 /* pause if something went wrong */
741 if (G_UNLIKELY (ret != GST_FLOW_OK))
744 /* check EOS condition */
745 if ((demux->segment.flags & GST_SEEK_FLAG_SEGMENT) &&
746 (demux->segment.stop != -1) &&
747 (demux->segment.last_stop >= demux->segment.stop)) {
748 ret = GST_FLOW_UNEXPECTED;
751 } else { /* Reverse playback */
753 switch (demux->state) {
754 case FLV_STATE_TAG_TYPE:
755 ret = gst_flv_demux_pull_tag (pad, demux);
756 /* When packet parsing returns UNEXPECTED that means we ve reached the
757 point where we want to go to the previous keyframe. This is either
758 the last FLV tag or the keyframe we used last time */
759 if (ret == GST_FLOW_UNEXPECTED) {
760 ret = gst_flv_demux_seek_to_prev_keyframe (demux);
761 demux->state = FLV_STATE_TAG_TYPE;
765 ret = gst_flv_demux_pull_header (pad, demux);
766 if (ret == GST_FLOW_OK)
767 gst_flv_demux_create_index (demux, demux->offset, G_MAXINT64);
770 /* pause if something went wrong */
771 if (G_UNLIKELY (ret != GST_FLOW_OK))
774 /* check EOS condition */
775 if (demux->segment.last_stop <= demux->segment.start) {
776 ret = GST_FLOW_UNEXPECTED;
781 gst_object_unref (demux);
787 const gchar *reason = gst_flow_get_name (ret);
789 GST_LOG_OBJECT (demux, "pausing task, reason %s", reason);
790 gst_pad_pause_task (pad);
792 if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
793 if (ret == GST_FLOW_UNEXPECTED) {
794 /* perform EOS logic */
795 if (!demux->no_more_pads) {
796 gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
797 demux->no_more_pads = TRUE;
800 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
803 /* for segment playback we need to post when (in stream time)
804 * we stopped, this is either stop (when set) or the duration. */
805 if ((stop = demux->segment.stop) == -1)
806 stop = demux->segment.duration;
808 if (demux->segment.rate >= 0) {
809 GST_LOG_OBJECT (demux, "Sending segment done, at end of segment");
810 gst_element_post_message (GST_ELEMENT_CAST (demux),
811 gst_message_new_segment_done (GST_OBJECT_CAST (demux),
812 GST_FORMAT_TIME, stop));
813 } else { /* Reverse playback */
814 GST_LOG_OBJECT (demux, "Sending segment done, at beginning of "
816 gst_element_post_message (GST_ELEMENT_CAST (demux),
817 gst_message_new_segment_done (GST_OBJECT_CAST (demux),
818 GST_FORMAT_TIME, demux->segment.start));
821 /* normal playback, send EOS to all linked pads */
822 if (!demux->no_more_pads) {
823 gst_element_no_more_pads (GST_ELEMENT (demux));
824 demux->no_more_pads = TRUE;
827 GST_LOG_OBJECT (demux, "Sending EOS, at end of stream");
828 if (!gst_flv_demux_push_src_event (demux, gst_event_new_eos ()))
829 GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
832 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
833 ("Internal data stream error."),
834 ("stream stopped, reason %s", reason));
835 gst_flv_demux_push_src_event (demux, gst_event_new_eos ());
838 gst_object_unref (demux);
844 gst_flv_demux_find_offset (GstFLVDemux * demux, GstSegment * segment)
848 GstIndexEntry *entry;
850 g_return_val_if_fail (segment != NULL, 0);
852 time = segment->start;
855 /* Let's check if we have an index entry for that seek time */
856 entry = gst_index_get_assoc_entry (demux->index, demux->index_id,
857 GST_INDEX_LOOKUP_BEFORE, GST_ASSOCIATION_FLAG_KEY_UNIT,
858 GST_FORMAT_TIME, time);
861 gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes);
862 gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
864 GST_DEBUG_OBJECT (demux, "found index entry for %" GST_TIME_FORMAT
865 " at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT,
866 GST_TIME_ARGS (segment->start), GST_TIME_ARGS (time), bytes);
868 /* Key frame seeking */
869 if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
870 /* Adjust the segment so that the keyframe fits in */
871 if (time < segment->start) {
872 segment->start = segment->time = time;
874 segment->last_stop = time;
877 GST_DEBUG_OBJECT (demux, "no index entry found for %" GST_TIME_FORMAT,
878 GST_TIME_ARGS (segment->start));
886 flv_demux_handle_seek_push (GstFLVDemux * demux, GstEvent * event)
890 GstSeekType start_type, stop_type;
893 gboolean update, flush, keyframe, ret;
894 GstSegment seeksegment;
896 gst_event_parse_seek (event, &rate, &format, &flags,
897 &start_type, &start, &stop_type, &stop);
899 if (format != GST_FORMAT_TIME)
902 flush = !!(flags & GST_SEEK_FLAG_FLUSH);
903 /* FIXME : the keyframe flag is never used ! */
904 keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
906 /* Work on a copy until we are sure the seek succeeded. */
907 memcpy (&seeksegment, &demux->segment, sizeof (GstSegment));
909 GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT,
912 /* Apply the seek to our segment */
913 gst_segment_set_seek (&seeksegment, rate, format, flags,
914 start_type, start, stop_type, stop, &update);
916 GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT,
919 if (flush || seeksegment.last_stop != demux->segment.last_stop) {
920 /* Do the actual seeking */
921 guint64 offset = gst_flv_demux_find_offset (demux, &seeksegment);
923 GST_DEBUG_OBJECT (demux, "generating an upstream seek at position %"
924 G_GUINT64_FORMAT, offset);
925 ret = gst_pad_push_event (demux->sinkpad,
926 gst_event_new_seek (seeksegment.rate, GST_FORMAT_BYTES,
927 seeksegment.flags | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET,
928 offset, GST_SEEK_TYPE_NONE, 0));
929 if (G_UNLIKELY (!ret)) {
930 GST_WARNING_OBJECT (demux, "upstream seek failed");
933 /* Tell all the stream we moved to a different position (discont) */
934 demux->audio_need_discont = TRUE;
935 demux->video_need_discont = TRUE;
941 /* Ok seek succeeded, take the newly configured segment */
942 memcpy (&demux->segment, &seeksegment, sizeof (GstSegment));
944 /* Tell all the stream a new segment is needed */
945 demux->audio_need_segment = TRUE;
946 demux->video_need_segment = TRUE;
947 /* Clean any potential newsegment event kept for the streams. The first
948 * stream needing a new segment will create a new one. */
949 if (G_UNLIKELY (demux->new_seg_event)) {
950 gst_event_unref (demux->new_seg_event);
951 demux->new_seg_event = NULL;
953 gst_event_unref (event);
955 ret = gst_pad_push_event (demux->sinkpad, event);
963 GST_WARNING_OBJECT (demux, "we only support seeking in TIME format");
964 return gst_pad_push_event (demux->sinkpad, event);
969 gst_flv_demux_handle_seek_push (GstFLVDemux * demux, GstEvent * event)
971 if (!demux->indexed) {
973 gboolean building_index;
976 GST_OBJECT_LOCK (demux);
977 /* handle the seek in the chain function */
978 demux->seeking = TRUE;
979 demux->state = FLV_STATE_SEEK;
982 if (demux->seek_event)
983 gst_event_unref (demux->seek_event);
984 demux->seek_event = gst_event_ref (event);
986 /* set the building_index flag so that only one thread can setup the
987 * structures for index seeking. */
988 building_index = demux->building_index;
989 if (!building_index) {
990 demux->building_index = TRUE;
991 fmt = GST_FORMAT_BYTES;
992 if (!demux->file_size
993 && !gst_pad_query_peer_duration (demux->sinkpad, &fmt,
994 &demux->file_size)) {
995 GST_WARNING_OBJECT (demux,
996 "Cannot obtain file size - %" G_GINT64_FORMAT ", format %u",
997 demux->file_size, fmt);
998 GST_OBJECT_UNLOCK (demux);
1002 /* we hope the last tag is a scriptdataobject containing an index
1003 * the size of the last tag is given in the last guint32 bits
1004 * then we seek to the beginning of the tag, parse it and hopefully obtain an index */
1005 seek_offset = demux->file_size - sizeof (guint32);
1006 GST_DEBUG_OBJECT (demux,
1007 "File size obtained, seeking to %" G_GUINT64_FORMAT, seek_offset);
1009 GST_OBJECT_UNLOCK (demux);
1011 if (!building_index) {
1012 GST_INFO_OBJECT (demux, "Seeking to last 4 bytes at %" G_GUINT64_FORMAT,
1014 return flv_demux_seek_to_offset (demux, seek_offset);
1017 /* FIXME: we have to always return true so that we don't block the seek
1019 * Note: maybe it is OK to return true if we're still building the index */
1023 return flv_demux_handle_seek_push (demux, event);
1027 gst_flv_demux_handle_seek_pull (GstFLVDemux * demux, GstEvent * event,
1032 GstSeekType start_type, stop_type;
1035 gboolean update, flush, keyframe, ret;
1036 GstSegment seeksegment;
1038 gst_event_parse_seek (event, &rate, &format, &flags,
1039 &start_type, &start, &stop_type, &stop);
1041 if (format != GST_FORMAT_TIME)
1044 /* mark seeking thread entering flushing/pausing */
1045 GST_OBJECT_LOCK (demux);
1047 demux->seeking = seeking;
1048 GST_OBJECT_UNLOCK (demux);
1050 flush = !!(flags & GST_SEEK_FLAG_FLUSH);
1051 /* FIXME : the keyframe flag is never used */
1052 keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
1055 /* Flush start up and downstream to make sure data flow and loops are
1057 gst_flv_demux_push_src_event (demux, gst_event_new_flush_start ());
1058 gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ());
1060 /* Pause the pulling task */
1061 gst_pad_pause_task (demux->sinkpad);
1064 /* Take the stream lock */
1065 GST_PAD_STREAM_LOCK (demux->sinkpad);
1068 /* Stop flushing upstream we need to pull */
1069 gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ());
1072 /* Work on a copy until we are sure the seek succeeded. */
1073 memcpy (&seeksegment, &demux->segment, sizeof (GstSegment));
1075 GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT,
1078 /* Apply the seek to our segment */
1079 gst_segment_set_seek (&seeksegment, rate, format, flags,
1080 start_type, start, stop_type, stop, &update);
1082 GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT,
1085 if (flush || seeksegment.last_stop != demux->segment.last_stop) {
1086 /* Do the actual seeking */
1087 /* index is reliable if it is complete or we do not go to far ahead */
1088 if (seeking && !demux->indexed &&
1089 seeksegment.last_stop > demux->index_max_time + 10 * GST_SECOND) {
1090 GST_DEBUG_OBJECT (demux, "delaying seek to post-scan; "
1091 " index only up to %" GST_TIME_FORMAT,
1092 GST_TIME_ARGS (demux->index_max_time));
1093 /* stop flushing for now */
1095 gst_flv_demux_push_src_event (demux, gst_event_new_flush_stop ());
1096 /* delegate scanning and index building to task thread to avoid
1097 * occupying main (UI) loop */
1098 if (demux->seek_event)
1099 gst_event_unref (demux->seek_event);
1100 demux->seek_event = gst_event_ref (event);
1101 demux->seek_time = seeksegment.last_stop;
1102 demux->state = FLV_STATE_SEEK;
1105 /* now index should be as reliable as it can be for current purpose */
1106 demux->offset = gst_flv_demux_find_offset (demux, &seeksegment);
1108 /* Tell all the stream we moved to a different position (discont) */
1109 demux->audio_need_discont = TRUE;
1110 demux->video_need_discont = TRUE;
1112 /* If we seeked at the beginning of the file parse the header again */
1113 if (G_UNLIKELY (!demux->offset)) {
1114 demux->state = FLV_STATE_HEADER;
1115 } else { /* or parse a tag */
1116 demux->state = FLV_STATE_TAG_TYPE;
1123 if (G_UNLIKELY (demux->close_seg_event)) {
1124 gst_event_unref (demux->close_seg_event);
1125 demux->close_seg_event = NULL;
1129 /* Stop flushing, the sinks are at time 0 now */
1130 gst_flv_demux_push_src_event (demux, gst_event_new_flush_stop ());
1132 GST_DEBUG_OBJECT (demux, "closing running segment %" GST_SEGMENT_FORMAT,
1135 /* Close the current segment for a linear playback */
1136 if (demux->segment.rate >= 0) {
1137 /* for forward playback, we played from start to last_stop */
1138 demux->close_seg_event = gst_event_new_new_segment (TRUE,
1139 demux->segment.rate, demux->segment.format,
1140 demux->segment.start, demux->segment.last_stop, demux->segment.time);
1144 if ((stop = demux->segment.stop) == -1)
1145 stop = demux->segment.duration;
1147 /* for reverse playback, we played from stop to last_stop. */
1148 demux->close_seg_event = gst_event_new_new_segment (TRUE,
1149 demux->segment.rate, demux->segment.format,
1150 demux->segment.last_stop, stop, demux->segment.last_stop);
1155 /* Ok seek succeeded, take the newly configured segment */
1156 memcpy (&demux->segment, &seeksegment, sizeof (GstSegment));
1158 /* Notify about the start of a new segment */
1159 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1160 gst_element_post_message (GST_ELEMENT (demux),
1161 gst_message_new_segment_start (GST_OBJECT (demux),
1162 demux->segment.format, demux->segment.last_stop));
1165 /* Tell all the stream a new segment is needed */
1166 demux->audio_need_segment = TRUE;
1167 demux->video_need_segment = TRUE;
1168 /* Clean any potential newsegment event kept for the streams. The first
1169 * stream needing a new segment will create a new one. */
1170 if (G_UNLIKELY (demux->new_seg_event)) {
1171 gst_event_unref (demux->new_seg_event);
1172 demux->new_seg_event = NULL;
1177 GST_OBJECT_LOCK (demux);
1178 seeking = demux->seeking && !seeking;
1179 demux->seeking = FALSE;
1180 GST_OBJECT_UNLOCK (demux);
1182 /* if we detect an external seek having started (and possibly already having
1183 * flushed), do not restart task to give it a chance.
1184 * Otherwise external one's flushing will take care to pause task */
1186 gst_pad_pause_task (demux->sinkpad);
1188 gst_pad_start_task (demux->sinkpad,
1189 (GstTaskFunction) gst_flv_demux_loop, demux->sinkpad);
1192 GST_PAD_STREAM_UNLOCK (demux->sinkpad);
1194 gst_event_unref (event);
1200 GST_WARNING_OBJECT (demux, "we only support seeking in TIME format");
1201 gst_event_unref (event);
1206 /* If we can pull that's prefered */
1208 gst_flv_demux_sink_activate (GstPad * sinkpad)
1210 if (gst_pad_check_pull_range (sinkpad)) {
1211 return gst_pad_activate_pull (sinkpad, TRUE);
1213 return gst_pad_activate_push (sinkpad, TRUE);
1217 /* This function gets called when we activate ourselves in push mode.
1218 * We cannot seek (ourselves) in the stream */
1220 gst_flv_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
1224 demux = GST_FLV_DEMUX (gst_pad_get_parent (sinkpad));
1226 demux->random_access = FALSE;
1228 gst_object_unref (demux);
1233 /* this function gets called when we activate ourselves in pull mode.
1234 * We can perform random access to the resource and we start a task
1235 * to start reading */
1237 gst_flv_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
1241 demux = GST_FLV_DEMUX (gst_pad_get_parent (sinkpad));
1244 demux->random_access = TRUE;
1245 gst_object_unref (demux);
1246 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_flv_demux_loop,
1249 demux->random_access = FALSE;
1250 gst_object_unref (demux);
1251 return gst_pad_stop_task (sinkpad);
1256 gst_flv_demux_sink_event (GstPad * pad, GstEvent * event)
1259 gboolean ret = FALSE;
1261 demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
1263 GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event));
1265 switch (GST_EVENT_TYPE (event)) {
1266 case GST_EVENT_FLUSH_START:
1267 GST_DEBUG_OBJECT (demux, "trying to force chain function to exit");
1268 demux->flushing = TRUE;
1269 ret = gst_flv_demux_push_src_event (demux, event);
1271 case GST_EVENT_FLUSH_STOP:
1272 GST_DEBUG_OBJECT (demux, "flushing FLV demuxer");
1273 gst_flv_demux_flush (demux, TRUE);
1274 ret = gst_flv_demux_push_src_event (demux, event);
1277 GST_DEBUG_OBJECT (demux, "received EOS");
1279 GST_DEBUG_OBJECT (demux, "committing index");
1280 gst_index_commit (demux->index, demux->index_id);
1282 if (!demux->no_more_pads) {
1283 gst_element_no_more_pads (GST_ELEMENT (demux));
1284 demux->no_more_pads = TRUE;
1287 if (!gst_flv_demux_push_src_event (demux, event))
1288 GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
1291 case GST_EVENT_NEWSEGMENT:
1295 gint64 start, stop, time;
1298 GST_DEBUG_OBJECT (demux, "received new segment");
1300 gst_event_parse_new_segment (event, &update, &rate, &format, &start,
1303 if (format == GST_FORMAT_TIME) {
1304 /* time segment, this is perfect, copy over the values. */
1305 gst_segment_set_newsegment (&demux->segment, update, rate, format,
1308 GST_DEBUG_OBJECT (demux, "NEWSEGMENT: %" GST_SEGMENT_FORMAT,
1312 ret = gst_flv_demux_push_src_event (demux, event);
1314 /* non-time format */
1315 demux->audio_need_segment = TRUE;
1316 demux->video_need_segment = TRUE;
1318 gst_event_unref (event);
1323 ret = gst_flv_demux_push_src_event (demux, event);
1327 gst_object_unref (demux);
1333 gst_flv_demux_src_event (GstPad * pad, GstEvent * event)
1336 gboolean ret = FALSE;
1338 demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
1340 GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event));
1342 switch (GST_EVENT_TYPE (event)) {
1343 case GST_EVENT_SEEK:
1344 if (demux->random_access) {
1345 ret = gst_flv_demux_handle_seek_pull (demux, event, TRUE);
1347 ret = gst_flv_demux_handle_seek_push (demux, event);
1351 ret = gst_pad_push_event (demux->sinkpad, event);
1355 gst_object_unref (demux);
1361 gst_flv_demux_query (GstPad * pad, GstQuery * query)
1363 gboolean res = TRUE;
1366 demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
1368 switch (GST_QUERY_TYPE (query)) {
1369 case GST_QUERY_DURATION:
1373 gst_query_parse_duration (query, &format, NULL);
1375 /* duration is time only */
1376 if (format != GST_FORMAT_TIME) {
1377 GST_DEBUG_OBJECT (demux, "duration query only supported for time "
1383 GST_DEBUG_OBJECT (pad, "duration query, replying %" GST_TIME_FORMAT,
1384 GST_TIME_ARGS (demux->duration));
1386 gst_query_set_duration (query, GST_FORMAT_TIME, demux->duration);
1390 case GST_QUERY_POSITION:
1394 gst_query_parse_position (query, &format, NULL);
1396 /* position is time only */
1397 if (format != GST_FORMAT_TIME) {
1398 GST_DEBUG_OBJECT (demux, "position query only supported for time "
1404 GST_DEBUG_OBJECT (pad, "position query, replying %" GST_TIME_FORMAT,
1405 GST_TIME_ARGS (demux->segment.last_stop));
1407 gst_query_set_duration (query, GST_FORMAT_TIME, demux->segment.last_stop);
1412 case GST_QUERY_SEEKING:{
1415 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
1417 if (fmt != GST_FORMAT_TIME || !demux->index) {
1418 gst_query_set_seeking (query, fmt, FALSE, -1, -1);
1419 } else if (demux->random_access) {
1420 gst_query_set_seeking (query, GST_FORMAT_TIME, TRUE, 0,
1423 GstQuery *peerquery = gst_query_new_seeking (GST_FORMAT_BYTES);
1424 gboolean seekable = gst_pad_peer_query (demux->sinkpad, peerquery);
1427 gst_query_parse_seeking (peerquery, NULL, &seekable, NULL, NULL);
1428 gst_query_unref (peerquery);
1431 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0,
1434 gst_query_set_seeking (query, GST_FORMAT_TIME, FALSE, -1, -1);
1438 case GST_QUERY_LATENCY:
1443 if ((peer = gst_pad_get_peer (demux->sinkpad))) {
1444 /* query latency on peer pad */
1445 res = gst_pad_query (peer, query);
1446 gst_object_unref (peer);
1448 /* no peer, we don't know */
1456 gst_object_unref (demux);
1461 static GstStateChangeReturn
1462 gst_flv_demux_change_state (GstElement * element, GstStateChange transition)
1465 GstStateChangeReturn ret;
1467 demux = GST_FLV_DEMUX (element);
1469 switch (transition) {
1470 case GST_STATE_CHANGE_READY_TO_PAUSED:
1471 /* If this is our own index destroy it as the
1472 * old entries might be wrong for the new stream */
1473 if (demux->own_index) {
1474 gst_object_unref (demux->index);
1475 demux->index = NULL;
1476 demux->own_index = FALSE;
1479 /* If no index was created, generate one */
1480 if (G_UNLIKELY (!demux->index)) {
1481 GST_DEBUG_OBJECT (demux, "no index provided creating our own");
1483 demux->index = gst_index_factory_make ("memindex");
1485 gst_index_get_writer_id (demux->index, GST_OBJECT (demux),
1487 demux->own_index = TRUE;
1489 gst_flv_demux_cleanup (demux);
1495 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1496 if (ret == GST_STATE_CHANGE_FAILURE)
1499 switch (transition) {
1500 case GST_STATE_CHANGE_PAUSED_TO_READY:
1501 gst_flv_demux_cleanup (demux);
1511 gst_flv_demux_set_index (GstElement * element, GstIndex * index)
1513 GstFLVDemux *demux = GST_FLV_DEMUX (element);
1515 GST_OBJECT_LOCK (demux);
1517 gst_object_unref (demux->index);
1519 demux->index = gst_object_ref (index);
1520 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
1521 demux->own_index = FALSE;
1523 demux->index = NULL;
1525 GST_OBJECT_UNLOCK (demux);
1529 gst_flv_demux_get_index (GstElement * element)
1531 GstIndex *result = NULL;
1533 GstFLVDemux *demux = GST_FLV_DEMUX (element);
1535 GST_OBJECT_LOCK (demux);
1537 result = gst_object_ref (demux->index);
1538 GST_OBJECT_UNLOCK (demux);
1544 gst_flv_demux_dispose (GObject * object)
1546 GstFLVDemux *demux = GST_FLV_DEMUX (object);
1548 GST_DEBUG_OBJECT (demux, "disposing FLV demuxer");
1550 if (demux->adapter) {
1551 gst_adapter_clear (demux->adapter);
1552 g_object_unref (demux->adapter);
1553 demux->adapter = NULL;
1556 if (demux->taglist) {
1557 gst_tag_list_free (demux->taglist);
1558 demux->taglist = NULL;
1561 if (demux->new_seg_event) {
1562 gst_event_unref (demux->new_seg_event);
1563 demux->new_seg_event = NULL;
1566 if (demux->close_seg_event) {
1567 gst_event_unref (demux->close_seg_event);
1568 demux->close_seg_event = NULL;
1571 if (demux->audio_codec_data) {
1572 gst_buffer_unref (demux->audio_codec_data);
1573 demux->audio_codec_data = NULL;
1576 if (demux->video_codec_data) {
1577 gst_buffer_unref (demux->video_codec_data);
1578 demux->video_codec_data = NULL;
1581 if (demux->audio_pad) {
1582 gst_object_unref (demux->audio_pad);
1583 demux->audio_pad = NULL;
1586 if (demux->video_pad) {
1587 gst_object_unref (demux->video_pad);
1588 demux->video_pad = NULL;
1592 gst_object_unref (demux->index);
1593 demux->index = NULL;
1597 g_array_free (demux->times, TRUE);
1598 demux->times = NULL;
1601 if (demux->filepositions) {
1602 g_array_free (demux->filepositions, TRUE);
1603 demux->filepositions = NULL;
1606 GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
1610 gst_flv_demux_base_init (gpointer g_class)
1612 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1614 gst_element_class_add_pad_template (element_class,
1615 gst_static_pad_template_get (&flv_sink_template));
1616 gst_element_class_add_pad_template (element_class,
1617 gst_static_pad_template_get (&audio_src_template));
1618 gst_element_class_add_pad_template (element_class,
1619 gst_static_pad_template_get (&video_src_template));
1620 gst_element_class_set_details_simple (element_class, "FLV Demuxer",
1622 "Demux FLV feeds into digital streams",
1623 "Julien Moutte <julien@moutte.net>");
1627 gst_flv_demux_class_init (GstFLVDemuxClass * klass)
1629 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1630 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1632 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_flv_demux_dispose);
1634 gstelement_class->change_state =
1635 GST_DEBUG_FUNCPTR (gst_flv_demux_change_state);
1636 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_flv_demux_set_index);
1637 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_flv_demux_get_index);
1641 gst_flv_demux_init (GstFLVDemux * demux, GstFLVDemuxClass * g_class)
1644 gst_pad_new_from_static_template (&flv_sink_template, "sink");
1646 gst_pad_set_event_function (demux->sinkpad,
1647 GST_DEBUG_FUNCPTR (gst_flv_demux_sink_event));
1648 gst_pad_set_chain_function (demux->sinkpad,
1649 GST_DEBUG_FUNCPTR (gst_flv_demux_chain));
1650 gst_pad_set_activate_function (demux->sinkpad,
1651 GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate));
1652 gst_pad_set_activatepull_function (demux->sinkpad,
1653 GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate_pull));
1654 gst_pad_set_activatepush_function (demux->sinkpad,
1655 GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate_push));
1657 gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
1659 demux->adapter = gst_adapter_new ();
1660 demux->taglist = gst_tag_list_new ();
1661 gst_segment_init (&demux->segment, GST_FORMAT_TIME);
1663 demux->own_index = FALSE;
1665 gst_flv_demux_cleanup (demux);
1669 plugin_init (GstPlugin * plugin)
1671 GST_DEBUG_CATEGORY_INIT (flvdemux_debug, "flvdemux", 0, "FLV demuxer");
1673 if (!gst_element_register (plugin, "flvdemux", GST_RANK_PRIMARY,
1674 gst_flv_demux_get_type ()) ||
1675 !gst_element_register (plugin, "flvmux", GST_RANK_PRIMARY,
1676 gst_flv_mux_get_type ()))
1682 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
1683 "flv", "FLV muxing and demuxing plugin",
1684 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)