AG_GST_CHECK_PLUGIN(dtmf)
AG_GST_CHECK_PLUGIN(dvdspu)
AG_GST_CHECK_PLUGIN(festival)
-AG_GST_CHECK_PLUGIN(flv)
AG_GST_CHECK_PLUGIN(freeze)
AG_GST_CHECK_PLUGIN(h264parse)
AG_GST_CHECK_PLUGIN(librfb)
gst/dtmf/Makefile
gst/dvdspu/Makefile
gst/festival/Makefile
-gst/flv/Makefile
gst/freeze/Makefile
gst/h264parse/Makefile
gst/librfb/Makefile
$(top_srcdir)/gst/dtmf/gstrtpdtmfdepay.h \
$(top_srcdir)/gst/dvdspu/gstdvdspu.h \
$(top_srcdir)/gst/festival/gstfestival.h \
- $(top_srcdir)/gst/flv/gstflvdemux.h \
- $(top_srcdir)/gst/flv/gstflvmux.h \
$(top_srcdir)/gst/legacyresample/gstlegacyresample.h \
$(top_srcdir)/gst/liveadder/liveadder.h \
$(top_srcdir)/gst/mxf/mxfdemux.h \
<xi:include href="xml/element-dvbsrc.xml" />
<xi:include href="xml/element-dvdspu.xml" />
<xi:include href="xml/element-festival.xml" />
- <xi:include href="xml/element-flvdemux.xml" />
- <xi:include href="xml/element-flvmux.xml" />
<xi:include href="xml/element-fpsdisplaysink.xml" />
<xi:include href="xml/element-gstrtpbin.xml" />
<xi:include href="xml/element-gstrtpclient.xml" />
<xi:include href="xml/plugin-fbdevsink.xml" />
<xi:include href="xml/plugin-festival.xml" />
<xi:include href="xml/plugin-filter.xml" />
- <xi:include href="xml/plugin-flv.xml" />
<xi:include href="xml/plugin-freeze.xml" />
<xi:include href="xml/plugin-gsm.xml" />
<xi:include href="xml/plugin-gstinterlace.xml" />
</SECTION>
<SECTION>
-<FILE>element-flvdemux</FILE>
-<TITLE>flvdemux</TITLE>
-GstFLVDemux
-<SUBSECTION Standard>
-GstFLVDemuxClass
-GstFLVDemuxFlags
-GST_FLV_DEMUX
-GST_FLV_DEMUX_CLASS
-GST_IS_FLV_DEMUX
-GST_IS_FLV_DEMUX_CLASS
-GST_TYPE_FLV_DEMUX
-gst_flv_demux_get_type
-</SECTION>
-
-<SECTION>
-<FILE>element-flvmux</FILE>
-<TITLE>flvmux</TITLE>
-GstFlvMux
-<SUBSECTION Standard>
-GstFlvMuxClass
-GstFlvMuxFlags
-GST_FLV_MUX
-GST_FLV_MUX_CLASS
-GST_IS_FLV_MUX
-GST_IS_FLV_MUX_CLASS
-GST_TYPE_FLV_MUX
-gst_flv_mux_get_type
-</SECTION>
-
-<SECTION>
<FILE>element-fpsdisplaysink</FILE>
<TITLE>fpsdisplaysink</TITLE>
GstFPSDisplaySink
+++ /dev/null
-<plugin>
- <name>flv</name>
- <description>FLV muxing and demuxing plugin</description>
- <filename>../../gst/flv/.libs/libgstflv.so</filename>
- <basename>libgstflv.so</basename>
- <version>0.10.11.1</version>
- <license>LGPL</license>
- <source>gst-plugins-bad</source>
- <package>GStreamer Bad Plug-ins CVS/prerelease</package>
- <origin>Unknown package origin</origin>
- <elements>
- <element>
- <name>flvdemux</name>
- <longname>FLV Demuxer</longname>
- <class>Codec/Demuxer</class>
- <description>Demux FLV feeds into digital streams</description>
- <author>Julien Moutte <julien@moutte.net></author>
- <pads>
- <caps>
- <name>video</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>ANY</details>
- </caps>
- <caps>
- <name>audio</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>ANY</details>
- </caps>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>video/x-flv</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>flvmux</name>
- <longname>FLV muxer</longname>
- <class>Codec/Muxer</class>
- <description>Muxes video/audio streams into a FLV stream</description>
- <author>Sebastian Dröge <sebastian.droege@collabora.co.uk></author>
- <pads>
- <caps>
- <name>src</name>
- <direction>source</direction>
- <presence>always</presence>
- <details>video/x-flv</details>
- </caps>
- <caps>
- <name>audio</name>
- <direction>sink</direction>
- <presence>request</presence>
- <details>audio/x-adpcm, layout=(string)swf, channels=(int){ 1, 2 }, rate=(int){ 5512, 11025, 22050, 44100 }; audio/mpeg, mpegversion=(int)1, layer=(int)3, channels=(int){ 1, 2 }, rate=(int){ 5512, 8000, 11025, 22050, 44100 }; audio/mpeg, mpegversion=(int)4; audio/x-nellymoser, channels=(int){ 1, 2 }, rate=(int){ 5512, 8000, 11025, 16000, 22050, 44100 }; audio/x-raw-int, endianness=(int)1234, channels=(int){ 1, 2 }, width=(int)8, depth=(int)8, rate=(int){ 5512, 11025, 22050, 44100 }, signed=(boolean)false; audio/x-raw-int, endianness=(int)1234, channels=(int){ 1, 2 }, width=(int)16, depth=(int)16, rate=(int){ 5512, 11025, 22050, 44100 }, signed=(boolean)true; audio/x-alaw, channels=(int){ 1, 2 }, rate=(int){ 5512, 11025, 22050, 44100 }; audio/x-mulaw, channels=(int){ 1, 2 }, rate=(int){ 5512, 11025, 22050, 44100 }; audio/x-speex, channels=(int){ 1, 2 }, rate=(int){ 5512, 11025, 22050, 44100 }</details>
- </caps>
- <caps>
- <name>video</name>
- <direction>sink</direction>
- <presence>request</presence>
- <details>video/x-flash-video; video/x-flash-screen; video/x-vp6-flash; video/x-vp6-alpha; video/x-h264</details>
- </caps>
- </pads>
- </element>
- </elements>
-</plugin>
\ No newline at end of file
+++ /dev/null
-<plugin>
- <name>flvdemux</name>
- <description>Element demuxing FLV stream</description>
- <filename>../../gst/flv/.libs/libgstflvdemux.so</filename>
- <basename>libgstflvdemux.so</basename>
- <version>0.10.8</version>
- <license>LGPL</license>
- <source>gst-plugins-bad</source>
- <package>GStreamer Bad Plug-ins source release</package>
- <origin>Unknown package origin</origin>
- <elements>
- <element>
- <name>flvdemux</name>
- <longname>FLV Demuxer</longname>
- <class>Codec/Demuxer</class>
- <description>Demux FLV feeds into digital streams</description>
- <author>Julien Moutte <julien@moutte.net></author>
- <pads>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>video/x-flv</details>
- </caps>
- <caps>
- <name>audio</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>ANY</details>
- </caps>
- <caps>
- <name>video</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>ANY</details>
- </caps>
- </pads>
- </element>
- </elements>
-</plugin>
\ No newline at end of file
+++ /dev/null
-plugin_LTLIBRARIES = libgstflv.la
-
-libgstflv_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS)
-libgstflv_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS)
-libgstflv_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS}
-libgstflv_la_SOURCES = gstflvdemux.c gstflvparse.c gstflvmux.c
-libgstflv_la_LIBTOOLFLAGS = --tag=disable-static
-
-noinst_HEADERS = gstflvdemux.h gstflvparse.h gstflvmux.h
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Julien Moutte <julien@moutte.net>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/**
- * SECTION:element-flvdemux
- *
- * flvdemux demuxes an FLV file into the different contained streams.
- *
- * <refsect2>
- * <title>Example launch line</title>
- * |[
- * gst-launch -v filesrc location=/path/to/flv ! flvdemux ! audioconvert ! autoaudiosink
- * ]| This pipeline demuxes an FLV file and outputs the contained raw audio streams.
- * </refsect2>
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "gstflvdemux.h"
-#include "gstflvparse.h"
-#include "gstflvmux.h"
-
-#include <string.h>
-
-static GstStaticPadTemplate flv_sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("video/x-flv")
- );
-
-static GstStaticPadTemplate audio_src_template =
-GST_STATIC_PAD_TEMPLATE ("audio",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS_ANY);
-
-static GstStaticPadTemplate video_src_template =
-GST_STATIC_PAD_TEMPLATE ("video",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS_ANY);
-
-GST_DEBUG_CATEGORY (flvdemux_debug);
-#define GST_CAT_DEFAULT flvdemux_debug
-
-GST_BOILERPLATE (GstFLVDemux, gst_flv_demux, GstElement, GST_TYPE_ELEMENT);
-
-/* 9 bytes of header + 4 bytes of first previous tag size */
-#define FLV_HEADER_SIZE 13
-/* 1 byte of tag type + 3 bytes of tag data size */
-#define FLV_TAG_TYPE_SIZE 4
-
-static void
-gst_flv_demux_flush (GstFLVDemux * demux, gboolean discont)
-{
- GST_DEBUG_OBJECT (demux, "flushing queued data in the FLV demuxer");
-
- gst_adapter_clear (demux->adapter);
-
- demux->audio_need_discont = TRUE;
- demux->video_need_discont = TRUE;
-
- demux->flushing = FALSE;
-
- /* Only in push mode */
- if (!demux->random_access) {
- /* After a flush we expect a tag_type */
- demux->state = FLV_STATE_TAG_TYPE;
- /* We reset the offset and will get one from first push */
- demux->offset = 0;
- }
-}
-
-static void
-gst_flv_demux_cleanup (GstFLVDemux * demux)
-{
- GST_DEBUG_OBJECT (demux, "cleaning up FLV demuxer");
-
- demux->state = FLV_STATE_HEADER;
-
- demux->flushing = FALSE;
- demux->need_header = TRUE;
- demux->audio_need_segment = TRUE;
- demux->video_need_segment = TRUE;
- demux->audio_need_discont = TRUE;
- demux->video_need_discont = TRUE;
-
- /* By default we consider them as linked */
- demux->audio_linked = TRUE;
- demux->video_linked = TRUE;
-
- demux->has_audio = FALSE;
- demux->has_video = FALSE;
- demux->push_tags = FALSE;
- demux->got_par = FALSE;
-
- gst_segment_init (&demux->segment, GST_FORMAT_TIME);
-
- demux->w = demux->h = 0;
- demux->par_x = demux->par_y = 1;
- demux->video_offset = 0;
- demux->audio_offset = 0;
- demux->offset = demux->cur_tag_offset = 0;
- demux->tag_size = demux->tag_data_size = 0;
- demux->duration = GST_CLOCK_TIME_NONE;
-
- if (demux->new_seg_event) {
- gst_event_unref (demux->new_seg_event);
- demux->new_seg_event = NULL;
- }
-
- if (demux->close_seg_event) {
- gst_event_unref (demux->close_seg_event);
- demux->close_seg_event = NULL;
- }
-
- gst_adapter_clear (demux->adapter);
-
- if (demux->audio_codec_data) {
- gst_buffer_unref (demux->audio_codec_data);
- demux->audio_codec_data = NULL;
- }
-
- if (demux->video_codec_data) {
- gst_buffer_unref (demux->video_codec_data);
- demux->video_codec_data = NULL;
- }
-
- if (demux->audio_pad) {
- gst_element_remove_pad (GST_ELEMENT (demux), demux->audio_pad);
- gst_object_unref (demux->audio_pad);
- demux->audio_pad = NULL;
- }
-
- if (demux->video_pad) {
- gst_element_remove_pad (GST_ELEMENT (demux), demux->video_pad);
- gst_object_unref (demux->video_pad);
- demux->video_pad = NULL;
- }
-
- if (demux->times) {
- g_array_free (demux->times, TRUE);
- demux->times = NULL;
- }
-
- if (demux->filepositions) {
- g_array_free (demux->filepositions, TRUE);
- demux->filepositions = NULL;
- }
-}
-
-static GstFlowReturn
-gst_flv_demux_chain (GstPad * pad, GstBuffer * buffer)
-{
- GstFlowReturn ret = GST_FLOW_OK;
- GstFLVDemux *demux = NULL;
-
- demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
-
- GST_LOG_OBJECT (demux, "received buffer of %d bytes at offset %"
- G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer));
-
- if (G_UNLIKELY (GST_BUFFER_OFFSET (buffer) == 0)) {
- GST_DEBUG_OBJECT (demux, "beginning of file, expect header");
- demux->state = FLV_STATE_HEADER;
- demux->offset = 0;
- }
-
- if (G_UNLIKELY (demux->offset == 0 && GST_BUFFER_OFFSET (buffer) != 0)) {
- GST_DEBUG_OBJECT (demux, "offset was zero, synchronizing with buffer's");
- demux->offset = GST_BUFFER_OFFSET (buffer);
- }
-
- gst_adapter_push (demux->adapter, buffer);
-
-parse:
- if (G_UNLIKELY (ret != GST_FLOW_OK)) {
- if (ret == GST_FLOW_NOT_LINKED && (demux->audio_linked
- || demux->video_linked)) {
- ret = GST_FLOW_OK;
- } else {
- GST_DEBUG_OBJECT (demux, "got flow return %s", gst_flow_get_name (ret));
- goto beach;
- }
- }
-
- if (G_UNLIKELY (demux->flushing)) {
- GST_DEBUG_OBJECT (demux, "we are now flushing, exiting parser loop");
- ret = GST_FLOW_WRONG_STATE;
- goto beach;
- }
-
- switch (demux->state) {
- case FLV_STATE_HEADER:
- {
- if (gst_adapter_available (demux->adapter) >= FLV_HEADER_SIZE) {
- GstBuffer *buffer;
-
- buffer = gst_adapter_take_buffer (demux->adapter, FLV_HEADER_SIZE);
-
- ret = gst_flv_parse_header (demux, buffer);
-
- gst_buffer_unref (buffer);
- demux->offset += FLV_HEADER_SIZE;
-
- demux->state = FLV_STATE_TAG_TYPE;
- goto parse;
- } else {
- goto beach;
- }
- }
- case FLV_STATE_TAG_TYPE:
- {
- if (gst_adapter_available (demux->adapter) >= FLV_TAG_TYPE_SIZE) {
- GstBuffer *buffer;
-
- /* Remember the tag offset in bytes */
- demux->cur_tag_offset = demux->offset;
-
- buffer = gst_adapter_take_buffer (demux->adapter, FLV_TAG_TYPE_SIZE);
-
- ret = gst_flv_parse_tag_type (demux, buffer);
-
- gst_buffer_unref (buffer);
- demux->offset += FLV_TAG_TYPE_SIZE;
-
- goto parse;
- } else {
- goto beach;
- }
- }
- case FLV_STATE_TAG_VIDEO:
- {
- if (gst_adapter_available (demux->adapter) >= demux->tag_size) {
- GstBuffer *buffer;
-
- buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size);
-
- ret = gst_flv_parse_tag_video (demux, buffer);
-
- gst_buffer_unref (buffer);
- demux->offset += demux->tag_size;
-
- demux->state = FLV_STATE_TAG_TYPE;
- goto parse;
- } else {
- goto beach;
- }
- }
- case FLV_STATE_TAG_AUDIO:
- {
- if (gst_adapter_available (demux->adapter) >= demux->tag_size) {
- GstBuffer *buffer;
-
- buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size);
-
- ret = gst_flv_parse_tag_audio (demux, buffer);
-
- gst_buffer_unref (buffer);
- demux->offset += demux->tag_size;
-
- demux->state = FLV_STATE_TAG_TYPE;
- goto parse;
- } else {
- goto beach;
- }
- }
- case FLV_STATE_TAG_SCRIPT:
- {
- if (gst_adapter_available (demux->adapter) >= demux->tag_size) {
- GstBuffer *buffer;
-
- buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size);
-
- ret = gst_flv_parse_tag_script (demux, buffer);
-
- gst_buffer_unref (buffer);
- demux->offset += demux->tag_size;
-
- demux->state = FLV_STATE_TAG_TYPE;
- goto parse;
- } else {
- goto beach;
- }
- }
- default:
- GST_DEBUG_OBJECT (demux, "unexpected demuxer state");
- }
-
-beach:
- if (G_UNLIKELY (ret == GST_FLOW_NOT_LINKED)) {
- /* If either audio or video is linked we return GST_FLOW_OK */
- if (demux->audio_linked || demux->video_linked) {
- ret = GST_FLOW_OK;
- }
- }
-
- gst_object_unref (demux);
-
- return ret;
-}
-
-static GstFlowReturn
-gst_flv_demux_pull_range (GstFLVDemux * demux, GstPad * pad, guint64 offset,
- guint size, GstBuffer ** buffer)
-{
- GstFlowReturn ret;
-
- ret = gst_pad_pull_range (pad, offset, size, buffer);
- if (G_UNLIKELY (ret != GST_FLOW_OK)) {
- GST_WARNING_OBJECT (demux,
- "failed when pulling %d bytes from offset %" G_GUINT64_FORMAT ": %s",
- size, offset, gst_flow_get_name (ret));
- *buffer = NULL;
- return ret;
- }
-
- if (G_UNLIKELY (*buffer && GST_BUFFER_SIZE (*buffer) != size)) {
- GST_WARNING_OBJECT (demux,
- "partial pull got %d when expecting %d from offset %" G_GUINT64_FORMAT,
- GST_BUFFER_SIZE (*buffer), size, offset);
- gst_buffer_unref (*buffer);
- ret = GST_FLOW_UNEXPECTED;
- *buffer = NULL;
- return ret;
- }
-
- return ret;
-}
-
-static GstFlowReturn
-gst_flv_demux_pull_tag (GstPad * pad, GstFLVDemux * demux)
-{
- GstBuffer *buffer = NULL;
- GstFlowReturn ret = GST_FLOW_OK;
-
- /* Store tag offset */
- demux->cur_tag_offset = demux->offset;
-
- /* Get the first 4 bytes to identify tag type and size */
- if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset,
- FLV_TAG_TYPE_SIZE, &buffer)) != GST_FLOW_OK))
- goto beach;
-
- /* Identify tag type */
- ret = gst_flv_parse_tag_type (demux, buffer);
-
- gst_buffer_unref (buffer);
-
- if (G_UNLIKELY (ret != GST_FLOW_OK))
- goto beach;
-
- /* Jump over tag type + size */
- demux->offset += FLV_TAG_TYPE_SIZE;
-
- /* Pull the whole tag */
- if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset,
- demux->tag_size, &buffer)) != GST_FLOW_OK))
- goto beach;
-
- switch (demux->state) {
- case FLV_STATE_TAG_VIDEO:
- ret = gst_flv_parse_tag_video (demux, buffer);
- break;
- case FLV_STATE_TAG_AUDIO:
- ret = gst_flv_parse_tag_audio (demux, buffer);
- break;
- case FLV_STATE_TAG_SCRIPT:
- ret = gst_flv_parse_tag_script (demux, buffer);
- break;
- default:
- GST_WARNING_OBJECT (demux, "unexpected state %d", demux->state);
- }
-
- gst_buffer_unref (buffer);
-
- /* Jump over that part we've just parsed */
- demux->offset += demux->tag_size;
-
- /* Make sure we reinitialize the tag size */
- demux->tag_size = 0;
-
- /* Ready for the next tag */
- demux->state = FLV_STATE_TAG_TYPE;
-
- if (G_UNLIKELY (ret == GST_FLOW_NOT_LINKED)) {
- /* If either audio or video is linked we return GST_FLOW_OK */
- if (demux->audio_linked || demux->video_linked) {
- ret = GST_FLOW_OK;
- } else {
- GST_WARNING_OBJECT (demux, "parsing this tag returned not-linked and "
- "neither video nor audio are linked");
- }
- }
-
-beach:
- return ret;
-}
-
-static GstFlowReturn
-gst_flv_demux_pull_header (GstPad * pad, GstFLVDemux * demux)
-{
- GstBuffer *buffer = NULL;
- GstFlowReturn ret = GST_FLOW_OK;
-
- /* Get the first 9 bytes */
- if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset,
- FLV_HEADER_SIZE, &buffer)) != GST_FLOW_OK))
- goto beach;
-
- ret = gst_flv_parse_header (demux, buffer);
-
- gst_buffer_unref (buffer);
-
- /* Jump over the header now */
- demux->offset += FLV_HEADER_SIZE;
- demux->state = FLV_STATE_TAG_TYPE;
-
-beach:
- return ret;
-}
-
-static GstFlowReturn
-gst_flv_demux_seek_to_prev_keyframe (GstFLVDemux * demux)
-{
- return GST_FLOW_OK;
-}
-
-static gboolean
-gst_flv_demux_push_src_event (GstFLVDemux * demux, GstEvent * event)
-{
- gboolean ret = TRUE;
-
- if (demux->audio_pad)
- ret |= gst_pad_push_event (demux->audio_pad, gst_event_ref (event));
-
- if (demux->video_pad)
- ret |= gst_pad_push_event (demux->video_pad, gst_event_ref (event));
-
- gst_event_unref (event);
-
- return ret;
-}
-
-static void
-gst_flv_demux_create_index (GstFLVDemux * demux)
-{
- gint64 size;
- GstFormat fmt = GST_FORMAT_BYTES;
- size_t tag_size;
- guint64 old_offset;
- GstBuffer *buffer;
- GstFlowReturn ret;
-
- if (!gst_pad_query_peer_duration (demux->sinkpad, &fmt, &size) ||
- fmt != GST_FORMAT_BYTES)
- return;
-
- old_offset = demux->offset;
-
- while ((ret =
- gst_flv_demux_pull_range (demux, demux->sinkpad, demux->offset, 12,
- &buffer)) == GST_FLOW_OK) {
- if (gst_flv_parse_tag_timestamp (demux, buffer,
- &tag_size) == GST_CLOCK_TIME_NONE) {
- gst_buffer_unref (buffer);
- break;
- }
-
- gst_buffer_unref (buffer);
- demux->offset += tag_size;
- }
-
- demux->offset = old_offset;
-}
-
-static void
-gst_flv_demux_loop (GstPad * pad)
-{
- GstFLVDemux *demux = NULL;
- GstFlowReturn ret = GST_FLOW_OK;
-
- demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
-
- if (demux->segment.rate >= 0) {
- /* pull in data */
- switch (demux->state) {
- case FLV_STATE_TAG_TYPE:
- ret = gst_flv_demux_pull_tag (pad, demux);
- break;
- case FLV_STATE_DONE:
- ret = GST_FLOW_UNEXPECTED;
- break;
- default:
- ret = gst_flv_demux_pull_header (pad, demux);
- if (ret == GST_FLOW_OK)
- gst_flv_demux_create_index (demux);
-
- }
-
- /* pause if something went wrong */
- if (G_UNLIKELY (ret != GST_FLOW_OK))
- goto pause;
-
- /* check EOS condition */
- if ((demux->segment.flags & GST_SEEK_FLAG_SEGMENT) &&
- (demux->segment.stop != -1) &&
- (demux->segment.last_stop >= demux->segment.stop)) {
- ret = GST_FLOW_UNEXPECTED;
- goto pause;
- }
- } else { /* Reverse playback */
- /* pull in data */
- switch (demux->state) {
- case FLV_STATE_TAG_TYPE:
- ret = gst_flv_demux_pull_tag (pad, demux);
- /* When packet parsing returns UNEXPECTED that means we ve reached the
- point where we want to go to the previous keyframe. This is either
- the last FLV tag or the keyframe we used last time */
- if (ret == GST_FLOW_UNEXPECTED) {
- ret = gst_flv_demux_seek_to_prev_keyframe (demux);
- demux->state = FLV_STATE_TAG_TYPE;
- }
- break;
- default:
- ret = gst_flv_demux_pull_header (pad, demux);
- if (ret == GST_FLOW_OK)
- gst_flv_demux_create_index (demux);
- }
-
- /* pause if something went wrong */
- if (G_UNLIKELY (ret != GST_FLOW_OK))
- goto pause;
-
- /* check EOS condition */
- if (demux->segment.last_stop <= demux->segment.start) {
- ret = GST_FLOW_UNEXPECTED;
- goto pause;
- }
- }
-
- gst_object_unref (demux);
-
- return;
-
-pause:
- {
- const gchar *reason = gst_flow_get_name (ret);
-
- GST_LOG_OBJECT (demux, "pausing task, reason %s", reason);
- gst_pad_pause_task (pad);
-
- if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
- if (ret == GST_FLOW_UNEXPECTED) {
- /* perform EOS logic */
- gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
- if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
- gint64 stop;
-
- /* for segment playback we need to post when (in stream time)
- * we stopped, this is either stop (when set) or the duration. */
- if ((stop = demux->segment.stop) == -1)
- stop = demux->segment.duration;
-
- if (demux->segment.rate >= 0) {
- GST_LOG_OBJECT (demux, "Sending segment done, at end of segment");
- gst_element_post_message (GST_ELEMENT_CAST (demux),
- gst_message_new_segment_done (GST_OBJECT_CAST (demux),
- GST_FORMAT_TIME, stop));
- } else { /* Reverse playback */
- GST_LOG_OBJECT (demux, "Sending segment done, at beginning of "
- "segment");
- gst_element_post_message (GST_ELEMENT_CAST (demux),
- gst_message_new_segment_done (GST_OBJECT_CAST (demux),
- GST_FORMAT_TIME, demux->segment.start));
- }
- } else {
- /* normal playback, send EOS to all linked pads */
- gst_element_no_more_pads (GST_ELEMENT (demux));
- GST_LOG_OBJECT (demux, "Sending EOS, at end of stream");
- if (!gst_flv_demux_push_src_event (demux, gst_event_new_eos ()))
- GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
- }
- } else {
- GST_ELEMENT_ERROR (demux, STREAM, FAILED,
- ("Internal data stream error."),
- ("stream stopped, reason %s", reason));
- gst_flv_demux_push_src_event (demux, gst_event_new_eos ());
- }
- }
- gst_object_unref (demux);
- return;
- }
-}
-
-static guint64
-gst_flv_demux_find_offset (GstFLVDemux * demux, GstSegment * segment)
-{
- gint64 bytes = 0;
- gint64 time = 0;
- GstIndexEntry *entry;
-
- g_return_val_if_fail (segment != NULL, 0);
-
- time = segment->start;
-
- if (demux->index) {
- /* Let's check if we have an index entry for that seek time */
- entry = gst_index_get_assoc_entry (demux->index, demux->index_id,
- GST_INDEX_LOOKUP_BEFORE, GST_ASSOCIATION_FLAG_KEY_UNIT,
- GST_FORMAT_TIME, time);
-
- if (entry) {
- gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes);
- gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
-
- GST_DEBUG_OBJECT (demux, "found index entry for %" GST_TIME_FORMAT
- " at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT,
- GST_TIME_ARGS (segment->start), GST_TIME_ARGS (time), bytes);
-
- /* Key frame seeking */
- if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
- /* Adjust the segment so that the keyframe fits in */
- if (time < segment->start) {
- segment->start = segment->time = time;
- }
- segment->last_stop = time;
- }
- } else {
- GST_DEBUG_OBJECT (demux, "no index entry found for %" GST_TIME_FORMAT,
- GST_TIME_ARGS (segment->start));
- }
- }
-
- return bytes;
-}
-
-static gboolean
-gst_flv_demux_handle_seek_push (GstFLVDemux * demux, GstEvent * event)
-{
- GstFormat format;
- GstSeekFlags flags;
- GstSeekType start_type, stop_type;
- gint64 start, stop;
- gdouble rate;
- gboolean update, flush, keyframe, ret;
- GstSegment seeksegment;
-
- gst_event_parse_seek (event, &rate, &format, &flags,
- &start_type, &start, &stop_type, &stop);
-
- if (format != GST_FORMAT_TIME)
- goto wrong_format;
-
- flush = !!(flags & GST_SEEK_FLAG_FLUSH);
- keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
-
- /* Work on a copy until we are sure the seek succeeded. */
- memcpy (&seeksegment, &demux->segment, sizeof (GstSegment));
-
- GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT,
- &demux->segment);
-
- /* Apply the seek to our segment */
- gst_segment_set_seek (&seeksegment, rate, format, flags,
- start_type, start, stop_type, stop, &update);
-
- GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT,
- &seeksegment);
-
- if (flush || seeksegment.last_stop != demux->segment.last_stop) {
- /* Do the actual seeking */
- guint64 offset = gst_flv_demux_find_offset (demux, &seeksegment);
-
- GST_DEBUG_OBJECT (demux, "generating an upstream seek at position %"
- G_GUINT64_FORMAT, offset);
- ret = gst_pad_push_event (demux->sinkpad,
- gst_event_new_seek (seeksegment.rate, GST_FORMAT_BYTES,
- seeksegment.flags | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET,
- offset, GST_SEEK_TYPE_NONE, 0));
- if (G_UNLIKELY (!ret)) {
- GST_WARNING_OBJECT (demux, "upstream seek failed");
- }
-
- /* Tell all the stream we moved to a different position (discont) */
- demux->audio_need_discont = TRUE;
- demux->video_need_discont = TRUE;
- } else {
- ret = TRUE;
- }
-
- if (ret) {
- /* Ok seek succeeded, take the newly configured segment */
- memcpy (&demux->segment, &seeksegment, sizeof (GstSegment));
-
- /* Tell all the stream a new segment is needed */
- demux->audio_need_segment = TRUE;
- demux->video_need_segment = TRUE;
- /* Clean any potential newsegment event kept for the streams. The first
- * stream needing a new segment will create a new one. */
- if (G_UNLIKELY (demux->new_seg_event)) {
- gst_event_unref (demux->new_seg_event);
- demux->new_seg_event = NULL;
- }
- gst_event_unref (event);
- } else {
- ret = gst_pad_push_event (demux->sinkpad, event);
- }
-
- return ret;
-
-/* ERRORS */
-wrong_format:
- {
- GST_WARNING_OBJECT (demux, "we only support seeking in TIME format");
- return gst_pad_push_event (demux->sinkpad, event);
- }
-}
-
-static gboolean
-gst_flv_demux_handle_seek_pull (GstFLVDemux * demux, GstEvent * event)
-{
- GstFormat format;
- GstSeekFlags flags;
- GstSeekType start_type, stop_type;
- gint64 start, stop;
- gdouble rate;
- gboolean update, flush, keyframe, ret;
- GstSegment seeksegment;
-
- gst_event_parse_seek (event, &rate, &format, &flags,
- &start_type, &start, &stop_type, &stop);
-
- gst_event_unref (event);
-
- if (format != GST_FORMAT_TIME)
- goto wrong_format;
-
- flush = !!(flags & GST_SEEK_FLAG_FLUSH);
- keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
-
- if (flush) {
- /* Flush start up and downstream to make sure data flow and loops are
- idle */
- gst_flv_demux_push_src_event (demux, gst_event_new_flush_start ());
- gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ());
- } else {
- /* Pause the pulling task */
- gst_pad_pause_task (demux->sinkpad);
- }
-
- /* Take the stream lock */
- GST_PAD_STREAM_LOCK (demux->sinkpad);
-
- if (flush) {
- /* Stop flushing upstream we need to pull */
- gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ());
- }
-
- /* Work on a copy until we are sure the seek succeeded. */
- memcpy (&seeksegment, &demux->segment, sizeof (GstSegment));
-
- GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT,
- &demux->segment);
-
- /* Apply the seek to our segment */
- gst_segment_set_seek (&seeksegment, rate, format, flags,
- start_type, start, stop_type, stop, &update);
-
- GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT,
- &seeksegment);
-
- if (flush || seeksegment.last_stop != demux->segment.last_stop) {
- /* Do the actual seeking */
- demux->offset = gst_flv_demux_find_offset (demux, &seeksegment);
-
- /* Tell all the stream we moved to a different position (discont) */
- demux->audio_need_discont = TRUE;
- demux->video_need_discont = TRUE;
-
- /* If we seeked at the beginning of the file parse the header again */
- if (G_UNLIKELY (!demux->offset)) {
- demux->state = FLV_STATE_HEADER;
- } else { /* or parse a tag */
- demux->state = FLV_STATE_TAG_TYPE;
- }
- ret = TRUE;
- } else {
- ret = TRUE;
- }
-
- if (G_UNLIKELY (demux->close_seg_event)) {
- gst_event_unref (demux->close_seg_event);
- demux->close_seg_event = NULL;
- }
-
- if (flush) {
- /* Stop flushing, the sinks are at time 0 now */
- gst_flv_demux_push_src_event (demux, gst_event_new_flush_stop ());
- } else {
- GST_DEBUG_OBJECT (demux, "closing running segment %" GST_SEGMENT_FORMAT,
- &demux->segment);
-
- /* Close the current segment for a linear playback */
- if (demux->segment.rate >= 0) {
- /* for forward playback, we played from start to last_stop */
- demux->close_seg_event = gst_event_new_new_segment (TRUE,
- demux->segment.rate, demux->segment.format,
- demux->segment.start, demux->segment.last_stop, demux->segment.time);
- } else {
- gint64 stop;
-
- if ((stop = demux->segment.stop) == -1)
- stop = demux->segment.duration;
-
- /* for reverse playback, we played from stop to last_stop. */
- demux->close_seg_event = gst_event_new_new_segment (TRUE,
- demux->segment.rate, demux->segment.format,
- demux->segment.last_stop, stop, demux->segment.last_stop);
- }
- }
-
- if (ret) {
- /* Ok seek succeeded, take the newly configured segment */
- memcpy (&demux->segment, &seeksegment, sizeof (GstSegment));
-
- /* Notify about the start of a new segment */
- if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
- gst_element_post_message (GST_ELEMENT (demux),
- gst_message_new_segment_start (GST_OBJECT (demux),
- demux->segment.format, demux->segment.last_stop));
- }
-
- /* Tell all the stream a new segment is needed */
- demux->audio_need_segment = TRUE;
- demux->video_need_segment = TRUE;
- /* Clean any potential newsegment event kept for the streams. The first
- * stream needing a new segment will create a new one. */
- if (G_UNLIKELY (demux->new_seg_event)) {
- gst_event_unref (demux->new_seg_event);
- demux->new_seg_event = NULL;
- }
- }
-
- gst_pad_start_task (demux->sinkpad,
- (GstTaskFunction) gst_flv_demux_loop, demux->sinkpad);
-
- GST_PAD_STREAM_UNLOCK (demux->sinkpad);
-
- return ret;
-
- /* ERRORS */
-wrong_format:
- {
- GST_WARNING_OBJECT (demux, "we only support seeking in TIME format");
- return FALSE;
- }
-}
-
-/* If we can pull that's prefered */
-static gboolean
-gst_flv_demux_sink_activate (GstPad * sinkpad)
-{
- if (gst_pad_check_pull_range (sinkpad)) {
- return gst_pad_activate_pull (sinkpad, TRUE);
- } else {
- return gst_pad_activate_push (sinkpad, TRUE);
- }
-}
-
-/* This function gets called when we activate ourselves in push mode.
- * We cannot seek (ourselves) in the stream */
-static gboolean
-gst_flv_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
-{
- GstFLVDemux *demux;
-
- demux = GST_FLV_DEMUX (gst_pad_get_parent (sinkpad));
-
- demux->random_access = FALSE;
-
- gst_object_unref (demux);
-
- return TRUE;
-}
-
-/* this function gets called when we activate ourselves in pull mode.
- * We can perform random access to the resource and we start a task
- * to start reading */
-static gboolean
-gst_flv_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
-{
- GstFLVDemux *demux;
-
- demux = GST_FLV_DEMUX (gst_pad_get_parent (sinkpad));
-
- if (active) {
- demux->random_access = TRUE;
- gst_object_unref (demux);
- return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_flv_demux_loop,
- sinkpad);
- } else {
- demux->random_access = FALSE;
- gst_object_unref (demux);
- return gst_pad_stop_task (sinkpad);
- }
-}
-
-static gboolean
-gst_flv_demux_sink_event (GstPad * pad, GstEvent * event)
-{
- GstFLVDemux *demux;
- gboolean ret = FALSE;
-
- demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
-
- GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_FLUSH_START:
- GST_DEBUG_OBJECT (demux, "trying to force chain function to exit");
- demux->flushing = TRUE;
- ret = gst_flv_demux_push_src_event (demux, event);
- break;
- case GST_EVENT_FLUSH_STOP:
- GST_DEBUG_OBJECT (demux, "flushing FLV demuxer");
- gst_flv_demux_flush (demux, TRUE);
- ret = gst_flv_demux_push_src_event (demux, event);
- break;
- case GST_EVENT_EOS:
- GST_DEBUG_OBJECT (demux, "received EOS");
- if (demux->index) {
- GST_DEBUG_OBJECT (demux, "committing index");
- gst_index_commit (demux->index, demux->index_id);
- }
- gst_element_no_more_pads (GST_ELEMENT (demux));
- if (!gst_flv_demux_push_src_event (demux, event))
- GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
- ret = TRUE;
- break;
- case GST_EVENT_NEWSEGMENT:
- {
- GstFormat format;
- gdouble rate;
- gint64 start, stop, time;
- gboolean update;
-
- GST_DEBUG_OBJECT (demux, "received new segment");
-
- gst_event_parse_new_segment (event, &update, &rate, &format, &start,
- &stop, &time);
-
- if (format == GST_FORMAT_TIME) {
- /* time segment, this is perfect, copy over the values. */
- gst_segment_set_newsegment (&demux->segment, update, rate, format,
- start, stop, time);
-
- GST_DEBUG_OBJECT (demux, "NEWSEGMENT: %" GST_SEGMENT_FORMAT,
- &demux->segment);
-
- /* and forward */
- ret = gst_flv_demux_push_src_event (demux, event);
- } else {
- /* non-time format */
- demux->audio_need_segment = TRUE;
- demux->video_need_segment = TRUE;
- ret = TRUE;
- gst_event_unref (event);
- }
- break;
- }
- default:
- ret = gst_flv_demux_push_src_event (demux, event);
- break;
- }
-
- gst_object_unref (demux);
-
- return ret;
-}
-
-gboolean
-gst_flv_demux_src_event (GstPad * pad, GstEvent * event)
-{
- GstFLVDemux *demux;
- gboolean ret = FALSE;
-
- demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
-
- GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_SEEK:
- if (demux->random_access) {
- ret = gst_flv_demux_handle_seek_pull (demux, event);
- } else {
- ret = gst_flv_demux_handle_seek_push (demux, event);
- }
- break;
- default:
- ret = gst_pad_push_event (demux->sinkpad, event);
- break;
- }
-
- gst_object_unref (demux);
-
- return ret;
-}
-
-gboolean
-gst_flv_demux_query (GstPad * pad, GstQuery * query)
-{
- gboolean res = TRUE;
- GstFLVDemux *demux;
-
- demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_DURATION:
- {
- GstFormat format;
-
- gst_query_parse_duration (query, &format, NULL);
-
- /* duration is time only */
- if (format != GST_FORMAT_TIME) {
- GST_DEBUG_OBJECT (demux, "duration query only supported for time "
- "format");
- res = FALSE;
- goto beach;
- }
-
- GST_DEBUG_OBJECT (pad, "duration query, replying %" GST_TIME_FORMAT,
- GST_TIME_ARGS (demux->duration));
-
- gst_query_set_duration (query, GST_FORMAT_TIME, demux->duration);
-
- break;
- }
- case GST_QUERY_POSITION:
- {
- GstFormat format;
-
- gst_query_parse_position (query, &format, NULL);
-
- /* position is time only */
- if (format != GST_FORMAT_TIME) {
- GST_DEBUG_OBJECT (demux, "position query only supported for time "
- "format");
- res = FALSE;
- goto beach;
- }
-
- GST_DEBUG_OBJECT (pad, "position query, replying %" GST_TIME_FORMAT,
- GST_TIME_ARGS (demux->segment.last_stop));
-
- gst_query_set_duration (query, GST_FORMAT_TIME, demux->segment.last_stop);
-
- break;
- }
-
- case GST_QUERY_LATENCY:
- default:
- {
- GstPad *peer;
-
- if ((peer = gst_pad_get_peer (demux->sinkpad))) {
- /* query latency on peer pad */
- res = gst_pad_query (peer, query);
- gst_object_unref (peer);
- } else {
- /* no peer, we don't know */
- res = FALSE;
- }
- break;
- }
- }
-
-beach:
- gst_object_unref (demux);
-
- return res;
-}
-
-static GstStateChangeReturn
-gst_flv_demux_change_state (GstElement * element, GstStateChange transition)
-{
- GstFLVDemux *demux;
- GstStateChangeReturn ret;
-
- demux = GST_FLV_DEMUX (element);
-
- switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- /* If this is our own index destroy it as the
- * old entries might be wrong for the new stream */
- if (demux->own_index) {
- gst_object_unref (demux->index);
- demux->index = NULL;
- demux->own_index = FALSE;
- }
-
- /* If no index was created, generate one */
- if (G_UNLIKELY (!demux->index)) {
- GST_DEBUG_OBJECT (demux, "no index provided creating our own");
-
- demux->index = gst_index_factory_make ("memindex");
-
- gst_index_get_writer_id (demux->index, GST_OBJECT (demux),
- &demux->index_id);
- demux->own_index = TRUE;
- }
- gst_flv_demux_cleanup (demux);
- break;
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
- if (ret == GST_STATE_CHANGE_FAILURE)
- return ret;
-
- switch (transition) {
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- gst_flv_demux_cleanup (demux);
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-static void
-gst_flv_demux_set_index (GstElement * element, GstIndex * index)
-{
- GstFLVDemux *demux = GST_FLV_DEMUX (element);
-
- GST_OBJECT_LOCK (demux);
- if (demux->index)
- gst_object_unref (demux->index);
- demux->index = gst_object_ref (index);
- GST_OBJECT_UNLOCK (demux);
-
- gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
- demux->own_index = FALSE;
-}
-
-static GstIndex *
-gst_flv_demux_get_index (GstElement * element)
-{
- GstIndex *result = NULL;
-
- GstFLVDemux *demux = GST_FLV_DEMUX (element);
-
- GST_OBJECT_LOCK (demux);
- if (demux->index)
- result = gst_object_ref (demux->index);
- GST_OBJECT_UNLOCK (demux);
-
- return result;
-}
-
-static void
-gst_flv_demux_dispose (GObject * object)
-{
- GstFLVDemux *demux = GST_FLV_DEMUX (object);
-
- GST_DEBUG_OBJECT (demux, "disposing FLV demuxer");
-
- if (demux->adapter) {
- gst_adapter_clear (demux->adapter);
- g_object_unref (demux->adapter);
- demux->adapter = NULL;
- }
-
- if (demux->taglist) {
- gst_tag_list_free (demux->taglist);
- demux->taglist = NULL;
- }
-
- if (demux->new_seg_event) {
- gst_event_unref (demux->new_seg_event);
- demux->new_seg_event = NULL;
- }
-
- if (demux->close_seg_event) {
- gst_event_unref (demux->close_seg_event);
- demux->close_seg_event = NULL;
- }
-
- if (demux->audio_codec_data) {
- gst_buffer_unref (demux->audio_codec_data);
- demux->audio_codec_data = NULL;
- }
-
- if (demux->video_codec_data) {
- gst_buffer_unref (demux->video_codec_data);
- demux->video_codec_data = NULL;
- }
-
- if (demux->audio_pad) {
- gst_object_unref (demux->audio_pad);
- demux->audio_pad = NULL;
- }
-
- if (demux->video_pad) {
- gst_object_unref (demux->video_pad);
- demux->video_pad = NULL;
- }
-
- if (demux->index) {
- gst_object_unref (demux->index);
- demux->index = NULL;
- }
-
- if (demux->times) {
- g_array_free (demux->times, TRUE);
- demux->times = NULL;
- }
-
- if (demux->filepositions) {
- g_array_free (demux->filepositions, TRUE);
- demux->filepositions = NULL;
- }
-
- GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
-}
-
-static void
-gst_flv_demux_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&flv_sink_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&audio_src_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&video_src_template));
- gst_element_class_set_details_simple (element_class, "FLV Demuxer",
- "Codec/Demuxer",
- "Demux FLV feeds into digital streams",
- "Julien Moutte <julien@moutte.net>");
-}
-
-static void
-gst_flv_demux_class_init (GstFLVDemuxClass * klass)
-{
- GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
- gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_flv_demux_dispose);
-
- gstelement_class->change_state =
- GST_DEBUG_FUNCPTR (gst_flv_demux_change_state);
- gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_flv_demux_set_index);
- gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_flv_demux_get_index);
-}
-
-static void
-gst_flv_demux_init (GstFLVDemux * demux, GstFLVDemuxClass * g_class)
-{
- demux->sinkpad =
- gst_pad_new_from_static_template (&flv_sink_template, "sink");
-
- gst_pad_set_event_function (demux->sinkpad,
- GST_DEBUG_FUNCPTR (gst_flv_demux_sink_event));
- gst_pad_set_chain_function (demux->sinkpad,
- GST_DEBUG_FUNCPTR (gst_flv_demux_chain));
- gst_pad_set_activate_function (demux->sinkpad,
- GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate));
- gst_pad_set_activatepull_function (demux->sinkpad,
- GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate_pull));
- gst_pad_set_activatepush_function (demux->sinkpad,
- GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate_push));
-
- gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
-
- demux->adapter = gst_adapter_new ();
- demux->taglist = gst_tag_list_new ();
- gst_segment_init (&demux->segment, GST_FORMAT_TIME);
-
- demux->own_index = FALSE;
-
- gst_flv_demux_cleanup (demux);
-}
-
-static gboolean
-plugin_init (GstPlugin * plugin)
-{
- GST_DEBUG_CATEGORY_INIT (flvdemux_debug, "flvdemux", 0, "FLV demuxer");
-
- if (!gst_element_register (plugin, "flvdemux", GST_RANK_PRIMARY,
- gst_flv_demux_get_type ()) ||
- !gst_element_register (plugin, "flvmux", GST_RANK_PRIMARY,
- gst_flv_mux_get_type ()))
- return FALSE;
-
- return TRUE;
-}
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
- "flv", "FLV muxing and demuxing plugin",
- plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Julien Moutte <julien@moutte.net>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __FLV_DEMUX_H__
-#define __FLV_DEMUX_H__
-
-#include <gst/gst.h>
-#include <gst/base/gstadapter.h>
-
-G_BEGIN_DECLS
-#define GST_TYPE_FLV_DEMUX \
- (gst_flv_demux_get_type())
-#define GST_FLV_DEMUX(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FLV_DEMUX,GstFLVDemux))
-#define GST_FLV_DEMUX_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FLV_DEMUX,GstFLVDemuxClass))
-#define GST_IS_FLV_DEMUX(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FLV_DEMUX))
-#define GST_IS_FLV_DEMUX_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FLV_DEMUX))
-typedef struct _GstFLVDemux GstFLVDemux;
-typedef struct _GstFLVDemuxClass GstFLVDemuxClass;
-
-typedef enum
-{
- FLV_STATE_HEADER,
- FLV_STATE_TAG_TYPE,
- FLV_STATE_TAG_VIDEO,
- FLV_STATE_TAG_AUDIO,
- FLV_STATE_TAG_SCRIPT,
- FLV_STATE_DONE,
- FLV_STATE_NONE
-} GstFLVDemuxState;
-
-struct _GstFLVDemux
-{
- GstElement element;
-
- GstPad *sinkpad;
-
- GstPad *audio_pad;
- GstPad *video_pad;
-
- /* <private> */
-
- GstIndex *index;
- gint index_id;
- gboolean own_index;
-
- GArray * times;
- GArray * filepositions;
-
- GstAdapter *adapter;
-
- GstSegment segment;
-
- GstEvent *close_seg_event;
- GstEvent *new_seg_event;
-
- GstTagList *taglist;
-
- GstFLVDemuxState state;
-
- guint64 offset;
- guint64 cur_tag_offset;
- GstClockTime duration;
- guint64 tag_size;
- guint64 tag_data_size;
-
- /* Audio infos */
- guint16 rate;
- guint16 channels;
- guint16 width;
- guint16 audio_codec_tag;
- guint64 audio_offset;
- gboolean audio_need_discont;
- gboolean audio_need_segment;
- gboolean audio_linked;
- GstBuffer * audio_codec_data;
-
- /* Video infos */
- guint32 w;
- guint32 h;
- guint32 par_x;
- guint32 par_y;
- guint16 video_codec_tag;
- guint64 video_offset;
- gboolean video_need_discont;
- gboolean video_need_segment;
- gboolean video_linked;
- gboolean got_par;
- GstBuffer * video_codec_data;
-
- gboolean random_access;
- gboolean need_header;
- gboolean has_audio;
- gboolean has_video;
- gboolean push_tags;
- gboolean strict;
- gboolean flushing;
-};
-
-struct _GstFLVDemuxClass
-{
- GstElementClass parent_class;
-};
-
-GType gst_flv_demux_get_type (void);
-
-gboolean gst_flv_demux_query (GstPad * pad, GstQuery * query);
-gboolean gst_flv_demux_src_event (GstPad * pad, GstEvent * event);
-
-G_END_DECLS
-#endif /* __FLV_DEMUX_H__ */
+++ /dev/null
-/* GStreamer
- *
- * Copyright (c) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/**
- * SECTION:element-flvmux
- *
- * flvmux muxes different streams into an FLV file.
- *
- * <refsect2>
- * <title>Example launch line</title>
- * |[
- * gst-launch -v filesrc location=/path/to/audio ! decodebin2 ! queue ! flvmux name=m ! filesink location=file.flv filesrc location=/path/to/video ! decodebin2 ! queue ! m.
- * ]| This pipeline muxes an audio and video file into a single FLV file.
- * </refsect2>
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <math.h>
-#include <string.h>
-
-#include "gstflvmux.h"
-
-GST_DEBUG_CATEGORY_STATIC (flvmux_debug);
-#define GST_CAT_DEFAULT flvmux_debug
-
-static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("video/x-flv")
- );
-
-static GstStaticPadTemplate videosink_templ = GST_STATIC_PAD_TEMPLATE ("video",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS ("video/x-flash-video; "
- "video/x-flash-screen; "
- "video/x-vp6-flash; " "video/x-vp6-alpha; " "video/x-h264;")
- );
-
-static GstStaticPadTemplate audiosink_templ = GST_STATIC_PAD_TEMPLATE ("audio",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS
- ("audio/x-adpcm, layout = (string) swf, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
- "audio/mpeg, mpegversion = (int) 1, layer = (int) 3, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 22050, 44100 }, parsed = (boolean) TRUE; "
- "audio/mpeg, mpegversion = (int) 4, framed = (boolean) TRUE; "
- "audio/x-nellymoser, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 16000, 22050, 44100 }; "
- "audio/x-raw-int, endianness = (int) LITTLE_ENDIAN, channels = (int) { 1, 2 }, width = (int) 8, depth = (int) 8, rate = (int) { 5512, 11025, 22050, 44100 }, signed = (boolean) FALSE; "
- "audio/x-raw-int, endianness = (int) LITTLE_ENDIAN, channels = (int) { 1, 2 }, width = (int) 16, depth = (int) 16, rate = (int) { 5512, 11025, 22050, 44100 }, signed = (boolean) TRUE; "
- "audio/x-alaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
- "audio/x-mulaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
- "audio/x-speex, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 };")
- );
-
-#define _do_init(type) \
- G_STMT_START{ \
- static const GInterfaceInfo tag_setter_info = { \
- NULL, \
- NULL, \
- NULL \
- }; \
- g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, \
- &tag_setter_info); \
- }G_STMT_END
-
-GST_BOILERPLATE_FULL (GstFlvMux, gst_flv_mux, GstElement, GST_TYPE_ELEMENT,
- _do_init);
-
-static void gst_flv_mux_finalize (GObject * object);
-static GstFlowReturn
-gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data);
-
-static gboolean gst_flv_mux_handle_src_event (GstPad * pad, GstEvent * event);
-static GstPad *gst_flv_mux_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * name);
-static void gst_flv_mux_release_pad (GstElement * element, GstPad * pad);
-
-static GstStateChangeReturn
-gst_flv_mux_change_state (GstElement * element, GstStateChange transition);
-
-static void gst_flv_mux_reset (GstElement * element);
-
-static void
-gst_flv_mux_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&videosink_templ));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&audiosink_templ));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&src_templ));
- gst_element_class_set_details_simple (element_class, "FLV muxer",
- "Codec/Muxer",
- "Muxes video/audio streams into a FLV stream",
- "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
-
- GST_DEBUG_CATEGORY_INIT (flvmux_debug, "flvmux", 0, "FLV muxer");
-}
-
-static void
-gst_flv_mux_class_init (GstFlvMuxClass * klass)
-{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
-
- GST_DEBUG_CATEGORY_INIT (flvmux_debug, "flvmux", 0, "FLV muxer");
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
-
- gobject_class->finalize = gst_flv_mux_finalize;
-
- gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_flv_mux_change_state);
- gstelement_class->request_new_pad =
- GST_DEBUG_FUNCPTR (gst_flv_mux_request_new_pad);
- gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_flv_mux_release_pad);
-}
-
-static void
-gst_flv_mux_init (GstFlvMux * mux, GstFlvMuxClass * g_class)
-{
- mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
- gst_pad_set_event_function (mux->srcpad, gst_flv_mux_handle_src_event);
- gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
-
- mux->collect = gst_collect_pads_new ();
- gst_collect_pads_set_function (mux->collect,
- (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_flv_mux_collected), mux);
-
- gst_flv_mux_reset (GST_ELEMENT (mux));
-}
-
-static void
-gst_flv_mux_finalize (GObject * object)
-{
- GstFlvMux *mux = GST_FLV_MUX (object);
-
- gst_object_unref (mux->collect);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-gst_flv_mux_reset (GstElement * element)
-{
- GstFlvMux *mux = GST_FLV_MUX (element);
- GSList *sl;
-
- while ((sl = mux->collect->data) != NULL) {
- GstFlvPad *cpad = (GstFlvPad *) sl->data;
-
- if (cpad->audio_codec_data)
- gst_buffer_unref (cpad->audio_codec_data);
- if (cpad->video_codec_data)
- gst_buffer_unref (cpad->video_codec_data);
-
- gst_collect_pads_remove_pad (mux->collect, cpad->collect.pad);
- }
-
- if (mux->tags)
- gst_tag_list_free (mux->tags);
- mux->tags = NULL;
-
- mux->state = GST_FLV_MUX_STATE_HEADER;
-}
-
-static gboolean
-gst_flv_mux_handle_src_event (GstPad * pad, GstEvent * event)
-{
- GstEventType type;
-
- type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
-
- switch (type) {
- case GST_EVENT_SEEK:
- /* disable seeking for now */
- return FALSE;
- default:
- break;
- }
-
- return gst_pad_event_default (pad, event);
-}
-
-static gboolean
-gst_flv_mux_handle_sink_event (GstPad * pad, GstEvent * event)
-{
- GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
- gboolean ret = TRUE;
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_TAG:{
- GstTagList *tags;
-
- if (!mux->tags)
- mux->tags = gst_tag_list_new ();
-
- gst_event_parse_tag (event, &tags);
- if (tags) {
- gst_tag_list_insert (mux->tags, tags,
- gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
- }
-
- break;
- }
- case GST_EVENT_NEWSEGMENT:
- /* We don't support NEWSEGMENT events */
- ret = FALSE;
- gst_event_unref (event);
- break;
- default:
- break;
- }
-
- /* now GstCollectPads can take care of the rest, e.g. EOS */
- if (ret)
- ret = mux->collect_event (pad, event);
- gst_object_unref (mux);
-
- return ret;
-}
-
-static gboolean
-gst_flv_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
-{
- GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
- GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
- gboolean ret = TRUE;
- GstStructure *s;
-
- s = gst_caps_get_structure (caps, 0);
-
- if (strcmp (gst_structure_get_name (s), "video/x-flash-video") == 0) {
- cpad->video_codec = 2;
- } else if (strcmp (gst_structure_get_name (s), "video/x-flash-screen") == 0) {
- cpad->video_codec = 3;
- } else if (strcmp (gst_structure_get_name (s), "video/x-vp6-flash") == 0) {
- cpad->video_codec = 4;
- } else if (strcmp (gst_structure_get_name (s), "video/x-vp6-alpha") == 0) {
- cpad->video_codec = 5;
- } else if (strcmp (gst_structure_get_name (s), "video/x-h264") == 0) {
- cpad->video_codec = 7;
- } else {
- ret = FALSE;
- }
-
- if (ret && gst_structure_has_field (s, "codec_data")) {
- const GValue *val = gst_structure_get_value (s, "codec_data");
-
- if (val) {
- cpad->video_codec_data = gst_buffer_ref (gst_value_get_buffer (val));
- cpad->sent_codec_data = FALSE;
- } else {
- cpad->sent_codec_data = TRUE;
- }
- } else {
- cpad->sent_codec_data = TRUE;
- }
-
- gst_object_unref (mux);
-
- return ret;
-}
-
-static gboolean
-gst_flv_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
-{
- GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
- GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
- gboolean ret = TRUE;
- GstStructure *s;
-
- s = gst_caps_get_structure (caps, 0);
-
- if (strcmp (gst_structure_get_name (s), "audio/x-adpcm") == 0) {
- const gchar *layout = gst_structure_get_string (s, "layout");
- if (layout && strcmp (layout, "swf") == 0) {
- cpad->audio_codec = 1;
- } else {
- ret = FALSE;
- }
- } else if (strcmp (gst_structure_get_name (s), "audio/mpeg") == 0) {
- gint mpegversion;
-
- if (gst_structure_get_int (s, "mpegversion", &mpegversion)) {
- if (mpegversion == 1) {
- gint layer;
-
- if (gst_structure_get_int (s, "layer", &layer) && layer == 3) {
- gint rate;
-
- if (gst_structure_get_int (s, "rate", &rate) && rate == 8000)
- cpad->audio_codec = 14;
- else
- cpad->audio_codec = 2;
- } else {
- ret = FALSE;
- }
- } else if (mpegversion == 4) {
- cpad->audio_codec = 10;
- } else {
- ret = FALSE;
- }
- } else {
- ret = FALSE;
- }
- } else if (strcmp (gst_structure_get_name (s), "audio/x-nellymoser") == 0) {
- gint rate, channels;
-
- if (gst_structure_get_int (s, "rate", &rate)
- && gst_structure_get_int (s, "channels", &channels)) {
- if (channels == 1 && rate == 16000)
- cpad->audio_codec = 4;
- else if (channels == 1 && rate == 8000)
- cpad->audio_codec = 5;
- } else {
- cpad->audio_codec = 6;
- }
- } else if (strcmp (gst_structure_get_name (s), "audio/x-raw-int") == 0) {
- gint endianness;
-
- if (gst_structure_get_int (s, "endianness", &endianness)
- && endianness == G_LITTLE_ENDIAN)
- cpad->audio_codec = 3;
- else
- ret = FALSE;
- } else if (strcmp (gst_structure_get_name (s), "audio/x-alaw") == 0) {
- cpad->audio_codec = 7;
- } else if (strcmp (gst_structure_get_name (s), "audio/x-mulaw") == 0) {
- cpad->audio_codec = 8;
- } else if (strcmp (gst_structure_get_name (s), "audio/x-speex") == 0) {
- cpad->audio_codec = 11;
- } else {
- ret = FALSE;
- }
-
- if (ret) {
- gint rate, channels, width;
-
- if (gst_structure_get_int (s, "rate", &rate)) {
- if (cpad->audio_codec == 10)
- cpad->rate = 3;
- else if (rate == 5512)
- cpad->rate = 0;
- else if (rate == 11025)
- cpad->rate = 1;
- else if (rate == 22050)
- cpad->rate = 2;
- else if (rate == 44100)
- cpad->rate = 3;
- else if (rate == 8000 && (cpad->audio_codec == 5
- || cpad->audio_codec == 14))
- cpad->rate = 0;
- else if (rate == 16000 && cpad->audio_codec == 4)
- cpad->rate = 0;
- else
- ret = FALSE;
- } else if (cpad->audio_codec == 10) {
- cpad->rate = 3;
- } else {
- ret = FALSE;
- }
-
- if (gst_structure_get_int (s, "channels", &channels)) {
- if (cpad->audio_codec == 4 || cpad->audio_codec == 5
- || cpad->audio_codec == 6)
- cpad->channels = 0;
- else if (cpad->audio_codec == 10)
- cpad->channels = 1;
- else if (channels == 1)
- cpad->channels = 0;
- else if (channels == 2)
- cpad->channels = 1;
- else
- ret = FALSE;
- } else if (cpad->audio_codec == 4 || cpad->audio_codec == 5
- || cpad->audio_codec == 6) {
- cpad->channels = 0;
- } else if (cpad->audio_codec == 10) {
- cpad->channels = 1;
- } else {
- ret = FALSE;
- }
-
- if (gst_structure_get_int (s, "width", &width)) {
- if (cpad->audio_codec != 3)
- cpad->width = 1;
- else if (width == 8)
- cpad->width = 0;
- else if (width == 16)
- cpad->width = 1;
- else
- ret = FALSE;
- } else if (cpad->audio_codec != 3) {
- cpad->width = 1;
- } else {
- ret = FALSE;
- }
- }
-
- if (ret && gst_structure_has_field (s, "codec_data")) {
- const GValue *val = gst_structure_get_value (s, "codec_data");
-
- if (val) {
- cpad->audio_codec_data = gst_buffer_ref (gst_value_get_buffer (val));
- cpad->sent_codec_data = FALSE;
- } else {
- cpad->sent_codec_data = TRUE;
- }
- } else {
- cpad->sent_codec_data = TRUE;
- }
-
- gst_object_unref (mux);
-
- return ret;
-}
-
-static GstPad *
-gst_flv_mux_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * pad_name)
-{
- GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
- GstFlvMux *mux = GST_FLV_MUX (element);
- GstFlvPad *cpad;
- GstPad *pad = NULL;
- const gchar *name = NULL;
- GstPadSetCapsFunction setcapsfunc = NULL;
- gboolean video;
-
- if (mux->state != GST_FLV_MUX_STATE_HEADER) {
- GST_WARNING_OBJECT (mux, "Can't request pads after writing header");
- return NULL;
- }
-
- if (templ == gst_element_class_get_pad_template (klass, "audio")) {
- if (mux->have_audio) {
- GST_WARNING_OBJECT (mux, "Already have an audio pad");
- return NULL;
- }
- mux->have_audio = TRUE;
- name = "audio";
- video = FALSE;
- setcapsfunc = GST_DEBUG_FUNCPTR (gst_flv_mux_audio_pad_setcaps);
- } else if (templ == gst_element_class_get_pad_template (klass, "video")) {
- if (mux->have_video) {
- GST_WARNING_OBJECT (mux, "Already have a video pad");
- return NULL;
- }
- mux->have_video = TRUE;
- name = "video";
- video = TRUE;
- setcapsfunc = GST_DEBUG_FUNCPTR (gst_flv_mux_video_pad_setcaps);
- } else {
- GST_WARNING_OBJECT (mux, "Invalid template");
- return NULL;
- }
-
- pad = gst_pad_new_from_template (templ, name);
- cpad = (GstFlvPad *)
- gst_collect_pads_add_pad (mux->collect, pad, sizeof (GstFlvPad));
-
- cpad->video = video;
-
- cpad->audio_codec = G_MAXUINT;
- cpad->rate = G_MAXUINT;
- cpad->width = G_MAXUINT;
- cpad->channels = G_MAXUINT;
- cpad->audio_codec_data = NULL;
-
- cpad->video_codec = G_MAXUINT;
- cpad->video_codec_data = NULL;
-
- cpad->sent_codec_data = FALSE;
-
- cpad->last_timestamp = 0;
-
- /* FIXME: hacked way to override/extend the event function of
- * GstCollectPads; because it sets its own event function giving the
- * element no access to events.
- */
- mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (pad);
- gst_pad_set_event_function (pad,
- GST_DEBUG_FUNCPTR (gst_flv_mux_handle_sink_event));
-
- gst_pad_set_setcaps_function (pad, setcapsfunc);
- gst_pad_set_active (pad, TRUE);
- gst_element_add_pad (element, pad);
-
- return pad;
-}
-
-static void
-gst_flv_mux_release_pad (GstElement * element, GstPad * pad)
-{
- GstFlvMux *mux = GST_FLV_MUX (GST_PAD_PARENT (pad));
- GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
-
- if (cpad && cpad->audio_codec_data)
- gst_buffer_unref (cpad->audio_codec_data);
- if (cpad && cpad->video_codec_data)
- gst_buffer_unref (cpad->video_codec_data);
-
- gst_collect_pads_remove_pad (mux->collect, pad);
- gst_element_remove_pad (element, pad);
-}
-
-static GstFlowReturn
-gst_flv_mux_write_metadata (GstFlvMux * mux)
-{
- GstTagList *merged_tags;
- const GstTagList *user_tags;
- GstFlowReturn ret = GST_FLOW_OK;
- GstBuffer *script_tag, *tmp;
- guint8 *data;
- gint i, n_tags, tags_written = 0;
-
- user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
- GST_DEBUG_OBJECT (mux, "upstream tags = %" GST_PTR_FORMAT, mux->tags);
- GST_DEBUG_OBJECT (mux, "user-set tags = %" GST_PTR_FORMAT, user_tags);
-
- merged_tags = gst_tag_list_merge (user_tags, mux->tags,
- gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
-
- GST_DEBUG_OBJECT (mux, "merged tags = %" GST_PTR_FORMAT, merged_tags);
-
- script_tag = gst_buffer_new_and_alloc (11);
- data = GST_BUFFER_DATA (script_tag);
-
- data[0] = 18;
-
- /* Data size, unknown for now */
- data[1] = 0;
- data[2] = 0;
- data[3] = 0;
-
- /* Timestamp */
- data[4] = data[5] = data[6] = data[7] = 0;
-
- /* Stream ID */
- data[8] = data[9] = data[10] = 0;
-
- tmp = gst_buffer_new_and_alloc (13);
- data = GST_BUFFER_DATA (tmp);
- data[0] = 2; /* string */
- data[1] = 0;
- data[2] = 0x0a; /* length 10 */
- memcpy (&data[3], "onMetaData", sizeof ("onMetaData"));
-
- script_tag = gst_buffer_join (script_tag, tmp);
-
- n_tags =
- (merged_tags) ? gst_structure_n_fields ((GstStructure *) merged_tags) : 0;
- tmp = gst_buffer_new_and_alloc (5);
- data = GST_BUFFER_DATA (tmp);
- data[0] = 8; /* ECMA array */
- GST_WRITE_UINT32_BE (data + 1, n_tags);
- script_tag = gst_buffer_join (script_tag, tmp);
-
- for (i = 0; merged_tags && i < n_tags; i++) {
- const gchar *tag_name =
- gst_structure_nth_field_name ((const GstStructure *) merged_tags, i);
- if (!strcmp (tag_name, GST_TAG_DURATION)) {
- gdouble d;
- guint64 dur;
-
- if (!gst_tag_list_get_uint64 (merged_tags, GST_TAG_DURATION, &dur))
- continue;
-
- d = gst_guint64_to_gdouble (dur);
- d /= (gdouble) GST_SECOND;
-
- tmp = gst_buffer_new_and_alloc (2 + 8 + 1 + 8);
- data = GST_BUFFER_DATA (tmp);
- data[0] = 0; /* 8 bytes name */
- data[1] = 8;
- memcpy (&data[2], "duration", sizeof ("duration"));
- data[10] = 0; /* double */
- GST_WRITE_DOUBLE_BE (data + 11, d);
- script_tag = gst_buffer_join (script_tag, tmp);
- tags_written++;
- } else if (!strcmp (tag_name, GST_TAG_ARTIST) ||
- !strcmp (tag_name, GST_TAG_TITLE)) {
- gchar *s;
- const gchar *t;
-
- if (!strcmp (tag_name, GST_TAG_ARTIST))
- t = "creator";
- else if (!strcmp (tag_name, GST_TAG_TITLE))
- t = "title";
-
- if (!gst_tag_list_get_string (merged_tags, tag_name, &s))
- continue;
-
- tmp = gst_buffer_new_and_alloc (2 + strlen (t) + 1 + 2 + strlen (s));
- data = GST_BUFFER_DATA (tmp);
- data[0] = 0; /* tag name length */
- data[1] = strlen (t);
- memcpy (&data[2], t, strlen (t));
- data[2 + strlen (t)] = 2; /* string */
- data[3 + strlen (t)] = (strlen (s) >> 8) & 0xff;
- data[4 + strlen (t)] = (strlen (s)) & 0xff;
- memcpy (&data[5 + strlen (t)], s, strlen (s));
- script_tag = gst_buffer_join (script_tag, tmp);
-
- g_free (s);
- tags_written++;
- }
- }
-
- if (mux->have_video) {
- GstPad *video_pad = NULL;
- GSList *l = mux->collect->data;
-
- for (; l; l = l->next) {
- GstFlvPad *cpad = l->data;
- if (cpad && cpad->video) {
- video_pad = cpad->collect.pad;
- break;
- }
- }
-
- if (video_pad && GST_PAD_CAPS (video_pad)) {
- GstStructure *s = gst_caps_get_structure (GST_PAD_CAPS (video_pad), 0);
- gint par_x, par_y;
-
- if (gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_x, &par_y)) {
- gdouble d;
-
- d = par_x;
- tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 8);
- data = GST_BUFFER_DATA (tmp);
- data[0] = 0; /* 12 bytes name */
- data[1] = 12;
- memcpy (&data[2], "AspectRatioX", sizeof ("AspectRatioX"));
- data[14] = 0; /* double */
- GST_WRITE_DOUBLE_BE (data + 15, d);
- script_tag = gst_buffer_join (script_tag, tmp);
- tags_written++;
-
- d = par_y;
- tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 8);
- data = GST_BUFFER_DATA (tmp);
- data[0] = 0; /* 12 bytes name */
- data[1] = 12;
- memcpy (&data[2], "AspectRatioY", sizeof ("AspectRatioY"));
- data[14] = 0; /* double */
- GST_WRITE_DOUBLE_BE (data + 15, d);
- script_tag = gst_buffer_join (script_tag, tmp);
- tags_written++;
- }
- }
- }
-
- {
- const gchar *s = "GStreamer FLV muxer";
-
- tmp = gst_buffer_new_and_alloc (2 + 15 + 1 + 2 + strlen (s));
- data = GST_BUFFER_DATA (tmp);
- data[0] = 0; /* 15 bytes name */
- data[1] = 15;
- memcpy (&data[2], "metadatacreator", sizeof ("metadatacreator"));
- data[17] = 2; /* string */
- data[18] = (strlen (s) >> 8) & 0xff;
- data[19] = (strlen (s)) & 0xff;
- memcpy (&data[20], s, strlen (s));
- script_tag = gst_buffer_join (script_tag, tmp);
-
- tags_written++;
- }
-
- {
- GTimeVal tv = { 0, };
- time_t secs;
- struct tm *tm;
- gchar *s;
- static const gchar *weekdays[] = {
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
- };
- static const gchar *months[] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
- "Aug", "Sep", "Oct", "Nov", "Dec"
- };
-
- g_get_current_time (&tv);
- secs = tv.tv_sec;
- tm = gmtime (&secs);
-
- s = g_strdup_printf ("%s %s %d %d:%d:%d %d", weekdays[tm->tm_wday],
- months[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
- tm->tm_year + 1900);
-
- tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 2 + strlen (s));
- data = GST_BUFFER_DATA (tmp);
- data[0] = 0; /* 12 bytes name */
- data[1] = 12;
- memcpy (&data[2], "creationdate", sizeof ("creationdate"));
- data[14] = 2; /* string */
- data[15] = (strlen (s) >> 8) & 0xff;
- data[16] = (strlen (s)) & 0xff;
- memcpy (&data[17], s, strlen (s));
- script_tag = gst_buffer_join (script_tag, tmp);
-
- g_free (s);
- tags_written++;
- }
-
- tmp = gst_buffer_new_and_alloc (2 + 0 + 1);
- data = GST_BUFFER_DATA (tmp);
- data[0] = 0; /* 0 byte size */
- data[1] = 0;
- data[2] = 9; /* end marker */
- script_tag = gst_buffer_join (script_tag, tmp);
- tags_written++;
-
-
- tmp = gst_buffer_new_and_alloc (4);
- data = GST_BUFFER_DATA (tmp);
- GST_WRITE_UINT32_BE (data, GST_BUFFER_SIZE (script_tag));
- script_tag = gst_buffer_join (script_tag, tmp);
-
- data = GST_BUFFER_DATA (script_tag);
- data[1] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 16) & 0xff;
- data[2] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 8) & 0xff;
- data[3] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 0) & 0xff;
-
- GST_WRITE_UINT32_BE (data + 11 + 13 + 1, tags_written);
-
- gst_buffer_set_caps (script_tag, GST_PAD_CAPS (mux->srcpad));
- ret = gst_pad_push (mux->srcpad, script_tag);
-
- if (merged_tags)
- gst_tag_list_free (merged_tags);
-
- return ret;
-}
-
-static GstFlowReturn
-gst_flv_mux_write_header (GstFlvMux * mux)
-{
- GstBuffer *header = gst_buffer_new_and_alloc (9 + 4);
- guint8 *data = GST_BUFFER_DATA (header);
- GstFlowReturn ret;
-
- if (GST_PAD_CAPS (mux->srcpad) == NULL) {
- GstCaps *caps = gst_caps_new_simple ("video/x-flv", NULL);
-
- gst_pad_set_caps (mux->srcpad, caps);
- gst_caps_unref (caps);
- }
- gst_buffer_set_caps (header, GST_PAD_CAPS (mux->srcpad));
-
- data[0] = 'F';
- data[1] = 'L';
- data[2] = 'V';
- data[3] = 0x01; /* Version */
-
- data[4] = (mux->have_audio << 2) | mux->have_video; /* flags */
- GST_WRITE_UINT32_BE (data + 5, 9); /* data offset */
-
- GST_WRITE_UINT32_BE (data + 9, 0); /* previous tag size */
-
- ret = gst_pad_push (mux->srcpad, header);
- if (ret != GST_FLOW_OK)
- return ret;
-
- return gst_flv_mux_write_metadata (mux);
-}
-
-static GstFlowReturn
-gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvPad * cpad)
-{
- GstBuffer *tag;
- guint8 *data;
- guint size;
- GstBuffer *buffer =
- gst_collect_pads_pop (mux->collect, (GstCollectData *) cpad);
- guint32 timestamp =
- (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) ? GST_BUFFER_TIMESTAMP (buffer) /
- GST_MSECOND : cpad->last_timestamp / GST_MSECOND;
- gboolean second_run = FALSE;
- GstFlowReturn ret;
-
-next:
- size = 11;
- if (cpad->video) {
- size += 1;
- if (cpad->video_codec == 7 && !cpad->sent_codec_data)
- size += 4 + GST_BUFFER_SIZE (cpad->video_codec_data);
- else if (cpad->video_codec == 7)
- size += 4 + GST_BUFFER_SIZE (buffer);
- else
- size += GST_BUFFER_SIZE (buffer);
- } else {
- size += 1;
- if (cpad->audio_codec == 10 && !cpad->sent_codec_data)
- size += 1 + GST_BUFFER_SIZE (cpad->audio_codec_data);
- else if (cpad->audio_codec == 10)
- size += 1 + GST_BUFFER_SIZE (buffer);
- else
- size += GST_BUFFER_SIZE (buffer);
- }
- size += 4;
-
- tag = gst_buffer_new_and_alloc (size);
- data = GST_BUFFER_DATA (tag);
- memset (data, 0, size);
-
- data[0] = (cpad->video) ? 9 : 8;
-
- data[1] = ((size - 11 - 4) >> 16) & 0xff;
- data[2] = ((size - 11 - 4) >> 8) & 0xff;
- data[3] = ((size - 11 - 4) >> 0) & 0xff;
-
- data[4] = (timestamp >> 16) & 0xff;
- data[5] = (timestamp >> 8) & 0xff;
- data[6] = (timestamp >> 0) & 0xff;
- data[7] = (timestamp >> 24) & 0xff;
-
- data[8] = data[9] = data[10] = 0;
-
- if (cpad->video) {
- if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT))
- data[11] |= 2 << 4;
- else
- data[11] |= 1 << 4;
-
- data[11] |= cpad->video_codec & 0x0f;
-
- if (cpad->video_codec == 7 && !cpad->sent_codec_data) {
- data[12] = 0;
- data[13] = data[14] = data[15] = 0;
-
- memcpy (data + 11 + 1 + 4, GST_BUFFER_DATA (cpad->video_codec_data),
- GST_BUFFER_SIZE (cpad->video_codec_data));
- second_run = TRUE;
- } else if (cpad->video_codec == 7) {
- data[12] = 1;
-
- /* FIXME: what to do about composition time */
- data[13] = data[14] = data[15] = 0;
-
- memcpy (data + 11 + 1 + 4, GST_BUFFER_DATA (buffer),
- GST_BUFFER_SIZE (buffer));
- } else {
- memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer),
- GST_BUFFER_SIZE (buffer));
- }
- } else {
- data[11] |= (cpad->audio_codec << 4) & 0xf0;
- data[11] |= (cpad->rate << 2) & 0x0c;
- data[11] |= (cpad->width << 1) & 0x02;
- data[11] |= (cpad->channels << 0) & 0x01;
-
- if (cpad->audio_codec == 10 && !cpad->sent_codec_data) {
- data[12] = 0;
-
- memcpy (data + 11 + 1 + 1, GST_BUFFER_DATA (cpad->audio_codec_data),
- GST_BUFFER_SIZE (cpad->audio_codec_data));
- second_run = TRUE;
- } else if (cpad->audio_codec == 10) {
- data[12] = 1;
-
- memcpy (data + 11 + 1 + 1, GST_BUFFER_DATA (buffer),
- GST_BUFFER_SIZE (buffer));
- } else {
- memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer),
- GST_BUFFER_SIZE (buffer));
- }
- }
-
- GST_WRITE_UINT32_BE (data + size - 4, size - 4);
-
- gst_buffer_set_caps (tag, GST_PAD_CAPS (mux->srcpad));
-
- if (second_run) {
- second_run = FALSE;
- cpad->sent_codec_data = TRUE;
-
- ret = gst_pad_push (mux->srcpad, tag);
- if (ret != GST_FLOW_OK) {
- gst_buffer_unref (buffer);
- return ret;
- }
-
- cpad->last_timestamp = timestamp;
-
- tag = NULL;
- goto next;
- }
-
- gst_buffer_copy_metadata (tag, buffer, GST_BUFFER_COPY_TIMESTAMPS);
- GST_BUFFER_OFFSET (tag) = GST_BUFFER_OFFSET_END (tag) =
- GST_BUFFER_OFFSET_NONE;
-
- gst_buffer_unref (buffer);
-
- ret = gst_pad_push (mux->srcpad, tag);
-
- if (ret == GST_FLOW_OK)
- cpad->last_timestamp = timestamp;
-
- return ret;
-}
-
-static GstFlowReturn
-gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data)
-{
- GstFlvMux *mux = GST_FLV_MUX (user_data);
- GstFlvPad *best;
- GstClockTime best_time;
- GstFlowReturn ret;
- GSList *sl;
- gboolean eos = TRUE;
-
- if (mux->state == GST_FLV_MUX_STATE_HEADER) {
- if (mux->collect->data == NULL) {
- GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
- ("No input streams configured"));
- return GST_FLOW_ERROR;
- }
-
- if (gst_pad_push_event (mux->srcpad,
- gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)))
- ret = gst_flv_mux_write_header (mux);
- else
- ret = GST_FLOW_ERROR;
-
- if (ret != GST_FLOW_OK)
- return ret;
- mux->state = GST_FLV_MUX_STATE_DATA;
- }
-
- best = NULL;
- best_time = GST_CLOCK_TIME_NONE;
- for (sl = mux->collect->data; sl; sl = sl->next) {
- GstFlvPad *cpad = sl->data;
- GstBuffer *buffer = gst_collect_pads_peek (pads, (GstCollectData *) cpad);
- GstClockTime time;
-
- if (!buffer)
- continue;
-
- eos = FALSE;
-
- time = GST_BUFFER_TIMESTAMP (buffer);
- gst_buffer_unref (buffer);
-
- /* Use buffers without valid timestamp first */
- if (!GST_CLOCK_TIME_IS_VALID (time)) {
- GST_WARNING_OBJECT (pads, "Buffer without valid timestamp");
-
- best_time = cpad->last_timestamp;
- best = cpad;
- break;
- }
-
-
- if (best == NULL || (GST_CLOCK_TIME_IS_VALID (best_time)
- && time < best_time)) {
- best = cpad;
- best_time = time;
- }
- }
-
- if (GST_CLOCK_TIME_IS_VALID (best_time)
- && best_time / GST_MSECOND > G_MAXUINT32) {
- GST_WARNING_OBJECT (mux, "Timestamp larger than FLV supports - EOS");
- eos = TRUE;
- }
-
- if (!eos && best) {
- return gst_flv_mux_write_buffer (mux, best);
- } else if (eos) {
- gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
- return GST_FLOW_UNEXPECTED;
- } else {
- return GST_FLOW_OK;
- }
-}
-
-static GstStateChangeReturn
-gst_flv_mux_change_state (GstElement * element, GstStateChange transition)
-{
- GstStateChangeReturn ret;
- GstFlvMux *mux = GST_FLV_MUX (element);
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- gst_collect_pads_start (mux->collect);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- gst_collect_pads_stop (mux->collect);
- break;
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- gst_flv_mux_reset (GST_ELEMENT (mux));
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- break;
- default:
- break;
- }
-
- return ret;
-}
+++ /dev/null
-/* GStreamer
- *
- * Copyright (c) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __GST_FLV_MUX_H__
-#define __GST_FLV_MUX_H__
-
-#include <gst/gst.h>
-#include <gst/base/gstcollectpads.h>
-
-G_BEGIN_DECLS
-
-#define GST_TYPE_FLV_MUX \
- (gst_flv_mux_get_type ())
-#define GST_FLV_MUX(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_FLV_MUX, GstFlvMux))
-#define GST_FLV_MUX_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_FLV_MUX, GstFlvMuxClass))
-#define GST_IS_FLV_MUX(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_FLV_MUX))
-#define GST_IS_FLV_MUX_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_FLV_MUX))
-
-typedef struct
-{
- GstCollectData collect;
-
- gboolean video;
-
- guint audio_codec;
- guint rate;
- guint width;
- guint channels;
- GstBuffer *audio_codec_data;
-
- guint video_codec;
- GstBuffer *video_codec_data;
-
- gboolean sent_codec_data;
- GstClockTime last_timestamp;
-} GstFlvPad;
-
-typedef enum
-{
- GST_FLV_MUX_STATE_HEADER,
- GST_FLV_MUX_STATE_DATA
-} GstFlvMuxState;
-
-typedef struct _GstFlvMux {
- GstElement element;
-
- GstPad *srcpad;
- GstCollectPads *collect;
-
- /* <private> */
- GstPadEventFunction collect_event;
-
- GstFlvMuxState state;
- gboolean have_audio;
- gboolean have_video;
-
- GstTagList *tags;
-} GstFlvMux;
-
-typedef struct _GstFlvMuxClass {
- GstElementClass parent;
-} GstFlvMuxClass;
-
-GType gst_flv_mux_get_type (void);
-
-G_END_DECLS
-
-#endif /* __GST_FLV_MUX_H__ */
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Julien Moutte <julien@moutte.net>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "gstflvparse.h"
-
-#include <gst/base/gstbytereader.h>
-
-#include <string.h>
-
-GST_DEBUG_CATEGORY_EXTERN (flvdemux_debug);
-#define GST_CAT_DEFAULT flvdemux_debug
-
-static gchar *
-FLV_GET_STRING (GstByteReader * reader)
-{
- guint16 string_size = 0;
- gchar *string = NULL;
- const guint8 *str;
-
- g_return_val_if_fail (reader != NULL, NULL);
-
- if (G_UNLIKELY (!gst_byte_reader_get_uint16_be (reader, &string_size)))
- return NULL;
-
- if (G_UNLIKELY (string_size > gst_byte_reader_get_remaining (reader)))
- return NULL;
-
- string = g_try_malloc0 (string_size + 1);
- if (G_UNLIKELY (!string)) {
- return NULL;
- }
-
- if (G_UNLIKELY (!gst_byte_reader_get_data (reader, string_size, &str))) {
- g_free (string);
- return NULL;
- }
-
- memcpy (string, str, string_size);
- if (!g_utf8_validate (string, string_size, NULL)) {
- g_free (string);
- return NULL;
- }
-
- return string;
-}
-
-static const GstQueryType *
-gst_flv_demux_query_types (GstPad * pad)
-{
- static const GstQueryType query_types[] = {
- GST_QUERY_DURATION,
- 0
- };
-
- return query_types;
-}
-
-static void
-parse_flv_date_string (GDate * date, const gchar * s)
-{
- g_date_set_parse (date, s);
- if (g_date_valid (date))
- return;
-
- /* "Fri Oct 15 15:13:16 2004" needs to be parsed */
- {
- static const gchar *months[] = {
- "Jan", "Feb", "Mar", "Apr",
- "May", "Jun", "Jul", "Aug",
- "Sep", "Oct", "Nov", "Dec"
- };
- gchar **tokens = g_strsplit (s, " ", -1);
- guint64 d;
- gchar *endptr;
- gint i;
-
- if (g_strv_length (tokens) != 5)
- goto out;
-
- if (strlen (tokens[1]) != 3)
- goto out;
- for (i = 0; i < 12; i++) {
- if (!strcmp (tokens[1], months[i])) {
- break;
- }
- }
- if (i == 12)
- goto out;
- g_date_set_month (date, i + 1);
-
- d = g_ascii_strtoull (tokens[2], &endptr, 10);
- if (d == 0 && *endptr != '\0')
- goto out;
-
- g_date_set_day (date, d);
-
- d = g_ascii_strtoull (tokens[4], &endptr, 10);
- if (d == 0 && *endptr != '\0')
- goto out;
-
- g_date_set_year (date, d);
-
- out:
- if (tokens)
- g_strfreev (tokens);
- }
-}
-
-static gboolean
-gst_flv_parse_metadata_item (GstFLVDemux * demux, GstByteReader * reader,
- gboolean * end_marker)
-{
- gchar *tag_name = NULL;
- guint8 tag_type = 0;
-
- /* Initialize the end_marker flag to FALSE */
- *end_marker = FALSE;
-
- /* Name of the tag */
- tag_name = FLV_GET_STRING (reader);
- if (G_UNLIKELY (!tag_name)) {
- GST_WARNING_OBJECT (demux, "failed reading tag name");
- return FALSE;
- }
-
- /* What kind of object is that */
- if (!gst_byte_reader_get_uint8 (reader, &tag_type))
- goto error;
-
- GST_DEBUG_OBJECT (demux, "tag name %s, tag type %d", tag_name, tag_type);
-
- switch (tag_type) {
- case 0: // Double
- { /* Use a union to read the uint64 and then as a double */
- gdouble d;
-
- if (!gst_byte_reader_get_float64_be (reader, &d))
- goto error;
-
- GST_DEBUG_OBJECT (demux, "%s => (double) %f", tag_name, d);
-
- if (!strcmp (tag_name, "duration")) {
- demux->duration = d * GST_SECOND;
-
- gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_DURATION, demux->duration, NULL);
- } else if (!strcmp (tag_name, "AspectRatioX")) {
- demux->par_x = d;
- demux->got_par = TRUE;
- } else if (!strcmp (tag_name, "AspectRatioY")) {
- demux->par_y = d;
- demux->got_par = TRUE;
- } else {
- GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name);
- }
-
- break;
- }
- case 1: // Boolean
- {
- guint8 b;
-
- if (!gst_byte_reader_get_uint8 (reader, &b))
- goto error;
-
- GST_DEBUG_OBJECT (demux, "%s => (boolean) %d", tag_name, b);
-
- GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name);
-
- break;
- }
- case 2: // String
- {
- gchar *s = NULL;
-
- s = FLV_GET_STRING (reader);
- if (s == NULL)
- goto error;
-
- GST_DEBUG_OBJECT (demux, "%s => (string) %s", tag_name, s);
-
- if (!strcmp (tag_name, "creationdate")) {
- GDate *date = g_date_new ();
-
- parse_flv_date_string (date, s);
- if (!g_date_valid (date)) {
- GST_DEBUG_OBJECT (demux, "Failed to parse string as date");
- } else {
- gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_DATE, date, NULL);
- }
- g_date_free (date);
- } else if (!strcmp (tag_name, "creator")) {
- gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_ARTIST, s, NULL);
- } else if (!strcmp (tag_name, "title")) {
- gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_TITLE, s, NULL);
- } else if (!strcmp (tag_name, "metadatacreator")) {
- gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_ENCODER, s, NULL);
- } else {
- GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name);
- }
-
- g_free (s);
-
- break;
- }
- case 3: // Object
- {
- gboolean end_of_object_marker = FALSE;
-
- while (!end_of_object_marker) {
- gboolean ok =
- gst_flv_parse_metadata_item (demux, reader, &end_of_object_marker);
-
- if (G_UNLIKELY (!ok)) {
- GST_WARNING_OBJECT (demux, "failed reading a tag, skipping");
- goto error;
- }
- }
-
- break;
- }
- case 8: // ECMA array
- {
- guint32 nb_elems;
- gboolean end_of_object_marker = FALSE;
-
- if (!gst_byte_reader_get_uint32_be (reader, &nb_elems))
- goto error;
-
- GST_DEBUG_OBJECT (demux, "there are approx. %d elements in the array",
- nb_elems);
-
- while (!end_of_object_marker) {
- gboolean ok =
- gst_flv_parse_metadata_item (demux, reader, &end_of_object_marker);
-
- if (G_UNLIKELY (!ok)) {
- GST_WARNING_OBJECT (demux, "failed reading a tag, skipping");
- goto error;
- }
- }
-
- break;
- }
- case 9: // End marker
- {
- GST_DEBUG_OBJECT (demux, "end marker ?");
- if (tag_name[0] == '\0') {
-
- GST_DEBUG_OBJECT (demux, "end marker detected");
-
- *end_marker = TRUE;
- }
-
- break;
- }
- case 10: // Array
- {
- guint32 nb_elems;
-
- if (!gst_byte_reader_get_uint32_be (reader, &nb_elems))
- goto error;
-
- GST_DEBUG_OBJECT (demux, "array has %d elements", nb_elems);
-
- if (!strcmp (tag_name, "times")) {
- if (demux->times) {
- g_array_free (demux->times, TRUE);
- }
- demux->times = g_array_new (FALSE, TRUE, sizeof (gdouble));
- } else if (!strcmp (tag_name, "filepositions")) {
- if (demux->filepositions) {
- g_array_free (demux->filepositions, TRUE);
- }
- demux->filepositions = g_array_new (FALSE, TRUE, sizeof (gdouble));
- }
-
- while (nb_elems--) {
- guint8 elem_type;
-
- if (!gst_byte_reader_get_uint8 (reader, &elem_type))
- goto error;
-
- switch (elem_type) {
- case 0:
- {
- gdouble d;
-
- if (!gst_byte_reader_get_float64_be (reader, &d))
- goto error;
-
- GST_DEBUG_OBJECT (demux, "element is a double %f", d);
-
- if (!strcmp (tag_name, "times") && demux->times) {
- g_array_append_val (demux->times, d);
- } else if (!strcmp (tag_name, "filepositions") &&
- demux->filepositions) {
- g_array_append_val (demux->filepositions, d);
- }
- break;
- }
- default:
- GST_WARNING_OBJECT (demux, "unsupported array element type %d",
- elem_type);
- }
- }
-
- break;
- }
- case 11: // Date
- {
- gdouble d;
- gint16 i;
-
- if (!gst_byte_reader_get_float64_be (reader, &d))
- goto error;
-
- if (!gst_byte_reader_get_int16_be (reader, &i))
- goto error;
-
- GST_DEBUG_OBJECT (demux,
- "%s => (date as a double) %f, timezone offset %d", tag_name, d, i);
-
- GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name);
-
- break;
- }
- default:
- GST_WARNING_OBJECT (demux, "unsupported tag type %d", tag_type);
- }
-
- g_free (tag_name);
-
- return TRUE;
-
-error:
- g_free (tag_name);
-
- return FALSE;
-}
-
-GstFlowReturn
-gst_flv_parse_tag_script (GstFLVDemux * demux, GstBuffer * buffer)
-{
- GstFlowReturn ret = GST_FLOW_OK;
- GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buffer);
- guint8 type;
-
- g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 7, GST_FLOW_ERROR);
-
- gst_byte_reader_skip (&reader, 7);
-
- GST_LOG_OBJECT (demux, "parsing a script tag");
-
- if (!gst_byte_reader_get_uint8 (&reader, &type))
- return GST_FLOW_OK;
-
- /* Must be string */
- if (type == 2) {
- gchar *function_name;
- guint i;
-
- function_name = FLV_GET_STRING (&reader);
-
- GST_LOG_OBJECT (demux, "function name is %s", GST_STR_NULL (function_name));
-
- if (function_name != NULL && strcmp (function_name, "onMetaData") == 0) {
- guint32 nb_elems = 0;
- gboolean end_marker = FALSE;
-
- GST_DEBUG_OBJECT (demux, "we have a metadata script object");
-
- /* Next type must be a ECMA array */
- if (!gst_byte_reader_get_uint8 (&reader, &type) || type != 8) {
- g_free (function_name);
- return GST_FLOW_OK;
- }
-
- if (!gst_byte_reader_get_uint32_be (&reader, &nb_elems)) {
- g_free (function_name);
- return GST_FLOW_OK;
- }
-
- GST_DEBUG_OBJECT (demux, "there are approx. %d elements in the array",
- nb_elems);
-
- while (nb_elems-- && !end_marker) {
- gboolean ok = gst_flv_parse_metadata_item (demux, &reader, &end_marker);
-
- if (G_UNLIKELY (!ok)) {
- GST_WARNING_OBJECT (demux, "failed reading a tag, skipping");
- break;
- }
- }
-
- demux->push_tags = TRUE;
- }
-
- g_free (function_name);
-
- if (demux->index && demux->times && demux->filepositions
- && !demux->random_access) {
- /* If an index was found and we're in push mode, insert associations */
- for (i = 0; i < MIN (demux->times->len, demux->filepositions->len); i++) {
- guint64 time, fileposition;
-
- time = g_array_index (demux->times, gdouble, i) * GST_SECOND;
- fileposition = g_array_index (demux->filepositions, gdouble, i);
- GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %"
- G_GUINT64_FORMAT, GST_TIME_ARGS (time), fileposition);
- gst_index_add_association (demux->index, demux->index_id,
- GST_ASSOCIATION_FLAG_KEY_UNIT, GST_FORMAT_TIME, time,
- GST_FORMAT_BYTES, fileposition, NULL);
- }
- }
- }
-
- return ret;
-}
-
-static gboolean
-gst_flv_parse_audio_negotiate (GstFLVDemux * demux, guint32 codec_tag,
- guint32 rate, guint32 channels, guint32 width)
-{
- GstCaps *caps = NULL;
- gchar *codec_name = NULL;
- gboolean ret = FALSE;
-
- switch (codec_tag) {
- case 1:
- caps = gst_caps_new_simple ("audio/x-adpcm", "layout", G_TYPE_STRING,
- "swf", NULL);
- codec_name = "Shockwave ADPCM";
- break;
- case 2:
- case 14:
- caps = gst_caps_new_simple ("audio/mpeg",
- "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3,
- "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
- codec_name = "MPEG 1 Audio, Layer 3 (MP3)";
- break;
- case 0:
- case 3:
- /* Assuming little endian for 0 (aka endianness of the
- * system on which the file was created) as most people
- * are probably using little endian machines */
- caps = gst_caps_new_simple ("audio/x-raw-int",
- "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
- "signed", G_TYPE_BOOLEAN, (width == 8) ? FALSE : TRUE,
- "width", G_TYPE_INT, width, "depth", G_TYPE_INT, width, NULL);
- codec_name = "Raw Audio";
- break;
- case 4:
- case 5:
- case 6:
- caps = gst_caps_new_simple ("audio/x-nellymoser", NULL);
- codec_name = "Nellymoser ASAO";
- break;
- case 10:
- caps = gst_caps_new_simple ("audio/mpeg",
- "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE, NULL);
- codec_name = "AAC";
- break;
- case 7:
- caps = gst_caps_new_simple ("audio/x-alaw", NULL);
- codec_name = "A-Law";
- break;
- case 8:
- caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
- codec_name = "Mu-Law";
- break;
- case 11:
- caps = gst_caps_new_simple ("audio/x-speex", NULL);
- codec_name = "Speex";
- break;
- default:
- GST_WARNING_OBJECT (demux, "unsupported audio codec tag %u", codec_tag);
- }
-
- if (G_UNLIKELY (!caps)) {
- GST_WARNING_OBJECT (demux, "failed creating caps for audio pad");
- goto beach;
- }
-
- gst_caps_set_simple (caps,
- "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL);
-
- if (demux->audio_codec_data) {
- gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER,
- demux->audio_codec_data, NULL);
- }
-
- ret = gst_pad_set_caps (demux->audio_pad, caps);
-
- if (G_LIKELY (ret)) {
- /* Store the caps we have set */
- demux->audio_codec_tag = codec_tag;
- demux->rate = rate;
- demux->channels = channels;
- demux->width = width;
-
- if (codec_name) {
- if (demux->taglist == NULL)
- demux->taglist = gst_tag_list_new ();
- gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_AUDIO_CODEC, codec_name, NULL);
- }
-
- GST_DEBUG_OBJECT (demux->audio_pad, "successfully negotiated caps %"
- GST_PTR_FORMAT, caps);
- } else {
- GST_WARNING_OBJECT (demux->audio_pad, "failed negotiating caps %"
- GST_PTR_FORMAT, caps);
- }
-
- gst_caps_unref (caps);
-
-beach:
- return ret;
-}
-
-GstFlowReturn
-gst_flv_parse_tag_audio (GstFLVDemux * demux, GstBuffer * buffer)
-{
- GstFlowReturn ret = GST_FLOW_OK;
- guint32 pts = 0, codec_tag = 0, rate = 5512, width = 8, channels = 1;
- guint32 codec_data = 0, pts_ext = 0;
- guint8 flags = 0;
- guint8 *data = GST_BUFFER_DATA (buffer);
- GstBuffer *outbuf;
-
- GST_LOG_OBJECT (demux, "parsing an audio tag");
-
- g_return_val_if_fail (GST_BUFFER_SIZE (buffer) == demux->tag_size,
- GST_FLOW_ERROR);
-
- GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X", data[0], data[1],
- data[2], data[3]);
-
- /* Grab information about audio tag */
- pts = GST_READ_UINT24_BE (data);
- /* read the pts extension to 32 bits integer */
- pts_ext = GST_READ_UINT8 (data + 3);
- /* Combine them */
- pts |= pts_ext << 24;
-
- if (GST_BUFFER_SIZE (buffer) < 12) {
- GST_ERROR_OBJECT (demux, "Too small tag size");
- return GST_FLOW_ERROR;
- }
-
- /* Skip the stream id and go directly to the flags */
- flags = GST_READ_UINT8 (data + 7);
-
- /* Channels */
- if (flags & 0x01) {
- channels = 2;
- }
- /* Width */
- if (flags & 0x02) {
- width = 16;
- }
- /* Sampling rate */
- if ((flags & 0x0C) == 0x0C) {
- rate = 44100;
- } else if ((flags & 0x0C) == 0x08) {
- rate = 22050;
- } else if ((flags & 0x0C) == 0x04) {
- rate = 11025;
- }
- /* Codec tag */
- codec_tag = flags >> 4;
- if (codec_tag == 10) { /* AAC has an extra byte for packet type */
- codec_data = 2;
- } else {
- codec_data = 1;
- }
-
- /* codec tags with special rates */
- if (codec_tag == 5 || codec_tag == 14)
- rate = 8000;
- else if (codec_tag == 4)
- rate = 16000;
-
- GST_LOG_OBJECT (demux, "audio tag with %d channels, %dHz sampling rate, "
- "%d bits width, codec tag %u (flags %02X)", channels, rate, width,
- codec_tag, flags);
-
- /* If we don't have our audio pad created, then create it. */
- if (G_UNLIKELY (!demux->audio_pad)) {
-
- demux->audio_pad =
- gst_pad_new_from_template (gst_element_class_get_pad_template
- (GST_ELEMENT_GET_CLASS (demux), "audio"), "audio");
- if (G_UNLIKELY (!demux->audio_pad)) {
- GST_WARNING_OBJECT (demux, "failed creating audio pad");
- ret = GST_FLOW_ERROR;
- goto beach;
- }
-
- /* Negotiate caps */
- if (!gst_flv_parse_audio_negotiate (demux, codec_tag, rate, channels,
- width)) {
- gst_object_unref (demux->audio_pad);
- demux->audio_pad = NULL;
- ret = GST_FLOW_ERROR;
- goto beach;
- }
-
- GST_DEBUG_OBJECT (demux, "created audio pad with caps %" GST_PTR_FORMAT,
- GST_PAD_CAPS (demux->audio_pad));
-
- /* Set functions on the pad */
- gst_pad_set_query_type_function (demux->audio_pad,
- GST_DEBUG_FUNCPTR (gst_flv_demux_query_types));
- gst_pad_set_query_function (demux->audio_pad,
- GST_DEBUG_FUNCPTR (gst_flv_demux_query));
- gst_pad_set_event_function (demux->audio_pad,
- GST_DEBUG_FUNCPTR (gst_flv_demux_src_event));
-
- gst_pad_use_fixed_caps (demux->audio_pad);
-
- /* Make it active */
- gst_pad_set_active (demux->audio_pad, TRUE);
-
- /* We need to set caps before adding */
- gst_element_add_pad (GST_ELEMENT (demux),
- gst_object_ref (demux->audio_pad));
-
- /* We only emit no more pads when we have audio and video. Indeed we can
- * not trust the FLV header to tell us if there will be only audio or
- * only video and we would just break discovery of some files */
- if (demux->audio_pad && demux->video_pad) {
- GST_DEBUG_OBJECT (demux, "emitting no more pads");
- gst_element_no_more_pads (GST_ELEMENT (demux));
- }
- }
-
- /* Check if caps have changed */
- if (G_UNLIKELY (rate != demux->rate || channels != demux->channels ||
- codec_tag != demux->audio_codec_tag || width != demux->width)) {
- GST_DEBUG_OBJECT (demux, "audio settings have changed, changing caps");
-
- /* Negotiate caps */
- if (!gst_flv_parse_audio_negotiate (demux, codec_tag, rate, channels,
- width)) {
- ret = GST_FLOW_ERROR;
- goto beach;
- }
- }
-
- /* Push taglist if present */
- if ((demux->has_audio && !demux->audio_pad) &&
- (demux->has_video && !demux->video_pad)) {
- GST_DEBUG_OBJECT (demux, "we are still waiting for a stream to come up "
- "before we can push tags");
- } else {
- if (demux->taglist && demux->push_tags) {
- GST_DEBUG_OBJECT (demux, "pushing tags out");
- gst_element_found_tags (GST_ELEMENT (demux), demux->taglist);
- demux->taglist = gst_tag_list_new ();
- demux->push_tags = FALSE;
- }
- }
-
- /* Check if we have anything to push */
- if (demux->tag_data_size <= codec_data) {
- GST_LOG_OBJECT (demux, "Nothing left in this tag, returning");
- goto beach;
- }
-
- /* Create buffer from pad */
- outbuf =
- gst_buffer_create_sub (buffer, 7 + codec_data,
- demux->tag_data_size - codec_data);
-
- if (demux->audio_codec_tag == 10) {
- guint8 aac_packet_type = GST_READ_UINT8 (data + 8);
-
- switch (aac_packet_type) {
- case 0:
- {
- /* AudioSpecificConfic data */
- GST_LOG_OBJECT (demux, "got an AAC codec data packet");
- if (demux->audio_codec_data) {
- gst_buffer_unref (demux->audio_codec_data);
- }
- demux->audio_codec_data = outbuf;
- /* Use that buffer data in the caps */
- gst_flv_parse_audio_negotiate (demux, codec_tag, rate, channels, width);
- goto beach;
- break;
- }
- case 1:
- /* AAC raw packet */
- GST_LOG_OBJECT (demux, "got a raw AAC audio packet");
- break;
- default:
- GST_WARNING_OBJECT (demux, "invalid AAC packet type %u",
- aac_packet_type);
- }
- }
-
- /* Fill buffer with data */
- GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND;
- GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
- GST_BUFFER_OFFSET (outbuf) = demux->audio_offset++;
- GST_BUFFER_OFFSET_END (outbuf) = demux->audio_offset;
- gst_buffer_set_caps (outbuf, GST_PAD_CAPS (demux->audio_pad));
-
- if (demux->duration == GST_CLOCK_TIME_NONE ||
- demux->duration < GST_BUFFER_TIMESTAMP (outbuf))
- demux->duration = GST_BUFFER_TIMESTAMP (outbuf);
-
- /* Only add audio frames to the index if we have no video
- * and if we don't have random access */
- if (!demux->has_video && demux->index && !demux->random_access) {
- GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %"
- G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
- demux->cur_tag_offset);
- gst_index_add_association (demux->index, demux->index_id,
- GST_ASSOCIATION_FLAG_KEY_UNIT,
- GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (outbuf),
- GST_FORMAT_BYTES, demux->cur_tag_offset, NULL);
- }
-
- if (G_UNLIKELY (demux->audio_need_discont)) {
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
- demux->audio_need_discont = FALSE;
- }
-
- gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME,
- GST_BUFFER_TIMESTAMP (outbuf));
-
- /* Do we need a newsegment event ? */
- if (G_UNLIKELY (demux->audio_need_segment)) {
- if (demux->close_seg_event)
- gst_pad_push_event (demux->audio_pad,
- gst_event_ref (demux->close_seg_event));
-
- if (!demux->new_seg_event) {
- GST_DEBUG_OBJECT (demux, "pushing newsegment from %"
- GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
- GST_TIME_ARGS (demux->segment.last_stop),
- GST_TIME_ARGS (demux->segment.stop));
- demux->new_seg_event =
- gst_event_new_new_segment (FALSE, demux->segment.rate,
- demux->segment.format, demux->segment.last_stop,
- demux->segment.stop, demux->segment.last_stop);
- } else {
- GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event");
- }
-
- gst_pad_push_event (demux->audio_pad, gst_event_ref (demux->new_seg_event));
-
- demux->audio_need_segment = FALSE;
- }
-
- GST_LOG_OBJECT (demux, "pushing %d bytes buffer at pts %" GST_TIME_FORMAT
- " with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT,
- GST_BUFFER_SIZE (outbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf));
-
- /* Push downstream */
- ret = gst_pad_push (demux->audio_pad, outbuf);
- if (G_UNLIKELY (ret != GST_FLOW_OK)) {
- GST_WARNING_OBJECT (demux, "failed pushing a %" G_GUINT64_FORMAT
- " bytes audio buffer: %s", demux->tag_data_size,
- gst_flow_get_name (ret));
- if (ret == GST_FLOW_NOT_LINKED) {
- demux->audio_linked = FALSE;
- }
- goto beach;
- }
-
- demux->audio_linked = TRUE;
-
-beach:
- return ret;
-}
-
-static gboolean
-gst_flv_parse_video_negotiate (GstFLVDemux * demux, guint32 codec_tag)
-{
- gboolean ret = FALSE;
- GstCaps *caps = NULL;
- gchar *codec_name = NULL;
-
- /* Generate caps for that pad */
- switch (codec_tag) {
- case 2:
- caps = gst_caps_new_simple ("video/x-flash-video", NULL);
- codec_name = "Sorenson Video";
- break;
- case 3:
- caps = gst_caps_new_simple ("video/x-flash-screen", NULL);
- codec_name = "Flash Screen Video";
- case 4:
- caps = gst_caps_new_simple ("video/x-vp6-flash", NULL);
- codec_name = "On2 VP6 Video";
- break;
- case 5:
- caps = gst_caps_new_simple ("video/x-vp6-alpha", NULL);
- codec_name = "On2 VP6 Video with alpha channel";
- break;
- case 7:
- caps = gst_caps_new_simple ("video/x-h264", NULL);
- codec_name = "H.264/AVC Video";
- break;
- default:
- GST_WARNING_OBJECT (demux, "unsupported video codec tag %u", codec_tag);
- }
-
- if (G_UNLIKELY (!caps)) {
- GST_WARNING_OBJECT (demux, "failed creating caps for video pad");
- goto beach;
- }
-
- gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
- demux->par_x, demux->par_y, NULL);
-
- if (demux->video_codec_data) {
- gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER,
- demux->video_codec_data, NULL);
- }
-
- ret = gst_pad_set_caps (demux->video_pad, caps);
-
- if (G_LIKELY (ret)) {
- /* Store the caps we have set */
- demux->video_codec_tag = codec_tag;
-
- if (codec_name) {
- if (demux->taglist == NULL)
- demux->taglist = gst_tag_list_new ();
- gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_VIDEO_CODEC, codec_name, NULL);
- }
-
- GST_DEBUG_OBJECT (demux->video_pad, "successfully negotiated caps %"
- GST_PTR_FORMAT, caps);
- } else {
- GST_WARNING_OBJECT (demux->video_pad, "failed negotiating caps %"
- GST_PTR_FORMAT, caps);
- }
-
- gst_caps_unref (caps);
-
-beach:
- return ret;
-}
-
-GstFlowReturn
-gst_flv_parse_tag_video (GstFLVDemux * demux, GstBuffer * buffer)
-{
- GstFlowReturn ret = GST_FLOW_OK;
- guint32 pts = 0, codec_data = 1, pts_ext = 0;
- gboolean keyframe = FALSE;
- guint8 flags = 0, codec_tag = 0;
- guint8 *data = GST_BUFFER_DATA (buffer);
- GstBuffer *outbuf;
-
- g_return_val_if_fail (GST_BUFFER_SIZE (buffer) == demux->tag_size,
- GST_FLOW_ERROR);
-
- GST_LOG_OBJECT (demux, "parsing a video tag");
-
- GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X", data[0], data[1],
- data[2], data[3]);
-
- /* Grab information about video tag */
- pts = GST_READ_UINT24_BE (data);
- /* read the pts extension to 32 bits integer */
- pts_ext = GST_READ_UINT8 (data + 3);
- /* Combine them */
- pts |= pts_ext << 24;
-
- if (GST_BUFFER_SIZE (buffer) < 12) {
- GST_ERROR_OBJECT (demux, "Too small tag size");
- return GST_FLOW_ERROR;
- }
-
- /* Skip the stream id and go directly to the flags */
- flags = GST_READ_UINT8 (data + 7);
-
- /* Keyframe */
- if ((flags >> 4) == 1) {
- keyframe = TRUE;
- }
- /* Codec tag */
- codec_tag = flags & 0x0F;
- if (codec_tag == 4 || codec_tag == 5) {
- codec_data = 2;
- } else if (codec_tag == 7) {
- codec_data = 5;
- }
-
- GST_LOG_OBJECT (demux, "video tag with codec tag %u, keyframe (%d) "
- "(flags %02X)", codec_tag, keyframe, flags);
-
- /* If we don't have our video pad created, then create it. */
- if (G_UNLIKELY (!demux->video_pad)) {
- demux->video_pad =
- gst_pad_new_from_template (gst_element_class_get_pad_template
- (GST_ELEMENT_GET_CLASS (demux), "video"), "video");
- if (G_UNLIKELY (!demux->video_pad)) {
- GST_WARNING_OBJECT (demux, "failed creating video pad");
- ret = GST_FLOW_ERROR;
- goto beach;
- }
-
- if (!gst_flv_parse_video_negotiate (demux, codec_tag)) {
- gst_object_unref (demux->video_pad);
- demux->video_pad = NULL;
- ret = GST_FLOW_ERROR;
- goto beach;
- }
-
- /* When we ve set pixel-aspect-ratio we use that boolean to detect a
- * metadata tag that would come later and trigger a caps change */
- demux->got_par = FALSE;
-
- GST_DEBUG_OBJECT (demux, "created video pad with caps %" GST_PTR_FORMAT,
- GST_PAD_CAPS (demux->video_pad));
-
- /* Set functions on the pad */
- gst_pad_set_query_type_function (demux->video_pad,
- GST_DEBUG_FUNCPTR (gst_flv_demux_query_types));
- gst_pad_set_query_function (demux->video_pad,
- GST_DEBUG_FUNCPTR (gst_flv_demux_query));
- gst_pad_set_event_function (demux->video_pad,
- GST_DEBUG_FUNCPTR (gst_flv_demux_src_event));
-
- gst_pad_use_fixed_caps (demux->video_pad);
-
- /* Make it active */
- gst_pad_set_active (demux->video_pad, TRUE);
-
- /* We need to set caps before adding */
- gst_element_add_pad (GST_ELEMENT (demux),
- gst_object_ref (demux->video_pad));
-
- /* We only emit no more pads when we have audio and video. Indeed we can
- * not trust the FLV header to tell us if there will be only audio or
- * only video and we would just break discovery of some files */
- if (demux->audio_pad && demux->video_pad) {
- GST_DEBUG_OBJECT (demux, "emitting no more pads");
- gst_element_no_more_pads (GST_ELEMENT (demux));
- }
- }
-
- /* Check if caps have changed */
- if (G_UNLIKELY (codec_tag != demux->video_codec_tag || demux->got_par)) {
-
- GST_DEBUG_OBJECT (demux, "video settings have changed, changing caps");
-
- if (!gst_flv_parse_video_negotiate (demux, codec_tag)) {
- ret = GST_FLOW_ERROR;
- goto beach;
- }
-
- /* When we ve set pixel-aspect-ratio we use that boolean to detect a
- * metadata tag that would come later and trigger a caps change */
- demux->got_par = FALSE;
- }
-
- /* Push taglist if present */
- if ((demux->has_audio && !demux->audio_pad) &&
- (demux->has_video && !demux->video_pad)) {
- GST_DEBUG_OBJECT (demux, "we are still waiting for a stream to come up "
- "before we can push tags");
- } else {
- if (demux->taglist && demux->push_tags) {
- GST_DEBUG_OBJECT (demux, "pushing tags out");
- gst_element_found_tags (GST_ELEMENT (demux), demux->taglist);
- demux->taglist = gst_tag_list_new ();
- demux->push_tags = FALSE;
- }
- }
-
- /* Check if we have anything to push */
- if (demux->tag_data_size <= codec_data) {
- GST_LOG_OBJECT (demux, "Nothing left in this tag, returning");
- goto beach;
- }
-
- /* Create buffer from pad */
- outbuf =
- gst_buffer_create_sub (buffer, 7 + codec_data,
- demux->tag_data_size - codec_data);
-
- if (demux->video_codec_tag == 7) {
- guint8 avc_packet_type = GST_READ_UINT8 (data + 8);
-
- switch (avc_packet_type) {
- case 0:
- {
- /* AVCDecoderConfigurationRecord data */
- GST_LOG_OBJECT (demux, "got an H.264 codec data packet");
- if (demux->video_codec_data) {
- gst_buffer_unref (demux->video_codec_data);
- }
- demux->video_codec_data = outbuf;
- /* Use that buffer data in the caps */
- gst_flv_parse_video_negotiate (demux, codec_tag);
- goto beach;
- break;
- }
- case 1:
- /* H.264 NALU packet */
- GST_LOG_OBJECT (demux, "got a H.264 NALU audio packet");
- break;
- default:
- GST_WARNING_OBJECT (demux, "invalid AAC packet type %u",
- avc_packet_type);
- }
- }
-
- /* Fill buffer with data */
- GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND;
- GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
- GST_BUFFER_OFFSET (outbuf) = demux->video_offset++;
- GST_BUFFER_OFFSET_END (outbuf) = demux->video_offset;
- gst_buffer_set_caps (outbuf, GST_PAD_CAPS (demux->video_pad));
-
- if (demux->duration == GST_CLOCK_TIME_NONE ||
- demux->duration < GST_BUFFER_TIMESTAMP (outbuf))
- demux->duration = GST_BUFFER_TIMESTAMP (outbuf);
-
- if (!keyframe) {
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
- if (demux->index && !demux->random_access) {
- GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %"
- G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
- demux->cur_tag_offset);
- gst_index_add_association (demux->index, demux->index_id,
- GST_ASSOCIATION_FLAG_NONE,
- GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (outbuf),
- GST_FORMAT_BYTES, demux->cur_tag_offset, NULL);
- }
- } else {
- if (demux->index && !demux->random_access) {
- GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %"
- G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
- demux->cur_tag_offset);
- gst_index_add_association (demux->index, demux->index_id,
- GST_ASSOCIATION_FLAG_KEY_UNIT,
- GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (outbuf),
- GST_FORMAT_BYTES, demux->cur_tag_offset, NULL);
- }
- }
-
- if (G_UNLIKELY (demux->video_need_discont)) {
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
- demux->video_need_discont = FALSE;
- }
-
- gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME,
- GST_BUFFER_TIMESTAMP (outbuf));
-
- /* Do we need a newsegment event ? */
- if (G_UNLIKELY (demux->video_need_segment)) {
- if (demux->close_seg_event)
- gst_pad_push_event (demux->video_pad,
- gst_event_ref (demux->close_seg_event));
-
- if (!demux->new_seg_event) {
- GST_DEBUG_OBJECT (demux, "pushing newsegment from %"
- GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
- GST_TIME_ARGS (demux->segment.last_stop),
- GST_TIME_ARGS (demux->segment.stop));
- demux->new_seg_event =
- gst_event_new_new_segment (FALSE, demux->segment.rate,
- demux->segment.format, demux->segment.last_stop,
- demux->segment.stop, demux->segment.last_stop);
- } else {
- GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event");
- }
-
- gst_pad_push_event (demux->video_pad, gst_event_ref (demux->new_seg_event));
-
- demux->video_need_segment = FALSE;
- }
-
- GST_LOG_OBJECT (demux, "pushing %d bytes buffer at pts %" GST_TIME_FORMAT
- " with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
- ", keyframe (%d)", GST_BUFFER_SIZE (outbuf),
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
- keyframe);
-
- /* Push downstream */
- ret = gst_pad_push (demux->video_pad, outbuf);
-
- if (G_UNLIKELY (ret != GST_FLOW_OK)) {
- GST_WARNING_OBJECT (demux, "failed pushing a %" G_GUINT64_FORMAT
- " bytes video buffer: %s", demux->tag_data_size,
- gst_flow_get_name (ret));
- if (ret == GST_FLOW_NOT_LINKED) {
- demux->video_linked = FALSE;
- }
- goto beach;
- }
-
- demux->video_linked = TRUE;
-
-beach:
- return ret;
-}
-
-GstClockTime
-gst_flv_parse_tag_timestamp (GstFLVDemux * demux, GstBuffer * buffer,
- size_t * tag_size)
-{
- guint32 pts = 0, pts_ext = 0;
- guint32 tag_data_size;
- guint8 type;
- gboolean keyframe = TRUE;
- GstClockTime ret;
- guint8 *data = GST_BUFFER_DATA (buffer);
-
- g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 12, GST_CLOCK_TIME_NONE);
-
- type = data[0];
-
- if (type != 9 && type != 8 && type != 18) {
- GST_WARNING_OBJECT (demux, "Unsupported tag type %u", data[0]);
- return GST_CLOCK_TIME_NONE;
- }
-
- if (type == 9)
- demux->has_video = TRUE;
- else if (type == 8)
- demux->has_audio = TRUE;
-
- tag_data_size = GST_READ_UINT24_BE (data + 1);
-
- if (GST_BUFFER_SIZE (buffer) >= tag_data_size + 11 + 4) {
- if (GST_READ_UINT32_BE (data + tag_data_size + 11) != tag_data_size + 11) {
- GST_WARNING_OBJECT (demux, "Invalid tag size");
- return GST_CLOCK_TIME_NONE;
- }
- }
-
- if (tag_size)
- *tag_size = tag_data_size + 11 + 4;
-
- data += 4;
-
- GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X", data[0], data[1],
- data[2], data[3]);
-
- /* Grab timestamp of tag tag */
- pts = GST_READ_UINT24_BE (data);
- /* read the pts extension to 32 bits integer */
- pts_ext = GST_READ_UINT8 (data + 3);
- /* Combine them */
- pts |= pts_ext << 24;
-
- if (type == 9) {
- data += 7;
-
- keyframe = ((data[0] >> 4) == 1);
- }
-
- ret = pts * GST_MSECOND;
-
- if (demux->index && (type == 9 || (type == 8 && !demux->has_video))) {
- GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %"
- G_GUINT64_FORMAT, GST_TIME_ARGS (ret), demux->offset);
- gst_index_add_association (demux->index, demux->index_id,
- (keyframe) ? GST_ASSOCIATION_FLAG_KEY_UNIT : GST_ASSOCIATION_FLAG_NONE,
- GST_FORMAT_TIME, ret, GST_FORMAT_BYTES, demux->offset, NULL);
- }
-
- if (demux->duration == GST_CLOCK_TIME_NONE || demux->duration < ret)
- demux->duration = ret;
-
- return ret;
-}
-
-GstFlowReturn
-gst_flv_parse_tag_type (GstFLVDemux * demux, GstBuffer * buffer)
-{
- GstFlowReturn ret = GST_FLOW_OK;
- guint8 tag_type = 0;
- guint8 *data = GST_BUFFER_DATA (buffer);
-
- g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 4, GST_FLOW_ERROR);
-
- tag_type = data[0];
-
- switch (tag_type) {
- case 9:
- demux->state = FLV_STATE_TAG_VIDEO;
- demux->has_video = TRUE;
- break;
- case 8:
- demux->state = FLV_STATE_TAG_AUDIO;
- demux->has_audio = TRUE;
- break;
- case 18:
- demux->state = FLV_STATE_TAG_SCRIPT;
- break;
- default:
- GST_WARNING_OBJECT (demux, "unsupported tag type %u", tag_type);
- }
-
- /* Tag size is 1 byte of type + 3 bytes of size + 7 bytes + tag data size +
- * 4 bytes of previous tag size */
- demux->tag_data_size = GST_READ_UINT24_BE (data + 1);
- demux->tag_size = demux->tag_data_size + 11;
-
- GST_LOG_OBJECT (demux, "tag data size is %" G_GUINT64_FORMAT,
- demux->tag_data_size);
-
- return ret;
-}
-
-GstFlowReturn
-gst_flv_parse_header (GstFLVDemux * demux, GstBuffer * buffer)
-{
- GstFlowReturn ret = GST_FLOW_OK;
- guint8 *data = GST_BUFFER_DATA (buffer);
-
- g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 9, GST_FLOW_ERROR);
-
- /* Check for the FLV tag */
- if (data[0] == 'F' && data[1] == 'L' && data[2] == 'V') {
- GST_DEBUG_OBJECT (demux, "FLV header detected");
- } else {
- if (G_UNLIKELY (demux->strict)) {
- GST_WARNING_OBJECT (demux, "invalid header tag detected");
- ret = GST_FLOW_UNEXPECTED;
- goto beach;
- }
- }
-
- /* Jump over the 4 first bytes */
- data += 4;
-
- /* Now look at audio/video flags */
- {
- guint8 flags = data[0];
-
- demux->has_video = demux->has_audio = FALSE;
-
- if (flags & 1) {
- GST_DEBUG_OBJECT (demux, "there is a video stream");
- demux->has_video = TRUE;
- }
- if (flags & 4) {
- GST_DEBUG_OBJECT (demux, "there is an audio stream");
- demux->has_audio = TRUE;
- }
- }
-
- /* We don't care about the rest */
- demux->need_header = FALSE;
-
-beach:
- return ret;
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Julien Moutte <julien@moutte.net>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __FLV_PARSE_H__
-#define __FLV_PARSE_H__
-
-#include "gstflvdemux.h"
-
-G_BEGIN_DECLS
-
-
-GstFlowReturn gst_flv_parse_tag_script (GstFLVDemux * demux,
- GstBuffer *buffer);
-
-GstFlowReturn gst_flv_parse_tag_audio (GstFLVDemux * demux, GstBuffer *buffer);
-
-GstFlowReturn gst_flv_parse_tag_video (GstFLVDemux * demux, GstBuffer *buffer);
-
-GstFlowReturn gst_flv_parse_tag_type (GstFLVDemux * demux, GstBuffer *buffer);
-
-GstFlowReturn gst_flv_parse_header (GstFLVDemux * demux, GstBuffer *buffer);
-
-GstClockTime gst_flv_parse_tag_timestamp (GstFLVDemux *demux, GstBuffer *buffer, size_t *tag_data_size);
-
-G_END_DECLS
-#endif /* __FLV_PARSE_H__ */