summaryrefslogtreecommitdiffstats
path: root/gst/flv/gstflvparse.c
diff options
context:
space:
mode:
authorJulien Moutte <julien@moutte.net>2007-07-19 15:05:30 +0000
committerJulien Moutte <julien@moutte.net>2007-07-19 15:05:30 +0000
commitfaea34bf985e95c4356e96a88737273baa6ad94b (patch)
tree694f0e6f98c0868550bf0c9aa08c48ef59d46326 /gst/flv/gstflvparse.c
parent6cbacb8a1497c21208edd0507986c2e263d17bfc (diff)
downloadgst-plugins-bad-faea34bf985e95c4356e96a88737273baa6ad94b.tar.gz
gst-plugins-bad-faea34bf985e95c4356e96a88737273baa6ad94b.tar.bz2
gst-plugins-bad-faea34bf985e95c4356e96a88737273baa6ad94b.zip
Adds a first draft of an FLV demuxer.
Original commit message from CVS: 2007-07-19 Julien MOUTTE <julien@moutte.net> * configure.ac: * gst/flv/Makefile.am: * gst/flv/gstflvdemux.c: (gst_flv_demux_flush), (gst_flv_demux_cleanup), (gst_flv_demux_chain), (gst_flv_demux_pull_tag), (gst_flv_demux_pull_header), (gst_flv_demux_seek_to_prev_keyframe), (gst_flv_demux_loop), (gst_flv_demux_sink_activate), (gst_flv_demux_sink_activate_push), (gst_flv_demux_sink_activate_pull), (gst_flv_demux_sink_event), (gst_flv_demux_change_state), (gst_flv_demux_dispose), (gst_flv_demux_base_init), (gst_flv_demux_class_init), (gst_flv_demux_init), (plugin_init): * gst/flv/gstflvdemux.h: * gst/flv/gstflvparse.c: (FLV_GET_BEUI24), (FLV_GET_STRING), (gst_flv_demux_query_types), (gst_flv_demux_query), (gst_flv_parse_metadata_item), (gst_flv_parse_tag_script), (gst_flv_parse_tag_audio), (gst_flv_parse_tag_video), (gst_flv_parse_tag_type), (gst_flv_parse_header): * gst/flv/gstflvparse.h: Adds a first draft of an FLV demuxer. It does not do seeking yet, it supports pull and push mode so YES you can use it to play youtube videos directly from an HTTP uri. Not so much testing done yet but it parses metadata, reply to duration queries, etc...
Diffstat (limited to 'gst/flv/gstflvparse.c')
-rw-r--r--gst/flv/gstflvparse.c797
1 files changed, 797 insertions, 0 deletions
diff --git a/gst/flv/gstflvparse.c b/gst/flv/gstflvparse.c
new file mode 100644
index 00000000..023df66c
--- /dev/null
+++ b/gst/flv/gstflvparse.c
@@ -0,0 +1,797 @@
+/* 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 <string.h>
+
+GST_DEBUG_CATEGORY_EXTERN (flvdemux_debug);
+#define GST_CAT_DEFAULT flvdemux_debug
+
+static guint32
+FLV_GET_BEUI24 (const guint8 * data, size_t data_size)
+{
+ guint32 ret = 0;
+
+ g_return_val_if_fail (data != NULL, 0);
+ g_return_val_if_fail (data_size >= 3, 0);
+
+ ret = GST_READ_UINT16_BE (data) << 8;
+ ret |= GST_READ_UINT8 (data + 2);
+
+ return ret;
+}
+
+static gchar *
+FLV_GET_STRING (const guint8 * data, size_t data_size)
+{
+ guint32 string_size = 0;
+ gchar *string = NULL;
+
+ g_return_val_if_fail (data != NULL, 0);
+ g_return_val_if_fail (data_size >= 3, 0);
+
+ string_size = GST_READ_UINT16_BE (data);
+
+ string = g_try_malloc0 (string_size + 1);
+ if (G_UNLIKELY (!string)) {
+ return NULL;
+ }
+
+ memcpy (string, data + 2, string_size);
+
+ 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 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_LATENCY:
+ {
+ 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;
+ }
+ default:
+ res = FALSE;
+ break;
+ }
+
+beach:
+ gst_object_unref (demux);
+
+ return res;
+}
+
+static size_t
+gst_flv_parse_metadata_item (GstFLVDemux * demux, const guint8 * data,
+ size_t data_size)
+{
+ gchar *tag_name = NULL;
+ guint8 tag_type = 0;
+ size_t offset = 0;
+
+ /* Name of the tag */
+ tag_name = FLV_GET_STRING (data, data_size);
+
+ offset += strlen (tag_name) + 2;
+
+ /* What kind of object is that */
+ tag_type = GST_READ_UINT8 (data + offset);
+
+ offset++;
+
+ 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 */
+ union
+ {
+ guint64 value_uint64;
+ gdouble value_double;
+ } value_union;
+
+ value_union.value_uint64 = GST_READ_UINT64_BE (data + offset);
+
+ offset += 8;
+
+ GST_DEBUG_OBJECT (demux, "%s => (double) %f", tag_name,
+ value_union.value_double);
+
+ if (!strcmp (tag_name, "duration")) {
+ demux->duration = value_union.value_double * GST_SECOND;
+
+ gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
+ GST_TAG_DURATION, demux->duration, NULL);
+ } else {
+ if (tag_name) {
+ if (!gst_tag_exists (tag_name)) {
+ gst_tag_register (tag_name, GST_TAG_FLAG_META, G_TYPE_DOUBLE,
+ tag_name, tag_name, gst_tag_merge_use_first);
+ }
+
+ if (gst_tag_get_type (tag_name) == G_TYPE_DOUBLE) {
+ gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
+ tag_name, value_union.value_double, NULL);
+ } else {
+ GST_WARNING_OBJECT (demux, "tag %s already registered with a "
+ "different type", tag_name);
+ }
+ }
+ }
+
+ break;
+ }
+ case 1: // Boolean
+ {
+ gboolean value = GST_READ_UINT8 (data + offset);
+
+ offset++;
+
+ GST_DEBUG_OBJECT (demux, "%s => (boolean) %d", tag_name, value);
+
+ if (tag_name) {
+ if (!gst_tag_exists (tag_name)) {
+ gst_tag_register (tag_name, GST_TAG_FLAG_META, G_TYPE_BOOLEAN,
+ tag_name, tag_name, gst_tag_merge_use_first);
+ }
+
+ if (gst_tag_get_type (tag_name) == G_TYPE_BOOLEAN) {
+ gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
+ tag_name, value, NULL);
+ } else {
+ GST_WARNING_OBJECT (demux, "tag %s already registered with a "
+ "different type", tag_name);
+ }
+ }
+
+ break;
+ }
+ case 2: // String
+ {
+ gchar *value = NULL;
+
+ value = FLV_GET_STRING (data + offset, data_size - offset);
+
+ offset += strlen (value) + 2;
+
+ GST_DEBUG_OBJECT (demux, "%s => (string) %s", tag_name, value);
+
+ if (tag_name) {
+ if (!gst_tag_exists (tag_name)) {
+ gst_tag_register (tag_name, GST_TAG_FLAG_META, G_TYPE_STRING,
+ tag_name, tag_name, gst_tag_merge_strings_with_comma);
+ }
+
+ if (gst_tag_get_type (tag_name) == G_TYPE_STRING) {
+ gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
+ tag_name, value, NULL);
+ } else {
+ GST_WARNING_OBJECT (demux, "tag %s already registered with a "
+ "different type", tag_name);
+ }
+ }
+
+ g_free (value);
+
+ break;
+ }
+ default:
+ GST_WARNING_OBJECT (demux, "unsupported tag type %d", tag_type);
+ }
+
+ g_free (tag_name);
+
+ return offset;
+}
+
+GstFlowReturn
+gst_flv_parse_tag_script (GstFLVDemux * demux, const guint8 * data,
+ size_t data_size)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ size_t offset = 7;
+
+ GST_LOG_OBJECT (demux, "parsing a script tag");
+
+ if (GST_READ_UINT8 (data + offset++) == 2) {
+ gchar *function_name = FLV_GET_STRING (data + offset, data_size - offset);
+
+ GST_LOG_OBJECT (demux, "function name is %s", function_name);
+
+ if (!strcmp (function_name, "onMetaData")) {
+ guint32 nb_elems = 0;
+
+ GST_DEBUG_OBJECT (demux, "we have a metadata script object");
+
+ /* Jump over the onMetaData string and the array indicator */
+ offset += 13;
+
+ nb_elems = GST_READ_UINT32_BE (data + offset);
+
+ /* Jump over the number of elements */
+ offset += 4;
+
+ GST_DEBUG_OBJECT (demux, "there are %d elements in the array", nb_elems);
+
+ while (nb_elems--) {
+ offset += gst_flv_parse_metadata_item (demux, data + offset,
+ data_size - offset);
+ }
+
+ demux->push_tags = TRUE;
+ }
+
+ g_free (function_name);
+ }
+
+
+ return ret;
+}
+
+GstFlowReturn
+gst_flv_parse_tag_audio (GstFLVDemux * demux, const guint8 * data,
+ size_t data_size)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ GstBuffer *buffer = NULL;
+ guint32 pts = 0, codec_tag = 0, rate = 0, width = 0, channels = 0;
+ guint32 codec_data = 0;
+ guint8 flags = 0;
+
+ GST_LOG_OBJECT (demux, "parsing an audio tag");
+
+ /* Grab information about audio tag */
+ pts = FLV_GET_BEUI24 (data, data_size);
+ flags = GST_READ_UINT8 (data + 7);
+
+ /* Channels */
+ if (flags & 0x01) {
+ channels = 2;
+ } else {
+ channels = 1;
+ }
+ /* Width */
+ if (flags & 0x02) {
+ width = 16;
+ } else {
+ width = 8;
+ }
+ /* Sampling rate */
+ if (flags & 0x0C) {
+ rate = 44100;
+ } else if (flags & 0x08) {
+ rate = 22050;
+ } else if (flags & 0x04) {
+ rate = 11025;
+ } else {
+ rate = 5512;
+ }
+ /* Codec tag */
+ if (flags & 0x10) {
+ codec_tag = 1;
+ codec_data = 1;
+ } else if (flags & 0x20) {
+ codec_tag = 2;
+ codec_data = 1;
+ } else if (flags & 0x30) {
+ codec_tag = 3;
+ codec_data = 1;
+ } else if (flags & 0x40) {
+ codec_tag = 4;
+ codec_data = 1;
+ } else if (flags & 0x50) {
+ codec_tag = 5;
+ codec_data = 1;
+ }
+
+ GST_LOG_OBJECT (demux, "audio tag with %d channels, %dHz sampling rate, "
+ "%d bits width, codec tag %d", channels, rate, width, codec_tag);
+
+ /* If we don't have our audio pad created, then create it. */
+ if (G_UNLIKELY (!demux->audio_pad)) {
+ GstCaps *caps = NULL;
+
+ demux->audio_pad = gst_pad_new ("audio", GST_PAD_SRC);
+ if (G_UNLIKELY (!demux->audio_pad)) {
+ GST_WARNING_OBJECT (demux, "failed creating audio pad");
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+
+ /* Make it active */
+ gst_pad_set_active (demux->audio_pad, TRUE);
+
+ switch (codec_tag) {
+ case 2:
+ caps = gst_caps_new_simple ("audio/mpeg",
+ "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, NULL);
+ break;
+ case 0:
+ case 3:
+ caps = gst_caps_new_simple ("audio/x-raw-int", NULL);
+ break;
+ default:
+ GST_WARNING_OBJECT (demux, "unsupported audio codec tag", codec_tag);
+ }
+
+ if (G_UNLIKELY (!caps)) {
+ GST_WARNING_OBJECT (demux, "failed creating caps for audio pad");
+ ret = GST_FLOW_ERROR;
+ gst_object_unref (demux->audio_pad);
+ demux->audio_pad = NULL;
+ goto beach;
+ }
+
+ gst_caps_set_simple (caps,
+ "rate", G_TYPE_INT, rate,
+ "channels", G_TYPE_INT, channels, "width", G_TYPE_INT, width, NULL);
+
+ gst_pad_set_caps (demux->audio_pad, caps);
+
+ gst_caps_unref (caps);
+
+ /* Store the caps we have set */
+ demux->audio_codec_tag = codec_tag;
+ demux->rate = rate;
+ demux->channels = channels;
+ demux->width = width;
+
+ /* 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));
+
+ /* We need to set caps before adding */
+ gst_element_add_pad (GST_ELEMENT (demux), demux->audio_pad);
+ }
+
+ /* Check if caps have changed */
+ if (G_UNLIKELY (rate != demux->rate || channels != demux->channels ||
+ codec_tag != demux->audio_codec_tag || width != demux->width)) {
+ GstCaps *caps = NULL;
+
+ GST_DEBUG_OBJECT (demux, "audio settings have changed, changing caps");
+
+ switch (codec_tag) {
+ case 2:
+ caps = gst_caps_new_simple ("audio/mpeg",
+ "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, NULL);
+ break;
+ case 0:
+ case 3:
+ caps = gst_caps_new_simple ("audio/x-raw-int", NULL);
+ break;
+ default:
+ GST_WARNING_OBJECT (demux, "unsupported audio codec tag", codec_tag);
+ }
+
+ if (G_UNLIKELY (!caps)) {
+ GST_WARNING_OBJECT (demux, "failed creating caps for audio pad");
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+
+ gst_caps_set_simple (caps,
+ "rate", G_TYPE_INT, rate,
+ "channels", G_TYPE_INT, channels, "width", G_TYPE_INT, width, NULL);
+
+ gst_pad_set_caps (demux->audio_pad, caps);
+
+ gst_caps_unref (caps);
+
+ /* Store the caps we have set */
+ demux->audio_codec_tag = codec_tag;
+ demux->rate = rate;
+ demux->channels = channels;
+ demux->width = width;
+ }
+
+ /* Push taglist if present */
+ if ((demux->has_audio & (demux->audio_pad != NULL)) &&
+ (demux->has_video & (demux->video_pad != NULL)) &&
+ 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;
+ }
+
+ /* Create buffer from pad */
+ ret = gst_pad_alloc_buffer (demux->audio_pad, GST_BUFFER_OFFSET_NONE,
+ demux->tag_data_size - codec_data, GST_PAD_CAPS (demux->audio_pad),
+ &buffer);
+ if (G_UNLIKELY (ret != GST_FLOW_OK)) {
+ GST_WARNING_OBJECT (demux, "failed allocating a %d bytes buffer",
+ demux->tag_data_size);
+ goto beach;
+ }
+
+ /* Fill buffer with data */
+ GST_BUFFER_TIMESTAMP (buffer) = pts * GST_MSECOND;
+ GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_OFFSET (buffer) = demux->audio_offset++;
+ GST_BUFFER_OFFSET_END (buffer) = demux->audio_offset;
+
+ if (G_UNLIKELY (demux->audio_need_discont)) {
+ GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+ demux->audio_need_discont = FALSE;
+ }
+
+ gst_segment_set_last_stop (demux->segment, GST_FORMAT_TIME,
+ GST_BUFFER_TIMESTAMP (buffer));
+
+ /* Do we need a newsegment event ? */
+ if (G_UNLIKELY (demux->audio_need_segment)) {
+ 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 (-1));
+ demux->new_seg_event = gst_event_new_new_segment (FALSE,
+ demux->segment->rate, demux->segment->format,
+ demux->segment->last_stop, -1, demux->segment->last_stop);
+ }
+
+ gst_pad_push_event (demux->audio_pad, gst_event_ref (demux->new_seg_event));
+
+ demux->audio_need_segment = FALSE;
+ }
+
+ memcpy (GST_BUFFER_DATA (buffer), data + 7 + codec_data,
+ demux->tag_data_size - codec_data);
+
+ 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 (buffer), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), GST_BUFFER_OFFSET (buffer));
+
+ /* Push downstream */
+ ret = gst_pad_push (demux->audio_pad, buffer);
+
+beach:
+ return ret;
+}
+
+GstFlowReturn
+gst_flv_parse_tag_video (GstFLVDemux * demux, const guint8 * data,
+ size_t data_size)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ GstBuffer *buffer = NULL;
+ guint32 pts = 0, codec_tag = 0, codec_data = 0;
+ gboolean keyframe = FALSE;
+ guint8 flags = 0;
+
+ GST_LOG_OBJECT (demux, "parsing a video tag");
+
+ /* Grab information about audio tag */
+ pts = FLV_GET_BEUI24 (data, data_size);
+ flags = GST_READ_UINT8 (data + 7);
+
+ /* Keyframe */
+ if (flags & 0x10) {
+ keyframe = TRUE;
+ } else if (flags & 0x20) {
+ keyframe = FALSE;
+ }
+ /* Codec tag */
+ if (flags & 0x02) { // H263
+ codec_tag = 2;
+ codec_data = 1;
+ } else if (flags & 0x03) { // Sorenson Spark
+ codec_tag = 3;
+ codec_data = 1;
+ } else if (flags & 0x04) { // VP6
+ codec_tag = 4;
+ codec_data = 2;
+ } else if (flags & 0x05) { // VP6 Alpha
+ codec_tag = 5;
+ codec_data = 2;
+ }
+
+ GST_LOG_OBJECT (demux, "video tag with codec tag %d, keyframe (%d)",
+ codec_tag, keyframe);
+
+ /* If we don't have our video pad created, then create it. */
+ if (G_UNLIKELY (!demux->video_pad)) {
+ GstCaps *caps = NULL;
+
+ demux->video_pad = gst_pad_new ("video", GST_PAD_SRC);
+ if (G_UNLIKELY (!demux->video_pad)) {
+ GST_WARNING_OBJECT (demux, "failed creating video pad");
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+ /* Make it active */
+ gst_pad_set_active (demux->video_pad, TRUE);
+
+ /* Generate caps for that pad */
+ switch (codec_tag) {
+ case 2:
+ caps = gst_caps_new_simple ("video/x-flash-video", NULL);
+ break;
+ case 3:
+ caps = gst_caps_new_simple ("video/x-flash-video", NULL);
+ break;
+ case 4:
+ caps = gst_caps_new_simple ("video/x-vp6", NULL);
+ break;
+ case 5:
+ caps = gst_caps_new_simple ("video/x-vp6", NULL);
+ break;
+ default:
+ GST_WARNING_OBJECT (demux, "unsupported video codec tag %d", codec_tag);
+ }
+
+ if (G_UNLIKELY (!caps)) {
+ GST_WARNING_OBJECT (demux, "failed creating caps for video pad");
+ gst_object_unref (demux->video_pad);
+ demux->video_pad = NULL;
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+
+ gst_pad_set_caps (demux->video_pad, caps);
+
+ gst_caps_unref (caps);
+
+ /* Store the caps we have set */
+ demux->video_codec_tag = codec_tag;
+
+ /* 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));
+
+ /* We need to set caps before adding */
+ gst_element_add_pad (GST_ELEMENT (demux), demux->video_pad);
+ }
+
+ /* Check if caps have changed */
+ if (G_UNLIKELY (codec_tag != demux->video_codec_tag)) {
+ GstCaps *caps = NULL;
+
+ GST_DEBUG_OBJECT (demux, "video settings have changed, changing caps");
+
+ /* Generate caps for that pad */
+ switch (codec_tag) {
+ case 2:
+ caps = gst_caps_new_simple ("video/x-flash-video", NULL);
+ break;
+ case 3:
+ caps = gst_caps_new_simple ("video/x-flash-video", NULL);
+ break;
+ case 4:
+ caps = gst_caps_new_simple ("video/x-vp6", NULL);
+ break;
+ case 5:
+ caps = gst_caps_new_simple ("video/x-vp6", NULL);
+ break;
+ default:
+ GST_WARNING_OBJECT (demux, "unsupported video codec tag %d", codec_tag);
+ }
+
+ if (G_UNLIKELY (!caps)) {
+ GST_WARNING_OBJECT (demux, "failed creating caps for video pad");
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+
+ gst_pad_set_caps (demux->video_pad, caps);
+
+ gst_caps_unref (caps);
+
+ /* Store the caps we have set */
+ demux->video_codec_tag = codec_tag;
+ }
+
+ /* Push taglist if present */
+ if ((demux->has_audio & (demux->audio_pad != NULL)) &&
+ (demux->has_video & (demux->video_pad != NULL)) &&
+ 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;
+ }
+
+ /* Create buffer from pad */
+ ret = gst_pad_alloc_buffer (demux->video_pad, GST_BUFFER_OFFSET_NONE,
+ demux->tag_data_size - codec_data, GST_PAD_CAPS (demux->video_pad),
+ &buffer);
+ if (G_UNLIKELY (ret != GST_FLOW_OK)) {
+ GST_WARNING_OBJECT (demux, "failed allocating a %d bytes buffer",
+ demux->tag_data_size);
+ goto beach;
+ }
+
+ /* Fill buffer with data */
+ GST_BUFFER_TIMESTAMP (buffer) = pts * GST_MSECOND;
+ GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_OFFSET (buffer) = demux->video_offset++;
+ GST_BUFFER_OFFSET_END (buffer) = demux->video_offset;
+
+ if (!keyframe) {
+ GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
+ }
+
+ if (G_UNLIKELY (demux->video_need_discont)) {
+ GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+ demux->video_need_discont = FALSE;
+ }
+
+ gst_segment_set_last_stop (demux->segment, GST_FORMAT_TIME,
+ GST_BUFFER_TIMESTAMP (buffer));
+
+ /* Do we need a newsegment event ? */
+ if (G_UNLIKELY (demux->video_need_segment)) {
+ 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 (-1));
+ demux->new_seg_event = gst_event_new_new_segment (FALSE,
+ demux->segment->rate, demux->segment->format,
+ demux->segment->last_stop, -1, demux->segment->last_stop);
+ }
+
+ gst_pad_push_event (demux->video_pad, gst_event_ref (demux->new_seg_event));
+
+ demux->video_need_segment = FALSE;
+ }
+
+ /* FIXME: safety checks */
+ memcpy (GST_BUFFER_DATA (buffer), data + 7 + codec_data,
+ demux->tag_data_size - codec_data);
+
+ 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 (buffer),
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), GST_BUFFER_OFFSET (buffer),
+ keyframe);
+
+ /* Push downstream */
+ ret = gst_pad_push (demux->video_pad, buffer);
+
+beach:
+ return ret;
+}
+
+GstFlowReturn
+gst_flv_parse_tag_type (GstFLVDemux * demux, const guint8 * data,
+ size_t data_size)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ guint8 tag_type = 0;
+
+ tag_type = data[0];
+
+ switch (tag_type) {
+ case 9:
+ demux->state = FLV_STATE_TAG_VIDEO;
+ break;
+ case 8:
+ demux->state = FLV_STATE_TAG_AUDIO;
+ 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 = FLV_GET_BEUI24 (data + 1, data_size - 1);
+ demux->tag_size = demux->tag_data_size + 11;
+
+ GST_LOG_OBJECT (demux, "tag data size is %d", demux->tag_data_size);
+
+ return ret;
+}
+
+GstFlowReturn
+gst_flv_parse_header (GstFLVDemux * demux, const guint8 * data,
+ size_t data_size)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ /* 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];
+
+ 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;
+}