summaryrefslogtreecommitdiffstats
path: root/gst
diff options
context:
space:
mode:
Diffstat (limited to 'gst')
-rw-r--r--gst/flv/gstflvdemux.c20
-rw-r--r--gst/flv/gstflvdemux.h2
-rw-r--r--gst/flv/gstflvparse.c425
3 files changed, 251 insertions, 196 deletions
diff --git a/gst/flv/gstflvdemux.c b/gst/flv/gstflvdemux.c
index 045b3450..0f5e3f79 100644
--- a/gst/flv/gstflvdemux.c
+++ b/gst/flv/gstflvdemux.c
@@ -118,6 +118,16 @@ gst_flv_demux_cleanup (GstFLVDemux * demux)
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);
@@ -1098,6 +1108,16 @@ gst_flv_demux_dispose (GObject * object)
demux->new_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;
diff --git a/gst/flv/gstflvdemux.h b/gst/flv/gstflvdemux.h
index bd0832b0..989584c4 100644
--- a/gst/flv/gstflvdemux.h
+++ b/gst/flv/gstflvdemux.h
@@ -88,6 +88,7 @@ struct _GstFLVDemux
gboolean audio_need_discont;
gboolean audio_need_segment;
gboolean audio_linked;
+ GstBuffer * audio_codec_data;
/* Video infos */
guint32 w;
@@ -100,6 +101,7 @@ struct _GstFLVDemux
gboolean video_need_segment;
gboolean video_linked;
gboolean got_par;
+ GstBuffer * video_codec_data;
gboolean random_access;
gboolean need_header;
diff --git a/gst/flv/gstflvparse.c b/gst/flv/gstflvparse.c
index 3a0e97b7..55a3ac67 100644
--- a/gst/flv/gstflvparse.c
+++ b/gst/flv/gstflvparse.c
@@ -391,6 +391,90 @@ gst_flv_parse_tag_script (GstFLVDemux * demux, const guint8 * data,
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:
+ caps = gst_caps_new_simple ("audio/mpeg",
+ "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, NULL);
+ codec_name = "MPEG 1 Audio, Layer 3 (MP3)";
+ break;
+ case 0:
+ case 3:
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "endianness", G_TYPE_INT, G_BYTE_ORDER,
+ "signed", G_TYPE_BOOLEAN, 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, NULL);
+ codec_name = "AAC";
+ 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, const guint8 * data,
size_t data_size)
@@ -433,7 +517,11 @@ gst_flv_parse_tag_audio (GstFLVDemux * demux, const guint8 * data,
}
/* Codec tag */
codec_tag = flags >> 4;
- codec_data = 1;
+ if (codec_tag == 10) { /* AAC has an extra byte for packet type */
+ codec_data = 2;
+ } else {
+ codec_data = 1;
+ }
GST_LOG_OBJECT (demux, "audio tag with %d channels, %dHz sampling rate, "
"%d bits width, codec tag %u (flags %02X)", channels, rate, width,
@@ -441,8 +529,6 @@ gst_flv_parse_tag_audio (GstFLVDemux * demux, const guint8 * data,
/* If we don't have our audio pad created, then create it. */
if (G_UNLIKELY (!demux->audio_pad)) {
- GstCaps *caps = NULL;
- gchar *codec_name = NULL;
demux->audio_pad = gst_pad_new ("audio", GST_PAD_SRC);
if (G_UNLIKELY (!demux->audio_pad)) {
@@ -454,64 +540,17 @@ gst_flv_parse_tag_audio (GstFLVDemux * demux, const guint8 * data,
/* Make it active */
gst_pad_set_active (demux->audio_pad, TRUE);
- 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:
- caps = gst_caps_new_simple ("audio/mpeg",
- "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, NULL);
- codec_name = "MPEG 1 Audio, Layer 3 (MP3)";
- break;
- case 0:
- case 3:
- caps = gst_caps_new_simple ("audio/x-raw-int",
- "endianness", G_TYPE_INT, G_BYTE_ORDER,
- "signed", G_TYPE_BOOLEAN, TRUE,
- "width", G_TYPE_INT, width, "depth", G_TYPE_INT, width, NULL);
- codec_name = "Raw Audio";
- break;
- case 5:
- case 6:
- caps = gst_caps_new_simple ("audio/x-nellymoser", NULL);
- codec_name = "Nellymoser ASAO";
- 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");
- ret = GST_FLOW_ERROR;
+ /* 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_caps_set_simple (caps,
- "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL);
-
- gst_pad_set_caps (demux->audio_pad, caps);
- 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, "created audio pad with caps %" GST_PTR_FORMAT,
- 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;
+ GST_PAD_CAPS (demux->audio_pad));
/* Set functions on the pad */
gst_pad_set_query_type_function (demux->audio_pad,
@@ -537,61 +576,10 @@ gst_flv_parse_tag_audio (GstFLVDemux * demux, const guint8 * data,
/* 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;
- gchar *codec_name = NULL;
-
GST_DEBUG_OBJECT (demux, "audio settings have changed, changing caps");
- 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:
- caps = gst_caps_new_simple ("audio/mpeg",
- "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, NULL);
- codec_name = "MPEG 1 Audio, Layer 3 (MP3)";
- break;
- case 0:
- case 3:
- caps = gst_caps_new_simple ("audio/x-raw-int", NULL);
- codec_name = "Raw Audio";
- break;
- case 6:
- caps = gst_caps_new_simple ("audio/x-nellymoser", NULL);
- codec_name = "Nellymoser ASAO";
- 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");
- 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);
- 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_caps_unref (caps);
-
- /* Store the caps we have set */
- demux->audio_codec_tag = codec_tag;
- demux->rate = rate;
- demux->channels = channels;
- demux->width = width;
+ /* Negotiate caps */
+ gst_flv_parse_audio_negotiate (demux, codec_tag, rate, channels, width);
}
/* Push taglist if present */
@@ -627,8 +615,38 @@ gst_flv_parse_tag_audio (GstFLVDemux * demux, const guint8 * data,
goto beach;
}
+ memcpy (GST_BUFFER_DATA (buffer), data + 7 + codec_data,
+ MIN (demux->tag_data_size - codec_data, GST_BUFFER_SIZE (buffer)));
+
demux->audio_linked = TRUE;
+ 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 = buffer;
+ /* 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 (buffer) = pts * GST_MSECOND;
GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
@@ -663,9 +681,6 @@ gst_flv_parse_tag_audio (GstFLVDemux * demux, const guint8 * data,
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)),
@@ -678,6 +693,74 @@ 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:
+ case 5:
+ caps = gst_caps_new_simple ("video/x-vp6-flash", NULL);
+ codec_name = "On2 VP6 Video";
+ 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, const guint8 * data,
size_t data_size)
@@ -710,6 +793,8 @@ gst_flv_parse_tag_video (GstFLVDemux * demux, const guint8 * data,
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) "
@@ -717,9 +802,6 @@ gst_flv_parse_tag_video (GstFLVDemux * demux, const guint8 * data,
/* If we don't have our video pad created, then create it. */
if (G_UNLIKELY (!demux->video_pad)) {
- GstCaps *caps = NULL;
- gchar *codec_name = 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");
@@ -729,54 +811,19 @@ gst_flv_parse_tag_video (GstFLVDemux * demux, const guint8 * data,
/* 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);
- codec_name = "Sorenson Video";
- break;
- case 3:
- caps = gst_caps_new_simple ("video/x-flash-screen", NULL);
- codec_name = "Flash Screen Video";
- case 4:
- case 5:
- caps = gst_caps_new_simple ("video/x-vp6-flash", NULL);
- codec_name = "On2 VP6 Video";
- 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");
+ 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;
}
- gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
- demux->par_x, demux->par_y, NULL);
-
/* 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_pad_set_caps (demux->video_pad, caps);
-
GST_DEBUG_OBJECT (demux, "created video pad with caps %" GST_PTR_FORMAT,
- caps);
-
- gst_caps_unref (caps);
- 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);
- }
-
- /* Store the caps we have set */
- demux->video_codec_tag = codec_tag;
+ GST_PAD_CAPS (demux->video_pad));
/* Set functions on the pad */
gst_pad_set_query_type_function (demux->video_pad,
@@ -801,54 +848,14 @@ gst_flv_parse_tag_video (GstFLVDemux * demux, const guint8 * data,
/* Check if caps have changed */
if (G_UNLIKELY (codec_tag != demux->video_codec_tag || demux->got_par)) {
- GstCaps *caps = NULL;
- gchar *codec_name = 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);
- codec_name = "Sorenson Video";
- break;
- case 3:
- caps = gst_caps_new_simple ("video/x-flash-screen", NULL);
- codec_name = "Flash Screen Video";
- case 4:
- case 5:
- caps = gst_caps_new_simple ("video/x-vp6", NULL);
- codec_name = "On2 VP6 Video";
- 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_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
- demux->par_x, demux->par_y, NULL);
+ gst_flv_parse_video_negotiate (demux, codec_tag);
/* 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_pad_set_caps (demux->video_pad, caps);
-
- gst_caps_unref (caps);
- 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);
- }
-
- /* Store the caps we have set */
- demux->video_codec_tag = codec_tag;
}
/* Push taglist if present */
@@ -886,6 +893,36 @@ gst_flv_parse_tag_video (GstFLVDemux * demux, const guint8 * data,
demux->video_linked = TRUE;
+ memcpy (GST_BUFFER_DATA (buffer), data + 7 + codec_data,
+ MIN (demux->tag_data_size - codec_data, GST_BUFFER_SIZE (buffer)));
+
+ 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 = buffer;
+ /* 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 (buffer) = pts * GST_MSECOND;
GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
@@ -934,10 +971,6 @@ gst_flv_parse_tag_video (GstFLVDemux * demux, const guint8 * data,
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),