"audio/mpeg, mpegversion = (int) 4, stream-format = (string) raw, framed = (boolean) TRUE; "
"audio/x-nellymoser, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 16000, 22050, 44100 }; "
"audio/x-raw, format = (string) { U8, S16LE }, layout = (string) interleaved, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
- "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-alaw, channels = (int) { 1, 2 }, rate = (int) 8000; "
+ "audio/x-mulaw, channels = (int) { 1, 2 }, rate = (int) 8000; "
"audio/x-speex, channels = (int) 1, rate = (int) 16000;")
);
static GstIndex *gst_flv_demux_get_index (GstElement * element);
+static void gst_flv_demux_push_tags (GstFlvDemux * demux);
+
static void
gst_flv_demux_parse_and_add_index_entry (GstFlvDemux * demux, GstClockTime ts,
guint64 pos, gboolean keyframe)
demux->h = d;
} else if (!strcmp (tag_name, "framerate")) {
demux->framerate = d;
+ } else if (!strcmp (tag_name, "audiodatarate")) {
+ gst_tag_list_add (demux->audio_tags, GST_TAG_MERGE_REPLACE,
+ GST_TAG_NOMINAL_BITRATE, (guint) (d * 1024), NULL);
+ } else if (!strcmp (tag_name, "videodatarate")) {
+ gst_tag_list_add (demux->video_tags, GST_TAG_MERGE_REPLACE,
+ GST_TAG_NOMINAL_BITRATE, (guint) (d * 1024), NULL);
} else {
GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name);
}
} 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")) {
+ } else if (!strcmp (tag_name, "metadatacreator")
+ || !strcmp (tag_name, "encoder")) {
gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
GST_TAG_ENCODER, s, NULL);
} else {
return FALSE;
}
+static void
+gst_flv_demux_clear_tags (GstFlvDemux * demux)
+{
+ GST_DEBUG_OBJECT (demux, "clearing taglist");
+
+ if (demux->taglist) {
+ gst_tag_list_unref (demux->taglist);
+ }
+ demux->taglist = gst_tag_list_new_empty ();
+ gst_tag_list_set_scope (demux->taglist, GST_TAG_SCOPE_GLOBAL);
+
+ if (demux->audio_tags) {
+ gst_tag_list_unref (demux->audio_tags);
+ }
+ demux->audio_tags = gst_tag_list_new_empty ();
+
+ if (demux->video_tags) {
+ gst_tag_list_unref (demux->video_tags);
+ }
+ demux->video_tags = gst_tag_list_new_empty ();
+}
+
static GstFlowReturn
gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
{
gboolean end_marker = FALSE;
GST_DEBUG_OBJECT (demux, "we have a metadata script object");
+ gst_flv_demux_clear_tags (demux);
+
if (!gst_byte_reader_get_uint8 (&reader, &type)) {
g_free (function_name);
goto cleanup;
goto cleanup;
}
- demux->push_tags = TRUE;
+ gst_flv_demux_push_tags (demux);
}
g_free (function_name);
guint32 rate, guint32 channels, guint32 width)
{
GstCaps *caps = NULL, *old_caps;
- gchar *codec_name = NULL;
gboolean ret = FALSE;
guint adjusted_rate = rate;
+ guint adjusted_channels = channels;
GstEvent *event;
gchar *stream_id;
} else {
adjusted_rate = rate;
}
+
+ adjusted_channels =
+ gst_codec_utils_aac_get_channels (map.data, map.size);
+
+ if (adjusted_channels && (channels != adjusted_channels)) {
+ GST_LOG_OBJECT (demux, "Ajusting AAC channels %d -> %d", channels,
+ adjusted_channels);
+ } else {
+ adjusted_channels = channels;
+ }
}
gst_buffer_unmap (demux->audio_codec_data, &map);
}
gst_caps_set_simple (caps, "rate", G_TYPE_INT, adjusted_rate,
- "channels", G_TYPE_INT, channels, NULL);
+ "channels", G_TYPE_INT, adjusted_channels, NULL);
if (demux->audio_codec_data) {
gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER,
demux->width = width;
if (caps) {
- codec_name = gst_pb_utils_get_codec_description (caps);
-
- if (codec_name) {
- if (demux->taglist == NULL)
- demux->taglist = gst_tag_list_new_empty ();
- gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_AUDIO_CODEC, codec_name, NULL);
- g_free (codec_name);
- }
-
GST_DEBUG_OBJECT (demux->audio_pad, "successfully negotiated caps %"
GST_PTR_FORMAT, caps);
+
+ gst_flv_demux_push_tags (demux);
} else {
GST_DEBUG_OBJECT (demux->audio_pad, "delayed setting caps");
}
}
static void
-gst_flv_demux_push_tags (GstFlvDemux * demux)
+gst_flv_demux_add_codec_tag (GstFlvDemux * demux, const gchar * tag,
+ GstPad * pad)
{
- if (demux->has_audio && !demux->audio_pad) {
- GST_DEBUG_OBJECT (demux,
- "Waiting for audio stream pad to come up before we can push tags");
- return;
+ if (pad) {
+ GstCaps *caps = gst_pad_get_current_caps (pad);
+
+ if (caps) {
+ gchar *codec_name = gst_pb_utils_get_codec_description (caps);
+
+ if (codec_name) {
+ gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
+ tag, codec_name, NULL);
+ g_free (codec_name);
+ }
+
+ gst_caps_unref (caps);
+ }
}
- if (demux->has_video && !demux->video_pad) {
- GST_DEBUG_OBJECT (demux,
- "Waiting for video stream pad to come up before we can push tags");
- return;
+}
+
+static void
+gst_flv_demux_push_tags (GstFlvDemux * demux)
+{
+ gst_flv_demux_add_codec_tag (demux, GST_TAG_AUDIO_CODEC, demux->audio_pad);
+ gst_flv_demux_add_codec_tag (demux, GST_TAG_VIDEO_CODEC, demux->video_pad);
+
+ GST_DEBUG_OBJECT (demux, "pushing %" GST_PTR_FORMAT, demux->taglist);
+
+ gst_flv_demux_push_src_event (demux,
+ gst_event_new_tag (gst_tag_list_copy (demux->taglist)));
+
+#ifdef TIZEN_FEATURE_FLVDEMUX_MODIFICATION
+ GST_DEBUG_OBJECT (demux, "post tag msg %" GST_PTR_FORMAT, demux->taglist);
+
+ /* post message flv tag (for early recive application) */
+ gst_element_post_message (GST_ELEMENT_CAST (demux),
+ gst_message_new_tag (GST_OBJECT_CAST (demux),
+ gst_tag_list_copy (demux->taglist)));
+#endif
+
+ if (demux->audio_pad) {
+ GST_DEBUG_OBJECT (demux->audio_pad, "pushing audio %" GST_PTR_FORMAT,
+ demux->audio_tags);
+ gst_pad_push_event (demux->audio_pad,
+ gst_event_new_tag (gst_tag_list_copy (demux->audio_tags)));
}
- if (demux->taglist) {
- GST_DEBUG_OBJECT (demux, "pushing tags out %" GST_PTR_FORMAT,
- demux->taglist);
- gst_tag_list_set_scope (demux->taglist, GST_TAG_SCOPE_GLOBAL);
- gst_flv_demux_push_src_event (demux, gst_event_new_tag (demux->taglist));
- demux->taglist = gst_tag_list_new_empty ();
- demux->push_tags = FALSE;
+
+ if (demux->video_pad) {
+ GST_DEBUG_OBJECT (demux->video_pad, "pushing video %" GST_PTR_FORMAT,
+ demux->video_tags);
+ gst_pad_push_event (demux->video_pad,
+ gst_event_new_tag (gst_tag_list_copy (demux->video_tags)));
}
}
}
/* codec tags with special rates */
- if (codec_tag == 5 || codec_tag == 14)
+ if (codec_tag == 5 || codec_tag == 14 || codec_tag == 7 || codec_tag == 8)
rate = 8000;
else if ((codec_tag == 4) || (codec_tag == 11))
rate = 16000;
GST_DEBUG_OBJECT (demux, "emitting no more pads");
gst_element_no_more_pads (GST_ELEMENT (demux));
demux->no_more_pads = TRUE;
- demux->push_tags = TRUE;
}
}
}
}
- /* Push taglist if present */
- if (G_UNLIKELY (demux->push_tags))
- gst_flv_demux_push_tags (demux);
-
/* Check if we have anything to push */
if (demux->tag_data_size <= codec_data) {
GST_LOG_OBJECT (demux, "Nothing left in this tag, returning");
" after 6 seconds of audio");
gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
demux->no_more_pads = TRUE;
- demux->push_tags = TRUE;
}
/* Push downstream */
{
gboolean ret = FALSE;
GstCaps *caps = NULL, *old_caps;
- gchar *codec_name = NULL;
GstEvent *event;
gchar *stream_id;
gst_caps_new_simple ("video/x-h264", "stream-format", G_TYPE_STRING,
"avc", NULL);
break;
+ /* The following two are non-standard but apparently used, see in ffmpeg
+ * https://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavformat/flvdec.c;h=2bf1e059e1cbeeb79e4af9542da23f4560e1cf59;hb=b18d6c58000beed872d6bb1fe7d0fbe75ae26aef#l254
+ * https://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavformat/flvdec.c;h=2bf1e059e1cbeeb79e4af9542da23f4560e1cf59;hb=b18d6c58000beed872d6bb1fe7d0fbe75ae26aef#l282
+ */
+ case 8:
+ caps = gst_caps_new_empty_simple ("video/x-h263");
+ break;
+ case 9:
+ caps =
+ gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
+ "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
+ break;
default:
GST_WARNING_OBJECT (demux, "unsupported video codec tag %u", codec_tag);
}
goto beach;
}
- gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
- demux->par_x, demux->par_y, NULL);
+ if (demux->got_par) {
+ gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
+ demux->par_x, demux->par_y, NULL);
+ }
if (G_LIKELY (demux->w)) {
gst_caps_set_simple (caps, "width", G_TYPE_INT, demux->w, NULL);
demux->video_codec_tag = codec_tag;
if (caps) {
- codec_name = gst_pb_utils_get_codec_description (caps);
-
- if (codec_name) {
- if (demux->taglist == NULL)
- demux->taglist = gst_tag_list_new_empty ();
- gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_VIDEO_CODEC, codec_name, NULL);
- g_free (codec_name);
- }
-
GST_DEBUG_OBJECT (demux->video_pad, "successfully negotiated caps %"
GST_PTR_FORMAT, caps);
+
+ gst_flv_demux_push_tags (demux);
} else {
GST_DEBUG_OBJECT (demux->video_pad, "delayed setting caps");
}
cts = GST_READ_UINT24_BE (data + 9);
cts = (cts + 0xff800000) ^ 0xff800000;
+ if (cts < 0 && ABS (cts) > dts) {
+ GST_ERROR_OBJECT (demux, "Detected a negative composition time offset "
+ "'%d' that would lead to negative PTS, fixing", cts);
+ cts += ABS (cts) - dts;
+ }
+
GST_LOG_OBJECT (demux, "got cts %d", cts);
}
switch (avc_packet_type) {
case 0:
{
+ if (demux->tag_data_size < codec_data) {
+ GST_ERROR_OBJECT (demux, "Got invalid H.264 codec, ignoring.");
+ break;
+ }
+
/* AVCDecoderConfigurationRecord data */
GST_LOG_OBJECT (demux, "got an H.264 codec data packet");
if (demux->video_codec_data) {
GST_DEBUG_OBJECT (demux, "emitting no more pads");
gst_element_no_more_pads (GST_ELEMENT (demux));
demux->no_more_pads = TRUE;
- demux->push_tags = TRUE;
}
}
demux->got_par = FALSE;
}
- /* Push taglist if present */
- if (G_UNLIKELY (demux->push_tags))
- gst_flv_demux_push_tags (demux);
-
/* Check if we have anything to push */
if (demux->tag_data_size <= codec_data) {
GST_LOG_OBJECT (demux, "Nothing left in this tag, returning");
" after 6 seconds of video");
gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
demux->no_more_pads = TRUE;
- demux->push_tags = TRUE;
}
/* Push downstream */
demux->has_audio = FALSE;
demux->has_video = FALSE;
- demux->push_tags = FALSE;
demux->got_par = FALSE;
demux->indexed = FALSE;
g_array_free (demux->filepositions, TRUE);
demux->filepositions = NULL;
}
+
+ gst_flv_demux_clear_tags (demux);
}
/*
gst_buffer_unref (buffer);
buffer = NULL;
+ if (G_UNLIKELY (offset < tag_size))
+ goto exit;
+
offset -= tag_size;
if (GST_FLOW_OK != gst_flv_demux_pull_range (demux, demux->sinkpad, offset,
12, &buffer))
}
/* pause if something went wrong or at end */
- if (G_UNLIKELY (ret != GST_FLOW_OK))
+ if (G_UNLIKELY (ret != GST_FLOW_OK) && !(ret == GST_FLOW_NOT_LINKED
+ && !demux->no_more_pads))
goto pause;
gst_object_unref (demux);
GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
}
} else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
- GST_ELEMENT_ERROR (demux, STREAM, FAILED,
- ("Internal data stream error."),
- ("stream stopped, reason %s", reason));
+ GST_ELEMENT_FLOW_ERROR (demux, ret);
gst_flv_demux_push_src_event (demux, gst_event_new_eos ());
}
gst_object_unref (demux);
demux->taglist = NULL;
}
+ if (demux->audio_tags) {
+ gst_tag_list_unref (demux->audio_tags);
+ demux->audio_tags = NULL;
+ }
+
+ if (demux->video_tags) {
+ gst_tag_list_unref (demux->video_tags);
+ demux->video_tags = NULL;
+ }
+
if (demux->flowcombiner) {
gst_flow_combiner_free (demux->flowcombiner);
demux->flowcombiner = NULL;
gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
demux->adapter = gst_adapter_new ();
- demux->taglist = gst_tag_list_new_empty ();
demux->flowcombiner = gst_flow_combiner_new ();
- gst_segment_init (&demux->segment, GST_FORMAT_TIME);
demux->own_index = FALSE;