flv: port to 0.11

* use G_DEFINE_TYPE
* adjust to new GstBuffer
* misc segment and caps changes
This commit is contained in:
Mark Nauwelaerts 2011-07-04 11:09:19 +02:00
parent d59a00aa1c
commit 54c9b13ea7
3 changed files with 335 additions and 284 deletions

View file

@ -77,7 +77,8 @@ static GstStaticPadTemplate video_src_template =
GST_DEBUG_CATEGORY_STATIC (flvdemux_debug); GST_DEBUG_CATEGORY_STATIC (flvdemux_debug);
#define GST_CAT_DEFAULT flvdemux_debug #define GST_CAT_DEFAULT flvdemux_debug
GST_BOILERPLATE (GstFlvDemux, gst_flv_demux, GstElement, GST_TYPE_ELEMENT); #define gst_flv_demux_parent_class parent_class
G_DEFINE_TYPE (GstFlvDemux, gst_flv_demux, GST_TYPE_ELEMENT);
/* 9 bytes of header + 4 bytes of first previous tag size */ /* 9 bytes of header + 4 bytes of first previous tag size */
#define FLV_HEADER_SIZE 13 #define FLV_HEADER_SIZE 13
@ -527,17 +528,22 @@ static GstFlowReturn
gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer) gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
{ {
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buffer); GstByteReader reader;
guint8 type = 0; guint8 type = 0;
guint8 *data;
gsize size;
g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 7, GST_FLOW_ERROR); g_return_val_if_fail (gst_buffer_get_size (buffer) >= 7, GST_FLOW_ERROR);
data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
gst_byte_reader_init (&reader, data, size);
gst_byte_reader_skip (&reader, 7); gst_byte_reader_skip (&reader, 7);
GST_LOG_OBJECT (demux, "parsing a script tag"); GST_LOG_OBJECT (demux, "parsing a script tag");
if (!gst_byte_reader_get_uint8 (&reader, &type)) if (!gst_byte_reader_get_uint8 (&reader, &type))
return GST_FLOW_OK; goto cleanup;
/* Must be string */ /* Must be string */
if (type == 2) { if (type == 2) {
@ -554,7 +560,7 @@ gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
if (!gst_byte_reader_get_uint8 (&reader, &type)) { if (!gst_byte_reader_get_uint8 (&reader, &type)) {
g_free (function_name); g_free (function_name);
return GST_FLOW_OK; goto cleanup;
} }
switch (type) { switch (type) {
@ -565,7 +571,7 @@ gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
/* ECMA array */ /* ECMA array */
if (!gst_byte_reader_get_uint32_be (&reader, &nb_elems)) { if (!gst_byte_reader_get_uint32_be (&reader, &nb_elems)) {
g_free (function_name); g_free (function_name);
return GST_FLOW_OK; goto cleanup;
} }
/* The number of elements is just a hint, some files have /* The number of elements is just a hint, some files have
@ -591,7 +597,7 @@ gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
default: default:
GST_DEBUG_OBJECT (demux, "Unhandled script data type : %d", type); GST_DEBUG_OBJECT (demux, "Unhandled script data type : %d", type);
g_free (function_name); g_free (function_name);
return GST_FLOW_OK; goto cleanup;
} }
demux->push_tags = TRUE; demux->push_tags = TRUE;
@ -616,6 +622,9 @@ gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
} }
} }
cleanup:
gst_buffer_unmap (buffer, data, -1);
return ret; return ret;
} }
@ -656,13 +665,17 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag,
break; break;
case 10: case 10:
{ {
guint8 *data = NULL;
gsize size;
if (demux->audio_codec_data)
data = gst_buffer_map (demux->audio_codec_data, &size, NULL,
GST_MAP_READ);
/* use codec-data to extract and verify samplerate */ /* use codec-data to extract and verify samplerate */
if (demux->audio_codec_data && if (demux->audio_codec_data && size >= 2) {
GST_BUFFER_SIZE (demux->audio_codec_data) >= 2) {
gint freq_index; gint freq_index;
freq_index = freq_index = GST_READ_UINT16_BE (data);
((GST_READ_UINT16_BE (GST_BUFFER_DATA (demux->audio_codec_data))));
freq_index = (freq_index & 0x0780) >> 7; freq_index = (freq_index & 0x0780) >> 7;
adjusted_rate = adjusted_rate =
gst_codec_utils_aac_get_sample_rate_from_index (freq_index); gst_codec_utils_aac_get_sample_rate_from_index (freq_index);
@ -674,6 +687,8 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag,
adjusted_rate = rate; adjusted_rate = rate;
} }
} }
if (data)
gst_buffer_unmap (demux->audio_codec_data, data, -1);
caps = gst_caps_new_simple ("audio/mpeg", caps = gst_caps_new_simple ("audio/mpeg",
"mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE, "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
"stream-format", G_TYPE_STRING, "raw", NULL); "stream-format", G_TYPE_STRING, "raw", NULL);
@ -766,20 +781,30 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
guint32 pts = 0, codec_tag = 0, rate = 5512, width = 8, channels = 1; guint32 pts = 0, codec_tag = 0, rate = 5512, width = 8, channels = 1;
guint32 codec_data = 0, pts_ext = 0; guint32 codec_data = 0, pts_ext = 0;
guint8 flags = 0; guint8 flags = 0;
guint8 *data = GST_BUFFER_DATA (buffer); guint8 *data;
GstBuffer *outbuf; GstBuffer *outbuf;
gsize size;
GST_LOG_OBJECT (demux, "parsing an audio tag"); GST_LOG_OBJECT (demux, "parsing an audio tag");
if (demux->no_more_pads && !demux->audio_pad) { if (demux->no_more_pads && !demux->audio_pad) {
GST_WARNING_OBJECT (demux, GST_WARNING_OBJECT (demux,
"Signaled no-more-pads already but had no audio pad -- ignoring"); "Signaled no-more-pads already but had no audio pad -- ignoring");
goto beach; return GST_FLOW_OK;
} }
g_return_val_if_fail (GST_BUFFER_SIZE (buffer) == demux->tag_size, g_return_val_if_fail (gst_buffer_get_size (buffer) == demux->tag_size,
GST_FLOW_ERROR); GST_FLOW_ERROR);
/* Error out on tags with too small headers */
if (gst_buffer_get_size (buffer) < 11) {
GST_ERROR_OBJECT (demux, "Too small tag size (%d)",
gst_buffer_get_size (buffer));
return GST_FLOW_ERROR;
}
data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
/* Grab information about audio tag */ /* Grab information about audio tag */
pts = GST_READ_UINT24_BE (data); pts = GST_READ_UINT24_BE (data);
/* read the pts extension to 32 bits integer */ /* read the pts extension to 32 bits integer */
@ -790,20 +815,13 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X (%d)", data[0], data[1], GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X (%d)", data[0], data[1],
data[2], data[3], pts); data[2], data[3], pts);
/* Error out on tags with too small headers */
if (GST_BUFFER_SIZE (buffer) < 11) {
GST_ERROR_OBJECT (demux, "Too small tag size (%d)",
GST_BUFFER_SIZE (buffer));
return GST_FLOW_ERROR;
}
/* Silently skip buffers with no data */
if (GST_BUFFER_SIZE (buffer) == 11)
return GST_FLOW_OK;
/* Skip the stream id and go directly to the flags */ /* Skip the stream id and go directly to the flags */
flags = GST_READ_UINT8 (data + 7); flags = GST_READ_UINT8 (data + 7);
/* Silently skip buffers with no data */
if (size == 11)
goto beach;
/* Channels */ /* Channels */
if (flags & 0x01) { if (flags & 0x01) {
channels = 2; channels = 2;
@ -858,9 +876,17 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
ret = GST_FLOW_ERROR; ret = GST_FLOW_ERROR;
goto beach; goto beach;
} }
#ifndef GST_DISABLE_GST_DEBUG
{
GstCaps *caps;
caps = gst_pad_get_current_caps (demux->audio_pad);
GST_DEBUG_OBJECT (demux, "created audio pad with caps %" GST_PTR_FORMAT, GST_DEBUG_OBJECT (demux, "created audio pad with caps %" GST_PTR_FORMAT,
GST_PAD_CAPS (demux->audio_pad)); caps);
if (caps)
gst_caps_unref (caps);
}
#endif
/* Set functions on the pad */ /* Set functions on the pad */
gst_pad_set_query_type_function (demux->audio_pad, gst_pad_set_query_type_function (demux->audio_pad,
@ -914,9 +940,8 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
} }
/* Create buffer from pad */ /* Create buffer from pad */
outbuf = outbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY,
gst_buffer_create_sub (buffer, 7 + codec_data, 7 + codec_data, demux->tag_data_size - codec_data);
demux->tag_data_size - codec_data);
if (demux->audio_codec_tag == 10) { if (demux->audio_codec_tag == 10) {
guint8 aac_packet_type = GST_READ_UINT8 (data + 8); guint8 aac_packet_type = GST_READ_UINT8 (data + 8);
@ -950,7 +975,6 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
GST_BUFFER_OFFSET (outbuf) = demux->audio_offset++; GST_BUFFER_OFFSET (outbuf) = demux->audio_offset++;
GST_BUFFER_OFFSET_END (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 || if (demux->duration == GST_CLOCK_TIME_NONE ||
demux->duration < GST_BUFFER_TIMESTAMP (outbuf)) demux->duration < GST_BUFFER_TIMESTAMP (outbuf))
@ -968,24 +992,18 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
demux->audio_need_discont = FALSE; demux->audio_need_discont = FALSE;
} }
gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME, demux->segment.position = GST_BUFFER_TIMESTAMP (outbuf);
GST_BUFFER_TIMESTAMP (outbuf));
/* Do we need a newsegment event ? */ /* Do we need a newsegment event ? */
if (G_UNLIKELY (demux->audio_need_segment)) { if (G_UNLIKELY (demux->audio_need_segment)) {
if (demux->close_seg_event) /* FIXME need one segment sent for all stream to maintain a/v sync */
gst_pad_push_event (demux->audio_pad,
gst_event_ref (demux->close_seg_event));
if (!demux->new_seg_event) { if (!demux->new_seg_event) {
GST_DEBUG_OBJECT (demux, "pushing newsegment from %" GST_DEBUG_OBJECT (demux, "pushing newsegment from %"
GST_TIME_FORMAT " to %" GST_TIME_FORMAT, GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
GST_TIME_ARGS (demux->segment.last_stop), GST_TIME_ARGS (demux->segment.position),
GST_TIME_ARGS (demux->segment.stop)); GST_TIME_ARGS (demux->segment.stop));
demux->new_seg_event = demux->segment.start = demux->segment.time = demux->segment.position;
gst_event_new_new_segment (FALSE, demux->segment.rate, demux->new_seg_event = gst_event_new_segment (&demux->segment);
demux->segment.format, demux->segment.last_stop,
demux->segment.stop, demux->segment.last_stop);
} else { } else {
GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event"); GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event");
} }
@ -997,7 +1015,8 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
GST_LOG_OBJECT (demux, "pushing %d bytes buffer at pts %" GST_TIME_FORMAT GST_LOG_OBJECT (demux, "pushing %d bytes buffer at pts %" GST_TIME_FORMAT
" with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT, " with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT,
GST_BUFFER_SIZE (outbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), gst_buffer_get_size (outbuf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf)); GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf));
if (!GST_CLOCK_TIME_IS_VALID (demux->audio_start)) { if (!GST_CLOCK_TIME_IS_VALID (demux->audio_start)) {
@ -1022,7 +1041,7 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
ret = gst_pad_push (demux->audio_pad, outbuf); ret = gst_pad_push (demux->audio_pad, outbuf);
if (G_UNLIKELY (ret != GST_FLOW_OK)) { if (G_UNLIKELY (ret != GST_FLOW_OK)) {
if (demux->segment.rate < 0.0 && ret == GST_FLOW_UNEXPECTED && if (demux->segment.rate < 0.0 && ret == GST_FLOW_UNEXPECTED &&
demux->segment.last_stop > demux->segment.stop) { demux->segment.position > demux->segment.stop) {
/* In reverse playback we can get a GST_FLOW_UNEXPECTED when /* In reverse playback we can get a GST_FLOW_UNEXPECTED when
* we are at the end of the segment, so we just need to jump * we are at the end of the segment, so we just need to jump
* back to the previous section. */ * back to the previous section. */
@ -1043,6 +1062,8 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
demux->audio_linked = TRUE; demux->audio_linked = TRUE;
beach: beach:
gst_buffer_unmap (buffer, data, -1);
return ret; return ret;
} }
@ -1144,21 +1165,28 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
guint32 pts = 0, codec_data = 1, pts_ext = 0; guint32 pts = 0, codec_data = 1, pts_ext = 0;
gboolean keyframe = FALSE; gboolean keyframe = FALSE;
guint8 flags = 0, codec_tag = 0; guint8 flags = 0, codec_tag = 0;
guint8 *data = GST_BUFFER_DATA (buffer); guint8 *data;
GstBuffer *outbuf; GstBuffer *outbuf;
gsize size;
g_return_val_if_fail (GST_BUFFER_SIZE (buffer) == demux->tag_size, g_return_val_if_fail (gst_buffer_get_size (buffer) == demux->tag_size,
GST_FLOW_ERROR); GST_FLOW_ERROR);
GST_LOG_OBJECT (demux, "parsing a video tag"); GST_LOG_OBJECT (demux, "parsing a video tag");
if (demux->no_more_pads && !demux->video_pad) { if (demux->no_more_pads && !demux->video_pad) {
GST_WARNING_OBJECT (demux, GST_WARNING_OBJECT (demux,
"Signaled no-more-pads already but had no audio pad -- ignoring"); "Signaled no-more-pads already but had no audio pad -- ignoring");
goto beach; return GST_FLOW_OK;
} }
if (gst_buffer_get_size (buffer) < 12) {
GST_ERROR_OBJECT (demux, "Too small tag size");
return GST_FLOW_ERROR;
}
data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
/* Grab information about video tag */ /* Grab information about video tag */
pts = GST_READ_UINT24_BE (data); pts = GST_READ_UINT24_BE (data);
/* read the pts extension to 32 bits integer */ /* read the pts extension to 32 bits integer */
@ -1169,11 +1197,6 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X (%d)", data[0], data[1], GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X (%d)", data[0], data[1],
data[2], data[3], pts); data[2], data[3], pts);
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 */ /* Skip the stream id and go directly to the flags */
flags = GST_READ_UINT8 (data + 7); flags = GST_READ_UINT8 (data + 7);
@ -1223,8 +1246,17 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
* metadata tag that would come later and trigger a caps change */ * metadata tag that would come later and trigger a caps change */
demux->got_par = FALSE; demux->got_par = FALSE;
#ifndef GST_DISABLE_GST_DEBUG
{
GstCaps *caps;
caps = gst_pad_get_current_caps (demux->video_pad);
GST_DEBUG_OBJECT (demux, "created video pad with caps %" GST_PTR_FORMAT, GST_DEBUG_OBJECT (demux, "created video pad with caps %" GST_PTR_FORMAT,
GST_PAD_CAPS (demux->video_pad)); caps);
if (caps)
gst_caps_unref (caps);
}
#endif
/* Set functions on the pad */ /* Set functions on the pad */
gst_pad_set_query_type_function (demux->video_pad, gst_pad_set_query_type_function (demux->video_pad,
@ -1280,9 +1312,8 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
} }
/* Create buffer from pad */ /* Create buffer from pad */
outbuf = outbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY,
gst_buffer_create_sub (buffer, 7 + codec_data, 7 + codec_data, demux->tag_data_size - codec_data);
demux->tag_data_size - codec_data);
if (demux->video_codec_tag == 7) { if (demux->video_codec_tag == 7) {
guint8 avc_packet_type = GST_READ_UINT8 (data + 8); guint8 avc_packet_type = GST_READ_UINT8 (data + 8);
@ -1316,7 +1347,6 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
GST_BUFFER_OFFSET (outbuf) = demux->video_offset++; GST_BUFFER_OFFSET (outbuf) = demux->video_offset++;
GST_BUFFER_OFFSET_END (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 || if (demux->duration == GST_CLOCK_TIME_NONE ||
demux->duration < GST_BUFFER_TIMESTAMP (outbuf)) demux->duration < GST_BUFFER_TIMESTAMP (outbuf))
@ -1335,24 +1365,18 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
demux->video_need_discont = FALSE; demux->video_need_discont = FALSE;
} }
gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME, demux->segment.position = GST_BUFFER_TIMESTAMP (outbuf);
GST_BUFFER_TIMESTAMP (outbuf));
/* Do we need a newsegment event ? */ /* Do we need a newsegment event ? */
if (G_UNLIKELY (demux->video_need_segment)) { if (G_UNLIKELY (demux->video_need_segment)) {
if (demux->close_seg_event) /* FIXME need one segment sent for all stream to maintain a/v sync */
gst_pad_push_event (demux->video_pad,
gst_event_ref (demux->close_seg_event));
if (!demux->new_seg_event) { if (!demux->new_seg_event) {
GST_DEBUG_OBJECT (demux, "pushing newsegment from %" GST_DEBUG_OBJECT (demux, "pushing newsegment from %"
GST_TIME_FORMAT " to %" GST_TIME_FORMAT, GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
GST_TIME_ARGS (demux->segment.last_stop), GST_TIME_ARGS (demux->segment.position),
GST_TIME_ARGS (demux->segment.stop)); GST_TIME_ARGS (demux->segment.stop));
demux->new_seg_event = demux->segment.start = demux->segment.time = demux->segment.position;
gst_event_new_new_segment (FALSE, demux->segment.rate, demux->new_seg_event = gst_event_new_segment (&demux->segment);
demux->segment.format, demux->segment.last_stop,
demux->segment.stop, demux->segment.last_stop);
} else { } else {
GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event"); GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event");
} }
@ -1364,7 +1388,7 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
GST_LOG_OBJECT (demux, "pushing %d bytes buffer at pts %" GST_TIME_FORMAT GST_LOG_OBJECT (demux, "pushing %d bytes buffer at pts %" GST_TIME_FORMAT
" with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT " with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
", keyframe (%d)", GST_BUFFER_SIZE (outbuf), ", keyframe (%d)", gst_buffer_get_size (outbuf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf), GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
keyframe); keyframe);
@ -1392,7 +1416,7 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
if (G_UNLIKELY (ret != GST_FLOW_OK)) { if (G_UNLIKELY (ret != GST_FLOW_OK)) {
if (demux->segment.rate < 0.0 && ret == GST_FLOW_UNEXPECTED && if (demux->segment.rate < 0.0 && ret == GST_FLOW_UNEXPECTED &&
demux->segment.last_stop > demux->segment.stop) { demux->segment.position > demux->segment.stop) {
/* In reverse playback we can get a GST_FLOW_UNEXPECTED when /* In reverse playback we can get a GST_FLOW_UNEXPECTED when
* we are at the end of the segment, so we just need to jump * we are at the end of the segment, so we just need to jump
* back to the previous section. */ * back to the previous section. */
@ -1413,6 +1437,7 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
demux->video_linked = TRUE; demux->video_linked = TRUE;
beach: beach:
gst_buffer_unmap (buffer, data, -1);
return ret; return ret;
} }
@ -1424,16 +1449,20 @@ gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
guint32 tag_data_size; guint32 tag_data_size;
guint8 type; guint8 type;
gboolean keyframe = TRUE; gboolean keyframe = TRUE;
GstClockTime ret; GstClockTime ret = GST_CLOCK_TIME_NONE;
guint8 *data = GST_BUFFER_DATA (buffer); guint8 *data, *bdata;
gsize size;
g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 12, GST_CLOCK_TIME_NONE); g_return_val_if_fail (gst_buffer_get_size (buffer) >= 12,
GST_CLOCK_TIME_NONE);
data = bdata = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
type = data[0]; type = data[0];
if (type != 9 && type != 8 && type != 18) { if (type != 9 && type != 8 && type != 18) {
GST_WARNING_OBJECT (demux, "Unsupported tag type %u", data[0]); GST_WARNING_OBJECT (demux, "Unsupported tag type %u", data[0]);
return GST_CLOCK_TIME_NONE; goto exit;
} }
if (type == 9) if (type == 9)
@ -1443,10 +1472,10 @@ gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
tag_data_size = GST_READ_UINT24_BE (data + 1); tag_data_size = GST_READ_UINT24_BE (data + 1);
if (GST_BUFFER_SIZE (buffer) >= tag_data_size + 11 + 4) { if (size >= tag_data_size + 11 + 4) {
if (GST_READ_UINT32_BE (data + tag_data_size + 11) != tag_data_size + 11) { if (GST_READ_UINT32_BE (data + tag_data_size + 11) != tag_data_size + 11) {
GST_WARNING_OBJECT (demux, "Invalid tag size"); GST_WARNING_OBJECT (demux, "Invalid tag size");
return GST_CLOCK_TIME_NONE; goto exit;
} }
} }
@ -1483,6 +1512,8 @@ gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
if (demux->duration == GST_CLOCK_TIME_NONE || demux->duration < ret) if (demux->duration == GST_CLOCK_TIME_NONE || demux->duration < ret)
demux->duration = ret; demux->duration = ret;
exit:
gst_buffer_unmap (buffer, bdata, -1);
return ret; return ret;
} }
@ -1491,9 +1522,11 @@ gst_flv_demux_parse_tag_type (GstFlvDemux * demux, GstBuffer * buffer)
{ {
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
guint8 tag_type = 0; guint8 tag_type = 0;
guint8 *data = GST_BUFFER_DATA (buffer); guint8 *data;
g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 4, GST_FLOW_ERROR); g_return_val_if_fail (gst_buffer_get_size (buffer) >= 4, GST_FLOW_ERROR);
data = gst_buffer_map (buffer, NULL, NULL, GST_MAP_READ);
tag_type = data[0]; tag_type = data[0];
@ -1521,6 +1554,8 @@ gst_flv_demux_parse_tag_type (GstFlvDemux * demux, GstBuffer * buffer)
GST_LOG_OBJECT (demux, "tag data size is %" G_GUINT64_FORMAT, GST_LOG_OBJECT (demux, "tag data size is %" G_GUINT64_FORMAT,
demux->tag_data_size); demux->tag_data_size);
gst_buffer_unmap (buffer, data, -1);
return ret; return ret;
} }
@ -1528,9 +1563,11 @@ static GstFlowReturn
gst_flv_demux_parse_header (GstFlvDemux * demux, GstBuffer * buffer) gst_flv_demux_parse_header (GstFlvDemux * demux, GstBuffer * buffer)
{ {
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
guint8 *data = GST_BUFFER_DATA (buffer); guint8 *data;
g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 9, GST_FLOW_ERROR); g_return_val_if_fail (gst_buffer_get_size (buffer) >= 9, GST_FLOW_ERROR);
data = gst_buffer_map (buffer, NULL, NULL, GST_MAP_READ);
/* Check for the FLV tag */ /* Check for the FLV tag */
if (data[0] == 'F' && data[1] == 'L' && data[2] == 'V') { if (data[0] == 'F' && data[1] == 'L' && data[2] == 'V') {
@ -1543,12 +1580,9 @@ gst_flv_demux_parse_header (GstFlvDemux * demux, GstBuffer * buffer)
} }
} }
/* Jump over the 4 first bytes */
data += 4;
/* Now look at audio/video flags */ /* Now look at audio/video flags */
{ {
guint8 flags = data[0]; guint8 flags = data[4];
demux->has_video = demux->has_audio = FALSE; demux->has_video = demux->has_audio = FALSE;
@ -1569,6 +1603,7 @@ gst_flv_demux_parse_header (GstFlvDemux * demux, GstBuffer * buffer)
demux->need_header = FALSE; demux->need_header = FALSE;
beach: beach:
gst_buffer_unmap (buffer, data, -1);
return ret; return ret;
} }
@ -1644,11 +1679,6 @@ gst_flv_demux_cleanup (GstFlvDemux * demux)
demux->new_seg_event = NULL; 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); gst_adapter_clear (demux->adapter);
if (demux->audio_codec_data) { if (demux->audio_codec_data) {
@ -1716,7 +1746,8 @@ gst_flv_demux_chain (GstPad * pad, GstBuffer * buffer)
demux = GST_FLV_DEMUX (gst_pad_get_parent (pad)); demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
GST_LOG_OBJECT (demux, "received buffer of %d bytes at offset %" GST_LOG_OBJECT (demux, "received buffer of %d bytes at offset %"
G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer)); G_GUINT64_FORMAT, gst_buffer_get_size (buffer),
GST_BUFFER_OFFSET (buffer));
if (G_UNLIKELY (GST_BUFFER_OFFSET (buffer) == 0)) { if (G_UNLIKELY (GST_BUFFER_OFFSET (buffer) == 0)) {
GST_DEBUG_OBJECT (demux, "beginning of file, expect header"); GST_DEBUG_OBJECT (demux, "beginning of file, expect header");
@ -1876,20 +1907,16 @@ parse:
if (!demux->indexed) { if (!demux->indexed) {
if (demux->offset == demux->file_size - sizeof (guint32)) { if (demux->offset == demux->file_size - sizeof (guint32)) {
GstBuffer *buffer =
gst_adapter_take_buffer (demux->adapter, sizeof (guint32));
GstByteReader *reader = gst_byte_reader_new_from_buffer (buffer);
guint64 seek_offset; guint64 seek_offset;
guint8 *data;
if (!gst_adapter_available (demux->adapter) >= sizeof (guint32)) { data = gst_adapter_take (demux->adapter, 4);
/* error */ if (!data)
} goto no_index;
seek_offset = seek_offset = demux->file_size - sizeof (guint32) -
demux->file_size - sizeof (guint32) - GST_READ_UINT32_BE (data);
gst_byte_reader_peek_uint32_be_unchecked (reader); g_free (data);
gst_byte_reader_free (reader);
gst_buffer_unref (buffer);
GST_INFO_OBJECT (demux, GST_INFO_OBJECT (demux,
"Seeking to beginning of last tag at %" G_GUINT64_FORMAT, "Seeking to beginning of last tag at %" G_GUINT64_FORMAT,
@ -1966,10 +1993,10 @@ gst_flv_demux_pull_range (GstFlvDemux * demux, GstPad * pad, guint64 offset,
return ret; return ret;
} }
if (G_UNLIKELY (*buffer && GST_BUFFER_SIZE (*buffer) != size)) { if (G_UNLIKELY (*buffer && gst_buffer_get_size (*buffer) != size)) {
GST_WARNING_OBJECT (demux, GST_WARNING_OBJECT (demux,
"partial pull got %d when expecting %d from offset %" G_GUINT64_FORMAT, "partial pull got %d when expecting %d from offset %" G_GUINT64_FORMAT,
GST_BUFFER_SIZE (*buffer), size, offset); gst_buffer_get_size (*buffer), size, offset);
gst_buffer_unref (*buffer); gst_buffer_unref (*buffer);
ret = GST_FLOW_UNEXPECTED; ret = GST_FLOW_UNEXPECTED;
*buffer = NULL; *buffer = NULL;
@ -2216,6 +2243,7 @@ gst_flv_demux_get_metadata (GstFlvDemux * demux)
GstFormat fmt = GST_FORMAT_BYTES; GstFormat fmt = GST_FORMAT_BYTES;
size_t tag_size, size; size_t tag_size, size;
GstBuffer *buffer = NULL; GstBuffer *buffer = NULL;
guint8 *data;
if (G_UNLIKELY (!gst_pad_query_peer_duration (demux->sinkpad, &fmt, &offset) if (G_UNLIKELY (!gst_pad_query_peer_duration (demux->sinkpad, &fmt, &offset)
|| fmt != GST_FORMAT_BYTES)) || fmt != GST_FORMAT_BYTES))
@ -2231,7 +2259,9 @@ gst_flv_demux_get_metadata (GstFlvDemux * demux)
4, &buffer)) 4, &buffer))
goto exit; goto exit;
tag_size = GST_READ_UINT32_BE (GST_BUFFER_DATA (buffer)); data = gst_buffer_map (buffer, NULL, NULL, GST_MAP_READ);
tag_size = GST_READ_UINT32_BE (data);
gst_buffer_unmap (buffer, data, -1);
GST_DEBUG_OBJECT (demux, "last tag size: %" G_GSIZE_FORMAT, tag_size); GST_DEBUG_OBJECT (demux, "last tag size: %" G_GSIZE_FORMAT, tag_size);
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
buffer = NULL; buffer = NULL;
@ -2242,8 +2272,10 @@ gst_flv_demux_get_metadata (GstFlvDemux * demux)
goto exit; goto exit;
/* a consistency check */ /* a consistency check */
size = GST_READ_UINT24_BE (GST_BUFFER_DATA (buffer) + 1); data = gst_buffer_map (buffer, NULL, NULL, GST_MAP_READ);
size = GST_READ_UINT24_BE (data + 1);
if (size != tag_size - 11) { if (size != tag_size - 11) {
gst_buffer_unmap (buffer, data, -1);
GST_DEBUG_OBJECT (demux, GST_DEBUG_OBJECT (demux,
"tag size %" G_GSIZE_FORMAT ", expected %" G_GSIZE_FORMAT "tag size %" G_GSIZE_FORMAT ", expected %" G_GSIZE_FORMAT
", corrupt or truncated file", size, tag_size - 11); ", corrupt or truncated file", size, tag_size - 11);
@ -2254,7 +2286,8 @@ gst_flv_demux_get_metadata (GstFlvDemux * demux)
gst_flv_demux_parse_tag_timestamp (demux, FALSE, buffer, &size); gst_flv_demux_parse_tag_timestamp (demux, FALSE, buffer, &size);
/* maybe get some more metadata */ /* maybe get some more metadata */
if (GST_BUFFER_DATA (buffer)[0] == 18) { if (data[0] == 18) {
gst_buffer_unmap (buffer, data, -1);
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
buffer = NULL; buffer = NULL;
GST_DEBUG_OBJECT (demux, "script tag, pulling it to parse"); GST_DEBUG_OBJECT (demux, "script tag, pulling it to parse");
@ -2262,6 +2295,8 @@ gst_flv_demux_get_metadata (GstFlvDemux * demux)
if (GST_FLOW_OK == gst_flv_demux_pull_range (demux, demux->sinkpad, offset, if (GST_FLOW_OK == gst_flv_demux_pull_range (demux, demux->sinkpad, offset,
tag_size, &buffer)) tag_size, &buffer))
gst_flv_demux_parse_tag_script (demux, buffer); gst_flv_demux_parse_tag_script (demux, buffer);
} else {
gst_buffer_unmap (buffer, data, -1);
} }
exit: exit:
@ -2319,13 +2354,13 @@ gst_flv_demux_loop (GstPad * pad)
if (demux->segment.rate < 0.0) { if (demux->segment.rate < 0.0) {
/* check end of section */ /* check end of section */
if ((gint64) demux->offset >= demux->to_offset || if ((gint64) demux->offset >= demux->to_offset ||
demux->segment.last_stop >= demux->segment.stop + 2 * GST_SECOND || demux->segment.position >= demux->segment.stop + 2 * GST_SECOND ||
(demux->audio_done && demux->video_done)) (demux->audio_done && demux->video_done))
ret = gst_flv_demux_seek_to_prev_keyframe (demux); ret = gst_flv_demux_seek_to_prev_keyframe (demux);
} else { } else {
/* check EOS condition */ /* check EOS condition */
if ((demux->segment.stop != -1) && if ((demux->segment.stop != -1) &&
(demux->segment.last_stop >= demux->segment.stop)) { (demux->segment.position >= demux->segment.stop)) {
ret = GST_FLOW_UNEXPECTED; ret = GST_FLOW_UNEXPECTED;
} }
} }
@ -2346,6 +2381,15 @@ pause:
gst_pad_pause_task (pad); gst_pad_pause_task (pad);
if (ret == GST_FLOW_UNEXPECTED) { if (ret == GST_FLOW_UNEXPECTED) {
/* handle end-of-stream/segment */
/* so align our position with the end of it, if there is one
* this ensures a subsequent will arrive at correct base/acc time */
if (demux->segment.rate > 0.0 &&
GST_CLOCK_TIME_IS_VALID (demux->segment.stop))
demux->segment.position = demux->segment.stop;
else if (demux->segment.rate < 0.0)
demux->segment.position = demux->segment.start;
/* perform EOS logic */ /* perform EOS logic */
if (!demux->no_more_pads) { if (!demux->no_more_pads) {
gst_element_no_more_pads (GST_ELEMENT_CAST (demux)); gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
@ -2403,7 +2447,7 @@ gst_flv_demux_find_offset (GstFlvDemux * demux, GstSegment * segment)
g_return_val_if_fail (segment != NULL, 0); g_return_val_if_fail (segment != NULL, 0);
time = segment->last_stop; time = segment->position;
if (demux->index) { if (demux->index) {
/* Let's check if we have an index entry for that seek time */ /* Let's check if we have an index entry for that seek time */
@ -2417,7 +2461,7 @@ gst_flv_demux_find_offset (GstFlvDemux * demux, GstSegment * segment)
GST_DEBUG_OBJECT (demux, "found index entry for %" GST_TIME_FORMAT GST_DEBUG_OBJECT (demux, "found index entry for %" GST_TIME_FORMAT
" at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT, " at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT,
GST_TIME_ARGS (segment->last_stop), GST_TIME_ARGS (time), bytes); GST_TIME_ARGS (segment->position), GST_TIME_ARGS (time), bytes);
/* Key frame seeking */ /* Key frame seeking */
if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) { if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
@ -2425,7 +2469,7 @@ gst_flv_demux_find_offset (GstFlvDemux * demux, GstSegment * segment)
if (time < segment->start) { if (time < segment->start) {
segment->start = segment->time = time; segment->start = segment->time = time;
} }
segment->last_stop = time; segment->position = time;
} }
} else { } else {
GST_DEBUG_OBJECT (demux, "no index entry found for %" GST_TIME_FORMAT, GST_DEBUG_OBJECT (demux, "no index entry found for %" GST_TIME_FORMAT,
@ -2463,13 +2507,13 @@ flv_demux_handle_seek_push (GstFlvDemux * demux, GstEvent * event)
&demux->segment); &demux->segment);
/* Apply the seek to our segment */ /* Apply the seek to our segment */
gst_segment_set_seek (&seeksegment, rate, format, flags, gst_segment_do_seek (&seeksegment, rate, format, flags,
start_type, start, stop_type, stop, &update); start_type, start, stop_type, stop, &update);
GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT, GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT,
&seeksegment); &seeksegment);
if (flush || seeksegment.last_stop != demux->segment.last_stop) { if (flush || seeksegment.position != demux->segment.position) {
/* Do the actual seeking */ /* Do the actual seeking */
guint64 offset = gst_flv_demux_find_offset (demux, &seeksegment); guint64 offset = gst_flv_demux_find_offset (demux, &seeksegment);
@ -2636,7 +2680,7 @@ gst_flv_demux_handle_seek_pull (GstFlvDemux * demux, GstEvent * event,
if (flush) { if (flush) {
/* Stop flushing upstream we need to pull */ /* Stop flushing upstream we need to pull */
gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ()); gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop (TRUE));
} }
/* Work on a copy until we are sure the seek succeeded. */ /* Work on a copy until we are sure the seek succeeded. */
@ -2646,29 +2690,29 @@ gst_flv_demux_handle_seek_pull (GstFlvDemux * demux, GstEvent * event,
&demux->segment); &demux->segment);
/* Apply the seek to our segment */ /* Apply the seek to our segment */
gst_segment_set_seek (&seeksegment, rate, format, flags, gst_segment_do_seek (&seeksegment, rate, format, flags,
start_type, start, stop_type, stop, &update); start_type, start, stop_type, stop, &update);
GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT, GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT,
&seeksegment); &seeksegment);
if (flush || seeksegment.last_stop != demux->segment.last_stop) { if (flush || seeksegment.position != demux->segment.position) {
/* Do the actual seeking */ /* Do the actual seeking */
/* index is reliable if it is complete or we do not go to far ahead */ /* index is reliable if it is complete or we do not go to far ahead */
if (seeking && !demux->indexed && if (seeking && !demux->indexed &&
seeksegment.last_stop > demux->index_max_time + 10 * GST_SECOND) { seeksegment.position > demux->index_max_time + 10 * GST_SECOND) {
GST_DEBUG_OBJECT (demux, "delaying seek to post-scan; " GST_DEBUG_OBJECT (demux, "delaying seek to post-scan; "
" index only up to %" GST_TIME_FORMAT, " index only up to %" GST_TIME_FORMAT,
GST_TIME_ARGS (demux->index_max_time)); GST_TIME_ARGS (demux->index_max_time));
/* stop flushing for now */ /* stop flushing for now */
if (flush) if (flush)
gst_flv_demux_push_src_event (demux, gst_event_new_flush_stop ()); gst_flv_demux_push_src_event (demux, gst_event_new_flush_stop (TRUE));
/* delegate scanning and index building to task thread to avoid /* delegate scanning and index building to task thread to avoid
* occupying main (UI) loop */ * occupying main (UI) loop */
if (demux->seek_event) if (demux->seek_event)
gst_event_unref (demux->seek_event); gst_event_unref (demux->seek_event);
demux->seek_event = gst_event_ref (event); demux->seek_event = gst_event_ref (event);
demux->seek_time = seeksegment.last_stop; demux->seek_time = seeksegment.position;
demux->state = FLV_STATE_SEEK; demux->state = FLV_STATE_SEEK;
/* do not know about succes yet, but we did care and handled it */ /* do not know about succes yet, but we did care and handled it */
ret = TRUE; ret = TRUE;
@ -2682,35 +2726,9 @@ gst_flv_demux_handle_seek_pull (GstFlvDemux * demux, GstEvent * event,
ret = TRUE; ret = TRUE;
} }
if (G_UNLIKELY (demux->close_seg_event)) {
gst_event_unref (demux->close_seg_event);
demux->close_seg_event = NULL;
}
if (flush) { if (flush) {
/* Stop flushing, the sinks are at time 0 now */ /* Stop flushing, the sinks are at time 0 now */
gst_flv_demux_push_src_event (demux, gst_event_new_flush_stop ()); gst_flv_demux_push_src_event (demux, gst_event_new_flush_stop (TRUE));
} 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) { if (ret) {
@ -2721,7 +2739,7 @@ gst_flv_demux_handle_seek_pull (GstFlvDemux * demux, GstEvent * event,
if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) { if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
gst_element_post_message (GST_ELEMENT (demux), gst_element_post_message (GST_ELEMENT (demux),
gst_message_new_segment_start (GST_OBJECT (demux), gst_message_new_segment_start (GST_OBJECT (demux),
demux->segment.format, demux->segment.last_stop)); demux->segment.format, demux->segment.position));
} }
/* Tell all the stream a new segment is needed */ /* Tell all the stream a new segment is needed */
@ -2740,10 +2758,7 @@ gst_flv_demux_handle_seek_pull (GstFlvDemux * demux, GstEvent * event,
GST_TIME_FORMAT " to %" GST_TIME_FORMAT, GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
GST_TIME_ARGS (demux->segment.start), GST_TIME_ARGS (demux->segment.start),
GST_TIME_ARGS (demux->segment.stop)); GST_TIME_ARGS (demux->segment.stop));
demux->new_seg_event = demux->new_seg_event = gst_event_new_segment (&demux->segment);
gst_event_new_new_segment (FALSE, demux->segment.rate,
demux->segment.format, demux->segment.start,
demux->segment.stop, demux->segment.start);
} }
} }
@ -2781,9 +2796,28 @@ wrong_format:
static gboolean static gboolean
gst_flv_demux_sink_activate (GstPad * sinkpad) gst_flv_demux_sink_activate (GstPad * sinkpad)
{ {
if (gst_pad_check_pull_range (sinkpad)) { GstQuery *query;
gboolean pull_mode;
query = gst_query_new_scheduling ();
if (!gst_pad_peer_query (sinkpad, query)) {
gst_query_unref (query);
goto activate_push;
}
gst_query_parse_scheduling (query, &pull_mode, NULL, NULL, NULL, NULL, NULL);
gst_query_unref (query);
if (!pull_mode)
goto activate_push;
GST_DEBUG_OBJECT (sinkpad, "activating pull");
return gst_pad_activate_pull (sinkpad, TRUE); return gst_pad_activate_pull (sinkpad, TRUE);
} else {
activate_push:
{
GST_DEBUG_OBJECT (sinkpad, "activating push");
return gst_pad_activate_push (sinkpad, TRUE); return gst_pad_activate_push (sinkpad, TRUE);
} }
} }
@ -2862,22 +2896,17 @@ gst_flv_demux_sink_event (GstPad * pad, GstEvent * event)
GST_WARNING_OBJECT (demux, "failed pushing EOS on streams"); GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
ret = TRUE; ret = TRUE;
break; break;
case GST_EVENT_NEWSEGMENT: case GST_EVENT_SEGMENT:
{ {
GstFormat format; GstSegment in_segment;
gdouble rate;
gint64 start, stop, time;
gboolean update;
GST_DEBUG_OBJECT (demux, "received new segment"); GST_DEBUG_OBJECT (demux, "received new segment");
gst_event_parse_new_segment (event, &update, &rate, &format, &start, gst_event_copy_segment (event, &in_segment);
&stop, &time);
if (format == GST_FORMAT_TIME) { if (in_segment.format == GST_FORMAT_TIME) {
/* time segment, this is perfect, copy over the values. */ /* time segment, this is perfect, copy over the values. */
gst_segment_set_newsegment (&demux->segment, update, rate, format, memcpy (&demux->segment, &in_segment, sizeof (in_segment));
start, stop, time);
GST_DEBUG_OBJECT (demux, "NEWSEGMENT: %" GST_SEGMENT_FORMAT, GST_DEBUG_OBJECT (demux, "NEWSEGMENT: %" GST_SEGMENT_FORMAT,
&demux->segment); &demux->segment);
@ -2976,9 +3005,9 @@ gst_flv_demux_query (GstPad * pad, GstQuery * query)
} }
GST_DEBUG_OBJECT (pad, "position query, replying %" GST_TIME_FORMAT, GST_DEBUG_OBJECT (pad, "position query, replying %" GST_TIME_FORMAT,
GST_TIME_ARGS (demux->segment.last_stop)); GST_TIME_ARGS (demux->segment.position));
gst_query_set_position (query, GST_FORMAT_TIME, demux->segment.last_stop); gst_query_set_position (query, GST_FORMAT_TIME, demux->segment.position);
break; break;
} }
@ -3152,11 +3181,6 @@ gst_flv_demux_dispose (GObject * object)
demux->new_seg_event = NULL; 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) { if (demux->audio_codec_data) {
gst_buffer_unref (demux->audio_codec_data); gst_buffer_unref (demux->audio_codec_data);
demux->audio_codec_data = NULL; demux->audio_codec_data = NULL;
@ -3195,23 +3219,6 @@ gst_flv_demux_dispose (GObject * object)
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); 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 static void
gst_flv_demux_class_init (GstFlvDemuxClass * klass) gst_flv_demux_class_init (GstFlvDemuxClass * klass)
{ {
@ -3224,10 +3231,21 @@ gst_flv_demux_class_init (GstFlvDemuxClass * klass)
GST_DEBUG_FUNCPTR (gst_flv_demux_change_state); GST_DEBUG_FUNCPTR (gst_flv_demux_change_state);
gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_flv_demux_set_index); gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_flv_demux_set_index);
gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_flv_demux_get_index); gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_flv_demux_get_index);
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&flv_sink_template));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&audio_src_template));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&video_src_template));
gst_element_class_set_details_simple (gstelement_class, "FLV Demuxer",
"Codec/Demuxer",
"Demux FLV feeds into digital streams",
"Julien Moutte <julien@moutte.net>");
} }
static void static void
gst_flv_demux_init (GstFlvDemux * demux, GstFlvDemuxClass * g_class) gst_flv_demux_init (GstFlvDemux * demux)
{ {
demux->sinkpad = demux->sinkpad =
gst_pad_new_from_static_template (&flv_sink_template, "sink"); gst_pad_new_from_static_template (&flv_sink_template, "sink");

View file

@ -71,7 +71,6 @@ struct _GstFlvDemux
GstSegment segment; GstSegment segment;
GstEvent *close_seg_event;
GstEvent *new_seg_event; GstEvent *new_seg_event;
GstTagList *taglist; GstTagList *taglist;

View file

@ -82,19 +82,9 @@ static GstStaticPadTemplate audiosink_templ = GST_STATIC_PAD_TEMPLATE ("audio",
"audio/x-speex, 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) \ #define gst_flv_mux_parent_class parent_class
G_STMT_START{ \ G_DEFINE_TYPE_WITH_CODE (GstFlvMux, gst_flv_mux, GST_TYPE_ELEMENT,
static const GInterfaceInfo tag_setter_info = { \ G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
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 void gst_flv_mux_finalize (GObject * object);
static GstFlowReturn static GstFlowReturn
@ -102,9 +92,12 @@ gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data);
static gboolean gst_flv_mux_handle_src_event (GstPad * pad, GstEvent * event); static gboolean gst_flv_mux_handle_src_event (GstPad * pad, GstEvent * event);
static GstPad *gst_flv_mux_request_new_pad (GstElement * element, static GstPad *gst_flv_mux_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name); GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps);
static void gst_flv_mux_release_pad (GstElement * element, GstPad * pad); static void gst_flv_mux_release_pad (GstElement * element, GstPad * pad);
static gboolean gst_flv_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps);
static gboolean gst_flv_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps);
static void gst_flv_mux_get_property (GObject * object, static void gst_flv_mux_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec); guint prop_id, GValue * value, GParamSpec * pspec);
static void gst_flv_mux_set_property (GObject * object, static void gst_flv_mux_set_property (GObject * object,
@ -127,23 +120,27 @@ gst_flv_mux_index_entry_free (GstFlvMuxIndexEntry * entry)
g_slice_free (GstFlvMuxIndexEntry, entry); g_slice_free (GstFlvMuxIndexEntry, entry);
} }
static void static GstBuffer *
gst_flv_mux_base_init (gpointer g_class) _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
{ {
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); GstBuffer *buf;
gst_element_class_add_pad_template (element_class, buf = gst_buffer_new ();
gst_static_pad_template_get (&videosink_templ)); gst_buffer_take_memory (buf, -1,
gst_element_class_add_pad_template (element_class, gst_memory_new_wrapped (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
gst_static_pad_template_get (&audiosink_templ)); mem, free_func, size, 0, size));
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"); return buf;
}
static void
_gst_buffer_new_and_alloc (gsize size, GstBuffer ** buffer, guint8 ** data)
{
g_return_if_fail (data != NULL);
g_return_if_fail (buffer != NULL);
*data = g_malloc (size);
*buffer = _gst_buffer_new_wrapped (*data, size, g_free);
} }
static void static void
@ -181,10 +178,23 @@ gst_flv_mux_class_init (GstFlvMuxClass * klass)
gstelement_class->request_new_pad = gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_flv_mux_request_new_pad); GST_DEBUG_FUNCPTR (gst_flv_mux_request_new_pad);
gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_flv_mux_release_pad); gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_flv_mux_release_pad);
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&videosink_templ));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&audiosink_templ));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&src_templ));
gst_element_class_set_details_simple (gstelement_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 static void
gst_flv_mux_init (GstFlvMux * mux, GstFlvMuxClass * g_class) gst_flv_mux_init (GstFlvMux * mux)
{ {
mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src"); 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_pad_set_event_function (mux->srcpad, gst_flv_mux_handle_src_event);
@ -264,6 +274,27 @@ gst_flv_mux_handle_sink_event (GstPad * pad, GstEvent * event)
gboolean ret = TRUE; gboolean ret = TRUE;
switch (GST_EVENT_TYPE (event)) { switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
{
GstCaps *caps;
GstFlvPad *flvpad;
gst_event_parse_caps (event, &caps);
/* find stream data */
flvpad = (GstFlvPad *) gst_pad_get_element_private (pad);
g_assert (flvpad);
if (flvpad->video) {
ret = gst_flv_mux_video_pad_setcaps (pad, caps);
} else {
ret = gst_flv_mux_audio_pad_setcaps (pad, caps);
}
/* and eat */
ret = FALSE;
gst_event_unref (event);
break;
}
case GST_EVENT_TAG:{ case GST_EVENT_TAG:{
GstTagList *list; GstTagList *list;
GstTagSetter *setter = GST_TAG_SETTER (mux); GstTagSetter *setter = GST_TAG_SETTER (mux);
@ -474,14 +505,13 @@ gst_flv_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
static GstPad * static GstPad *
gst_flv_mux_request_new_pad (GstElement * element, gst_flv_mux_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * pad_name) GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
{ {
GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
GstFlvMux *mux = GST_FLV_MUX (element); GstFlvMux *mux = GST_FLV_MUX (element);
GstFlvPad *cpad; GstFlvPad *cpad;
GstPad *pad = NULL; GstPad *pad = NULL;
const gchar *name = NULL; const gchar *name = NULL;
GstPadSetCapsFunction setcapsfunc = NULL;
gboolean video; gboolean video;
if (mux->state != GST_FLV_MUX_STATE_HEADER) { if (mux->state != GST_FLV_MUX_STATE_HEADER) {
@ -497,7 +527,6 @@ gst_flv_mux_request_new_pad (GstElement * element,
mux->have_audio = TRUE; mux->have_audio = TRUE;
name = "audio"; name = "audio";
video = FALSE; video = FALSE;
setcapsfunc = GST_DEBUG_FUNCPTR (gst_flv_mux_audio_pad_setcaps);
} else if (templ == gst_element_class_get_pad_template (klass, "video")) { } else if (templ == gst_element_class_get_pad_template (klass, "video")) {
if (mux->have_video) { if (mux->have_video) {
GST_WARNING_OBJECT (mux, "Already have a video pad"); GST_WARNING_OBJECT (mux, "Already have a video pad");
@ -506,7 +535,6 @@ gst_flv_mux_request_new_pad (GstElement * element,
mux->have_video = TRUE; mux->have_video = TRUE;
name = "video"; name = "video";
video = TRUE; video = TRUE;
setcapsfunc = GST_DEBUG_FUNCPTR (gst_flv_mux_video_pad_setcaps);
} else { } else {
GST_WARNING_OBJECT (mux, "Invalid template"); GST_WARNING_OBJECT (mux, "Invalid template");
return NULL; return NULL;
@ -537,7 +565,6 @@ gst_flv_mux_request_new_pad (GstElement * element,
gst_pad_set_event_function (pad, gst_pad_set_event_function (pad,
GST_DEBUG_FUNCPTR (gst_flv_mux_handle_sink_event)); GST_DEBUG_FUNCPTR (gst_flv_mux_handle_sink_event));
gst_pad_set_setcaps_function (pad, setcapsfunc);
gst_pad_set_active (pad, TRUE); gst_pad_set_active (pad, TRUE);
gst_element_add_pad (element, pad); gst_element_add_pad (element, pad);
@ -562,10 +589,9 @@ gst_flv_mux_release_pad (GstElement * element, GstPad * pad)
static GstFlowReturn static GstFlowReturn
gst_flv_mux_push (GstFlvMux * mux, GstBuffer * buffer) gst_flv_mux_push (GstFlvMux * mux, GstBuffer * buffer)
{ {
gst_buffer_set_caps (buffer, GST_PAD_CAPS (mux->srcpad));
/* pushing the buffer that rewrites the header will make it no longer be the /* pushing the buffer that rewrites the header will make it no longer be the
* total output size in bytes, but it doesn't matter at that point */ * total output size in bytes, but it doesn't matter at that point */
mux->byte_count += GST_BUFFER_SIZE (buffer); mux->byte_count += gst_buffer_get_size (buffer);
return gst_pad_push (mux->srcpad, buffer); return gst_pad_push (mux->srcpad, buffer);
} }
@ -576,8 +602,7 @@ gst_flv_mux_create_header (GstFlvMux * mux)
GstBuffer *header; GstBuffer *header;
guint8 *data; guint8 *data;
header = gst_buffer_new_and_alloc (9 + 4); _gst_buffer_new_and_alloc (9 + 4, &header, &data);
data = GST_BUFFER_DATA (header);
data[0] = 'F'; data[0] = 'F';
data[1] = 'L'; data[1] = 'L';
@ -609,8 +634,7 @@ gst_flv_mux_preallocate_index (GstFlvMux * mux)
GST_DEBUG_OBJECT (mux, "preallocating %d bytes for the index", GST_DEBUG_OBJECT (mux, "preallocating %d bytes for the index",
preallocate_size); preallocate_size);
tmp = gst_buffer_new_and_alloc (preallocate_size); _gst_buffer_new_and_alloc (preallocate_size, &tmp, &data);
data = GST_BUFFER_DATA (tmp);
/* prefill the space with a gstfiller: <spaces> script tag variable */ /* prefill the space with a gstfiller: <spaces> script tag variable */
GST_WRITE_UINT16_BE (data, 9); /* 9 characters */ GST_WRITE_UINT16_BE (data, 9); /* 9 characters */
@ -624,8 +648,10 @@ gst_flv_mux_preallocate_index (GstFlvMux * mux)
static GstBuffer * static GstBuffer *
gst_flv_mux_create_number_script_value (const gchar * name, gdouble value) gst_flv_mux_create_number_script_value (const gchar * name, gdouble value)
{ {
GstBuffer *tmp = gst_buffer_new_and_alloc (2 + strlen (name) + 1 + 8); GstBuffer *tmp;
guint8 *data = GST_BUFFER_DATA (tmp); guint8 *data;
_gst_buffer_new_and_alloc (2 + strlen (name) + 1 + 8, &tmp, &data);
GST_WRITE_UINT16_BE (data, strlen (name)); /* name length */ GST_WRITE_UINT16_BE (data, strlen (name)); /* name length */
memcpy (&data[2], name, strlen (name)); memcpy (&data[2], name, strlen (name));
@ -647,8 +673,9 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
GST_DEBUG_OBJECT (mux, "tags = %" GST_PTR_FORMAT, tags); GST_DEBUG_OBJECT (mux, "tags = %" GST_PTR_FORMAT, tags);
script_tag = gst_buffer_new_and_alloc (11); /* FIXME perhaps some bytewriter'ing here ... */
data = GST_BUFFER_DATA (script_tag);
_gst_buffer_new_and_alloc (11, &script_tag, &data);
data[0] = 18; data[0] = 18;
@ -663,8 +690,7 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
/* Stream ID */ /* Stream ID */
data[8] = data[9] = data[10] = 0; data[8] = data[9] = data[10] = 0;
tmp = gst_buffer_new_and_alloc (13); _gst_buffer_new_and_alloc (13, &tmp, &data);
data = GST_BUFFER_DATA (tmp);
data[0] = 2; /* string */ data[0] = 2; /* string */
data[1] = 0; data[1] = 0;
data[2] = 10; /* length 10 */ data[2] = 10; /* length 10 */
@ -673,8 +699,7 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
script_tag = gst_buffer_join (script_tag, tmp); script_tag = gst_buffer_join (script_tag, tmp);
n_tags = (tags) ? gst_structure_n_fields ((GstStructure *) tags) : 0; n_tags = (tags) ? gst_structure_n_fields ((GstStructure *) tags) : 0;
tmp = gst_buffer_new_and_alloc (5); _gst_buffer_new_and_alloc (5, &tmp, &data);
data = GST_BUFFER_DATA (tmp);
data[0] = 8; /* ECMA array */ data[0] = 8; /* ECMA array */
GST_WRITE_UINT32_BE (data + 1, n_tags); GST_WRITE_UINT32_BE (data + 1, n_tags);
script_tag = gst_buffer_join (script_tag, tmp); script_tag = gst_buffer_join (script_tag, tmp);
@ -724,8 +749,8 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
if (!gst_tag_list_get_string (tags, tag_name, &s)) if (!gst_tag_list_get_string (tags, tag_name, &s))
continue; continue;
tmp = gst_buffer_new_and_alloc (2 + strlen (t) + 1 + 2 + strlen (s)); _gst_buffer_new_and_alloc (2 + strlen (t) + 1 + 2 + strlen (s),
data = GST_BUFFER_DATA (tmp); &tmp, &data);
data[0] = 0; /* tag name length */ data[0] = 0; /* tag name length */
data[1] = strlen (t); data[1] = strlen (t);
memcpy (&data[2], t, strlen (t)); memcpy (&data[2], t, strlen (t));
@ -767,8 +792,9 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
d /= (gdouble) GST_SECOND; d /= (gdouble) GST_SECOND;
GST_DEBUG_OBJECT (mux, "determined the duration to be %f", d); GST_DEBUG_OBJECT (mux, "determined the duration to be %f", d);
data = GST_BUFFER_DATA (script_tag); data = gst_buffer_map (script_tag, NULL, NULL, GST_MAP_WRITE);
GST_WRITE_DOUBLE_BE (data + 29 + 2 + 8 + 1, d); GST_WRITE_DOUBLE_BE (data + 29 + 2 + 8 + 1, d);
gst_buffer_unmap (script_tag, data, -1);
} }
if (mux->have_video) { if (mux->have_video) {
@ -784,8 +810,9 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
} }
} }
if (video_pad && GST_PAD_CAPS (video_pad)) { if (video_pad && gst_pad_has_current_caps (video_pad)) {
GstStructure *s = gst_caps_get_structure (GST_PAD_CAPS (video_pad), 0); GstCaps *caps;
GstStructure *s;
gint size; gint size;
gint num, den; gint num, den;
@ -797,6 +824,10 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
script_tag = gst_buffer_join (script_tag, tmp); script_tag = gst_buffer_join (script_tag, tmp);
tags_written++; tags_written++;
caps = gst_pad_get_current_caps (video_pad);
s = gst_caps_get_structure (caps, 0);
gst_caps_unref (caps);
if (gst_structure_get_int (s, "width", &size)) { if (gst_structure_get_int (s, "width", &size)) {
GST_DEBUG_OBJECT (mux, "putting width %d in the metadata", size); GST_DEBUG_OBJECT (mux, "putting width %d in the metadata", size);
@ -871,8 +902,7 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
{ {
const gchar *s = "GStreamer FLV muxer"; const gchar *s = "GStreamer FLV muxer";
tmp = gst_buffer_new_and_alloc (2 + 15 + 1 + 2 + strlen (s)); _gst_buffer_new_and_alloc (2 + 15 + 1 + 2 + strlen (s), &tmp, &data);
data = GST_BUFFER_DATA (tmp);
data[0] = 0; /* 15 bytes name */ data[0] = 0; /* 15 bytes name */
data[1] = 15; data[1] = 15;
memcpy (&data[2], "metadatacreator", 15); memcpy (&data[2], "metadatacreator", 15);
@ -906,8 +936,7 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
months[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, months[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
tm->tm_year + 1900); tm->tm_year + 1900);
tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 2 + strlen (s)); _gst_buffer_new_and_alloc (2 + 12 + 1 + 2 + strlen (s), &tmp, &data);
data = GST_BUFFER_DATA (tmp);
data[0] = 0; /* 12 bytes name */ data[0] = 0; /* 12 bytes name */
data[1] = 12; data[1] = 12;
memcpy (&data[2], "creationdate", 12); memcpy (&data[2], "creationdate", 12);
@ -921,25 +950,24 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
tags_written++; tags_written++;
} }
tmp = gst_buffer_new_and_alloc (2 + 0 + 1); _gst_buffer_new_and_alloc (2 + 0 + 1, &tmp, &data);
data = GST_BUFFER_DATA (tmp);
data[0] = 0; /* 0 byte size */ data[0] = 0; /* 0 byte size */
data[1] = 0; data[1] = 0;
data[2] = 9; /* end marker */ data[2] = 9; /* end marker */
script_tag = gst_buffer_join (script_tag, tmp); script_tag = gst_buffer_join (script_tag, tmp);
tags_written++; tags_written++;
tmp = gst_buffer_new_and_alloc (4); _gst_buffer_new_and_alloc (4, &tmp, &data);
data = GST_BUFFER_DATA (tmp); GST_WRITE_UINT32_BE (data, gst_buffer_get_size (script_tag));
GST_WRITE_UINT32_BE (data, GST_BUFFER_SIZE (script_tag));
script_tag = gst_buffer_join (script_tag, tmp); script_tag = gst_buffer_join (script_tag, tmp);
data = GST_BUFFER_DATA (script_tag); data = gst_buffer_map (script_tag, NULL, NULL, GST_MAP_WRITE);
data[1] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 16) & 0xff; data[1] = ((gst_buffer_get_size (script_tag) - 11 - 4) >> 16) & 0xff;
data[2] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 8) & 0xff; data[2] = ((gst_buffer_get_size (script_tag) - 11 - 4) >> 8) & 0xff;
data[3] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 0) & 0xff; data[3] = ((gst_buffer_get_size (script_tag) - 11 - 4) >> 0) & 0xff;
GST_WRITE_UINT32_BE (data + 11 + 13 + 1, tags_written); GST_WRITE_UINT32_BE (data + 11 + 13 + 1, tags_written);
gst_buffer_unmap (script_tag, data, -1);
return script_tag; return script_tag;
} }
@ -954,26 +982,29 @@ gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
guint32 timestamp = guint32 timestamp =
(GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) ? GST_BUFFER_TIMESTAMP (buffer) / (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) ? GST_BUFFER_TIMESTAMP (buffer) /
GST_MSECOND : cpad->last_timestamp / GST_MSECOND; GST_MSECOND : cpad->last_timestamp / GST_MSECOND;
guint8 *bdata;
gsize bsize;
bdata = gst_buffer_map (buffer, &bsize, NULL, GST_MAP_READ);
size = 11; size = 11;
if (cpad->video) { if (cpad->video) {
size += 1; size += 1;
if (cpad->video_codec == 7) if (cpad->video_codec == 7)
size += 4 + GST_BUFFER_SIZE (buffer); size += 4 + bsize;
else else
size += GST_BUFFER_SIZE (buffer); size += bsize;
} else { } else {
size += 1; size += 1;
if (cpad->audio_codec == 10) if (cpad->audio_codec == 10)
size += 1 + GST_BUFFER_SIZE (buffer); size += 1 + bsize;
else else
size += GST_BUFFER_SIZE (buffer); size += bsize;
} }
size += 4; size += 4;
tag = gst_buffer_new_and_alloc (size); _gst_buffer_new_and_alloc (size, &tag, &data);
GST_BUFFER_TIMESTAMP (tag) = timestamp * GST_MSECOND; GST_BUFFER_TIMESTAMP (tag) = timestamp * GST_MSECOND;
data = GST_BUFFER_DATA (tag);
memset (data, 0, size); memset (data, 0, size);
data[0] = (cpad->video) ? 9 : 8; data[0] = (cpad->video) ? 9 : 8;
@ -1005,11 +1036,9 @@ gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
/* FIXME: what to do about composition time */ /* FIXME: what to do about composition time */
data[13] = data[14] = data[15] = 0; data[13] = data[14] = data[15] = 0;
memcpy (data + 11 + 1 + 4, GST_BUFFER_DATA (buffer), memcpy (data + 11 + 1 + 4, bdata, bsize);
GST_BUFFER_SIZE (buffer));
} else { } else {
memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer), memcpy (data + 11 + 1, bdata, bsize);
GST_BUFFER_SIZE (buffer));
} }
} else { } else {
data[11] |= (cpad->audio_codec << 4) & 0xf0; data[11] |= (cpad->audio_codec << 4) & 0xf0;
@ -1020,17 +1049,21 @@ gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
if (cpad->audio_codec == 10) { if (cpad->audio_codec == 10) {
data[12] = is_codec_data ? 0 : 1; data[12] = is_codec_data ? 0 : 1;
memcpy (data + 11 + 1 + 1, GST_BUFFER_DATA (buffer), memcpy (data + 11 + 1 + 1, bdata, bsize);
GST_BUFFER_SIZE (buffer));
} else { } else {
memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer), memcpy (data + 11 + 1, bdata, bsize);
GST_BUFFER_SIZE (buffer));
} }
} }
gst_buffer_unmap (buffer, bdata, -1);
GST_WRITE_UINT32_BE (data + size - 4, size - 4); GST_WRITE_UINT32_BE (data + size - 4, size - 4);
gst_buffer_copy_metadata (tag, buffer, GST_BUFFER_COPY_TIMESTAMPS); GST_BUFFER_TIMESTAMP (tag) = GST_BUFFER_TIMESTAMP (buffer);
GST_BUFFER_DURATION (tag) = GST_BUFFER_DURATION (buffer);
GST_BUFFER_OFFSET (tag) = GST_BUFFER_OFFSET (buffer);
GST_BUFFER_OFFSET_END (tag) = GST_BUFFER_OFFSET_END (buffer);
/* mark the buffer if it's an audio buffer and there's also video being muxed /* mark the buffer if it's an audio buffer and there's also video being muxed
* or it's a video interframe */ * or it's a video interframe */
if ((mux->have_video && !cpad->video) || if ((mux->have_video && !cpad->video) ||
@ -1140,7 +1173,7 @@ gst_flv_mux_write_header (GstFlvMux * mux)
gst_structure_set_value (structure, "streamheader", &streamheader); gst_structure_set_value (structure, "streamheader", &streamheader);
g_value_unset (&streamheader); g_value_unset (&streamheader);
if (GST_PAD_CAPS (mux->srcpad) == NULL) if (!gst_pad_has_current_caps (mux->srcpad))
gst_pad_set_caps (mux->srcpad, caps); gst_pad_set_caps (mux->srcpad, caps);
gst_caps_unref (caps); gst_caps_unref (caps);
@ -1196,7 +1229,7 @@ gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvPad * cpad)
GstFlowReturn ret; GstFlowReturn ret;
/* arrange downstream running time */ /* arrange downstream running time */
buffer = gst_buffer_make_metadata_writable (buffer); buffer = gst_buffer_make_writable (buffer);
GST_BUFFER_TIMESTAMP (buffer) = GST_BUFFER_TIMESTAMP (buffer) =
gst_segment_to_running_time (&cpad->collect.segment, gst_segment_to_running_time (&cpad->collect.segment,
GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buffer)); GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buffer));
@ -1255,13 +1288,15 @@ gst_flv_mux_rewrite_header (GstFlvMux * mux)
GList *l; GList *l;
guint32 index_len, allocate_size; guint32 index_len, allocate_size;
guint32 i, index_skip; guint32 i, index_skip;
GstSegment segment;
if (mux->streamable) if (mux->streamable)
return GST_FLOW_OK; return GST_FLOW_OK;
/* seek back to the preallocated index space */ /* seek back to the preallocated index space */
event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, gst_segment_init (&segment, GST_FORMAT_BYTES);
13 + 29, GST_CLOCK_TIME_NONE, 13 + 29); segment.start = segment.time = 13 + 29;
event = gst_event_new_segment (&segment);
if (!gst_pad_push_event (mux->srcpad, event)) { if (!gst_pad_push_event (mux->srcpad, event)) {
GST_WARNING_OBJECT (mux, "Seek to rewrite header failed"); GST_WARNING_OBJECT (mux, "Seek to rewrite header failed");
return GST_FLOW_OK; return GST_FLOW_OK;
@ -1308,8 +1343,7 @@ gst_flv_mux_rewrite_header (GstFlvMux * mux)
/* see size calculation in gst_flv_mux_preallocate_index */ /* see size calculation in gst_flv_mux_preallocate_index */
allocate_size = 11 + 8 + 22 + 10 + index_len * 18; allocate_size = 11 + 8 + 22 + 10 + index_len * 18;
GST_DEBUG_OBJECT (mux, "Allocating %d bytes for index", allocate_size); GST_DEBUG_OBJECT (mux, "Allocating %d bytes for index", allocate_size);
index = gst_buffer_new_and_alloc (allocate_size); _gst_buffer_new_and_alloc (allocate_size, &index, &data);
data = GST_BUFFER_DATA (index);
GST_WRITE_UINT16_BE (data, 9); /* the 'keyframes' key */ GST_WRITE_UINT16_BE (data, 9); /* the 'keyframes' key */
memcpy (data + 2, "keyframes", 9); memcpy (data + 2, "keyframes", 9);
@ -1358,8 +1392,7 @@ gst_flv_mux_rewrite_header (GstFlvMux * mux)
guint8 *data; guint8 *data;
guint32 remaining_filler_size; guint32 remaining_filler_size;
tmp = gst_buffer_new_and_alloc (14); _gst_buffer_new_and_alloc (14, &tmp, &data);
data = GST_BUFFER_DATA (tmp);
GST_WRITE_UINT16_BE (data, 9); GST_WRITE_UINT16_BE (data, 9);
memcpy (data + 2, "gstfiller", 9); memcpy (data + 2, "gstfiller", 9);
GST_WRITE_UINT8 (data + 11, 2); /* string */ GST_WRITE_UINT8 (data + 11, 2); /* string */
@ -1376,7 +1409,6 @@ gst_flv_mux_rewrite_header (GstFlvMux * mux)
rewrite = gst_buffer_join (rewrite, index); rewrite = gst_buffer_join (rewrite, index);
gst_buffer_set_caps (rewrite, GST_PAD_CAPS (mux->srcpad));
return gst_flv_mux_push (mux, rewrite); return gst_flv_mux_push (mux, rewrite);
} }
@ -1391,14 +1423,16 @@ gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data)
gboolean eos = TRUE; gboolean eos = TRUE;
if (mux->state == GST_FLV_MUX_STATE_HEADER) { if (mux->state == GST_FLV_MUX_STATE_HEADER) {
GstSegment segment;
if (mux->collect->data == NULL) { if (mux->collect->data == NULL) {
GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL), GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
("No input streams configured")); ("No input streams configured"));
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
if (gst_pad_push_event (mux->srcpad, gst_segment_init (&segment, GST_FORMAT_BYTES);
gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0))) if (gst_pad_push_event (mux->srcpad, gst_event_new_segment (&segment)))
ret = gst_flv_mux_write_header (mux); ret = gst_flv_mux_write_header (mux);
else else
ret = GST_FLOW_ERROR; ret = GST_FLOW_ERROR;