index 8b01b0ba34cd424865d3b224a441cda8dcf26e25..4eb4d06f421826e5a7d8d7fbf4353cfff7698c51 100644 (file)
--- a/gst/asfdemux/asfpacket.c
+++ b/gst/asfdemux/asfpacket.c
/* we are unlikely to deal with lengths > 2GB here any time soon, so just
* return a signed int and use that for error reporting */
-static gint
+static inline gint
asf_packet_read_varlen_int (guint lentype_flags, guint lentype_bit_offset,
const guint8 ** p_data, guint * p_size)
{
- const guint lens[4] = { 0, 1, 2, 4 };
+ static const guint lens[4] = { 0, 1, 2, 4 };
guint len, val;
len = lens[(lentype_flags >> lentype_bit_offset) & 0x03];
/* will make caller bail out with a short read if there's not enough data */
- if (*p_size < len) {
+ if (G_UNLIKELY (*p_size < len)) {
GST_WARNING ("need %u bytes, but only %u bytes available", len, *p_size);
return -1;
}
{
AsfPayload *ret;
- if (stream->payloads->len == 0) {
+ if (G_UNLIKELY (stream->payloads->len == 0)) {
GST_DEBUG ("No previous fragments to merge with for stream %u", stream->id);
return NULL;
}
ret =
&g_array_index (stream->payloads, AsfPayload, stream->payloads->len - 1);
- if (ret->mo_size != payload->mo_size ||
- ret->mo_number != payload->mo_number || ret->mo_offset != 0) {
- GST_WARNING ("Previous fragment does not match continued fragment");
- return NULL;
+ if (G_UNLIKELY (ret->mo_size != payload->mo_size ||
+ ret->mo_number != payload->mo_number || ret->mo_offset != 0)) {
+ if (payload->mo_size != 0) {
+ GST_WARNING ("Previous fragment does not match continued fragment");
+ return NULL;
+ } else {
+ /* Warn about this case, but accept it anyway: files in the wild sometimes
+ * have continued packets where the subsequent fragments say that they're
+ * zero-sized. */
+ GST_WARNING ("Previous fragment found, but current fragment has "
+ "zero size, accepting anyway");
+ }
}
#if 0
if (this_fragment->mo_offset + this_payload_len > first_fragment->mo_size) {
gst_asf_payload_queue_for_stream (GstASFDemux * demux, AsfPayload * payload,
AsfStream * stream)
{
+ GST_DEBUG_OBJECT (demux, "Got payload for stream %d ts:%" GST_TIME_FORMAT,
+ stream->id, GST_TIME_ARGS (payload->ts));
+ /* remember the first timestamp in the stream */
+ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (demux->first_ts) &&
+ GST_CLOCK_TIME_IS_VALID (payload->ts))) {
+ GST_DEBUG_OBJECT (demux, "first ts: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (payload->ts));
+ demux->first_ts = payload->ts;
+ }
+
+ /* make timestamps start from 0 */
+ if (G_LIKELY (demux->first_ts < payload->ts))
+ payload->ts -= demux->first_ts;
+ else
+ payload->ts = 0;
+
/* remove any incomplete payloads that will never be completed */
while (stream->payloads->len > 0) {
AsfPayload *prev;
idx_last = stream->payloads->len - 1;
prev = &g_array_index (stream->payloads, AsfPayload, idx_last);
- if (gst_asf_payload_is_complete (prev))
+ if (G_UNLIKELY (gst_asf_payload_is_complete (prev)))
break;
GST_DEBUG_OBJECT (demux, "Dropping incomplete fragmented media object "
* absolutely necessary after a seek (we don't push out payloads that are
* before the segment start until we have at least one that falls within the
* segment) */
- if (GST_CLOCK_TIME_IS_VALID (payload->ts) &&
- payload->ts < demux->segment.start && payload->keyframe) {
+ if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (payload->ts) &&
+ payload->ts < demux->segment.start && payload->keyframe)) {
GST_DEBUG_OBJECT (demux, "Queueing keyframe before segment start, removing"
" %u previously-queued payloads, which would be out of segment too and"
" hence don't have to be decoded", stream->payloads->len);
GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT);
}
+ /* remember the first queued timestamp for the segment */
+ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (demux->segment_ts) &&
+ GST_CLOCK_TIME_IS_VALID (payload->ts))) {
+ GST_DEBUG_OBJECT (demux, "segment ts: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (payload->ts));
+ demux->segment_ts = payload->ts;
+ /* always note, but only determines segment when streaming */
+ if (demux->streaming)
+ gst_segment_set_seek (&demux->segment, demux->in_segment.rate,
+ GST_FORMAT_TIME, demux->segment.flags, GST_SEEK_TYPE_SET,
+ demux->segment_ts, GST_SEEK_TYPE_NONE, 0, NULL);
+ }
+
g_array_append_vals (stream->payloads, payload, 1);
}
off = 8;
for (ext = stream->ext_props.payload_extensions; ext->len > 0; ++ext) {
- if (off + ext->len > payload->rep_data_len) {
+ if (G_UNLIKELY (off + ext->len > payload->rep_data_len)) {
GST_WARNING ("not enough replicated data for defined extensions");
return;
}
switch (ext->id) {
case ASF_PAYLOAD_EXTENSION_DURATION:
- if (ext->len == 2) {
- payload->duration =
- GST_READ_UINT16_LE (payload->rep_data + off) * GST_MSECOND;
+ if (G_LIKELY (ext->len == 2)) {
+ guint16 tdur = GST_READ_UINT16_LE (payload->rep_data + off);
+ /* packet durations of 1ms are mostly invalid */
+ if (tdur != 1)
+ payload->duration = tdur * GST_MSECOND;
} else {
GST_WARNING ("unexpected DURATION extensions len %u", ext->len);
}
break;
+ case ASF_PAYLOAD_EXTENSION_SYSTEM_CONTENT:
+ if (G_LIKELY (ext->len == 1)) {
+ guint8 data = payload->rep_data[off];
+
+ payload->interlaced = data & 0x1;
+ payload->rff = data & 0x8;
+ payload->tff = (data & 0x2) || !(data & 0x4);
+ GST_DEBUG ("SYSTEM_CONTENT: interlaced:%d, rff:%d, tff:%d",
+ payload->interlaced, payload->rff, payload->tff);
+ } else {
+ GST_WARNING ("unexpected SYSTEM_CONTE extensions len %u", ext->len);
+ }
+ break;
+ case ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO:
+ if (G_LIKELY (ext->len == 2)) {
+ payload->par_x = payload->rep_data[off];
+ payload->par_y = payload->rep_data[off + 1];
+ GST_DEBUG ("PAR %d / %d", payload->par_x, payload->par_y);
+ } else {
+ GST_WARNING ("unexpected SYSTEM_PIXEL_ASPECT_RATIO extensions len %u",
+ ext->len);
+ }
+ break;
default:
+ GST_WARNING ("UNKNOWN PAYLOAD EXTENSION !");
break;
}
off += ext->len;
guint payload_len;
guint stream_num;
- if (*p_size < 1) {
+ if (G_UNLIKELY (*p_size < 1)) {
GST_WARNING_OBJECT (demux, "Short packet!");
return FALSE;
}
payload.ts = GST_CLOCK_TIME_NONE;
payload.duration = GST_CLOCK_TIME_NONE;
+ payload.par_x = 0;
+ payload.par_y = 0;
+ payload.interlaced = FALSE;
+ payload.tff = FALSE;
+ payload.rff = FALSE;
payload.mo_number =
asf_packet_read_varlen_int (packet->prop_flags, 4, p_data, p_size);
GST_LOG_OBJECT (demux, "keyframe : %s", (payload.keyframe) ? "yes" : "no");
GST_LOG_OBJECT (demux, "compressed : %s", (is_compressed) ? "yes" : "no");
- if (*p_size < payload.rep_data_len) {
+ if (G_UNLIKELY (*p_size < payload.rep_data_len)) {
GST_WARNING_OBJECT (demux, "Short packet! rep_data_len=%u, size=%u",
payload.rep_data_len, *p_size);
return FALSE;
*p_data += payload.rep_data_len;
*p_size -= payload.rep_data_len;
- if (*p_size == 0) {
+ if (G_UNLIKELY (*p_size == 0)) {
GST_WARNING_OBJECT (demux, "payload without data!?");
return FALSE;
}
/* we use -1 as lentype for a single payload that's the size of the packet */
- if (lentype >= 0 && lentype <= 3) {
+ if (G_UNLIKELY ((lentype >= 0 && lentype <= 3))) {
payload_len = asf_packet_read_varlen_int (lentype, 0, p_data, p_size);
if (*p_size < payload_len) {
GST_WARNING_OBJECT (demux, "Short packet! payload_len=%u, size=%u",
stream = gst_asf_demux_get_stream (demux, stream_num);
- if (stream == NULL) {
+ if (G_UNLIKELY (stream == NULL)) {
GST_WARNING_OBJECT (demux, "Payload for unknown stream %u, skipping",
stream_num);
- *p_data += payload_len;
- *p_size -= payload_len;
+ if (*p_size < payload_len) {
+ *p_data += *p_size;
+ *p_size = 0;
+ } else {
+ *p_data += payload_len;
+ *p_size -= payload_len;
+ }
return TRUE;
}
- if (!is_compressed) {
+ if (G_UNLIKELY (!is_compressed)) {
GST_LOG_OBJECT (demux, "replicated data length: %u", payload.rep_data_len);
if (payload.rep_data_len >= 8) {
payload.mo_size = GST_READ_UINT32_LE (payload.rep_data);
payload.ts = GST_READ_UINT32_LE (payload.rep_data + 4) * GST_MSECOND;
- payload.ts -= demux->preroll * GST_MSECOND;
+ if (G_UNLIKELY (payload.ts < demux->preroll))
+ payload.ts = 0;
+ else
+ payload.ts -= demux->preroll;
asf_payload_parse_replicated_data_extensions (stream, &payload);
GST_LOG_OBJECT (demux, "media object size : %u", payload.mo_size);
GST_TIME_ARGS (payload.duration));
} else if (payload.rep_data_len != 0) {
GST_WARNING_OBJECT (demux, "invalid replicated data length, very bad");
+ *p_data += payload_len;
+ *p_size -= payload_len;
+ return FALSE;
}
GST_LOG_OBJECT (demux, "media object offset : %u", payload.mo_offset);
GST_LOG_OBJECT (demux, "payload length: %u", payload_len);
- if ((stream = gst_asf_demux_get_stream (demux, stream_num))) {
+ if (payload_len == 0) {
+ GST_DEBUG_OBJECT (demux, "skipping empty payload");
+ } else if (payload.mo_offset == 0 && payload.mo_size == payload_len) {
+ /* if the media object is not fragmented, just create a sub-buffer */
+ GST_LOG_OBJECT (demux, "unfragmented media object size %u", payload_len);
payload.buf = asf_packet_create_payload_buffer (packet, p_data, p_size,
payload_len);
+ payload.buf_filled = payload_len;
+ gst_asf_payload_queue_for_stream (demux, &payload, stream);
+ } else {
+ const guint8 *payload_data = *p_data;
+
+ g_assert (payload_len <= *p_size);
+
+ *p_data += payload_len;
+ *p_size -= payload_len;
/* n-th fragment of a fragmented media object? */
if (payload.mo_offset != 0) {
AsfPayload *prev;
if ((prev = asf_payload_find_previous_fragment (&payload, stream))) {
- if (payload.mo_offset != GST_BUFFER_SIZE (prev->buf)) {
+ if (prev->buf == NULL || payload.mo_size != prev->mo_size ||
+ payload.mo_offset >= GST_BUFFER_SIZE (prev->buf) ||
+ payload.mo_offset + payload_len > GST_BUFFER_SIZE (prev->buf)) {
GST_WARNING_OBJECT (demux, "Offset doesn't match previous data?!");
+ } else {
+ /* we assume fragments are payloaded with increasing mo_offset */
+ if (payload.mo_offset != prev->buf_filled) {
+ GST_WARNING_OBJECT (demux, "media object payload discontinuity: "
+ "offset=%u vs buf_filled=%u", payload.mo_offset,
+ prev->buf_filled);
+ }
+ memcpy (GST_BUFFER_DATA (prev->buf) + payload.mo_offset,
+ payload_data, payload_len);
+ prev->buf_filled =
+ MAX (prev->buf_filled, payload.mo_offset + payload_len);
+ GST_LOG_OBJECT (demux, "Merged media object fragments, size now %u",
+ prev->buf_filled);
}
- /* note: buffer join/merge might not preserve buffer flags */
- prev->buf = gst_buffer_join (prev->buf, payload.buf);
- GST_LOG_OBJECT (demux, "Merged fragments, merged size: %u",
- GST_BUFFER_SIZE (prev->buf));
} else {
- gst_buffer_unref (payload.buf);
+ GST_DEBUG_OBJECT (demux, "n-th payload fragment, but don't have "
+ "any previous fragment, ignoring payload");
}
- payload.buf = NULL;
} else {
+ GST_LOG_OBJECT (demux, "allocating buffer of size %u for fragmented "
+ "media object", payload.mo_size);
+ payload.buf = gst_buffer_new_and_alloc (payload.mo_size);
+ memcpy (GST_BUFFER_DATA (payload.buf), payload_data, payload_len);
+ payload.buf_filled = payload_len;
+
gst_asf_payload_queue_for_stream (demux, &payload, stream);
}
}
*p_data += payload_len;
*p_size -= payload_len;
- ts = (payload.mo_offset - demux->preroll) * GST_MSECOND;
+ ts = payload.mo_offset * GST_MSECOND;
+ if (G_UNLIKELY (ts < demux->preroll))
+ ts = 0;
+ else
+ ts -= demux->preroll;
ts_delta = payload.rep_data[0] * GST_MSECOND;
for (num = 0; payload_len > 0; ++num) {
++payload_data;
--payload_len;
- if (payload_len < sub_payload_len) {
+ if (G_UNLIKELY (payload_len < sub_payload_len)) {
GST_WARNING_OBJECT (demux, "Short payload! %u bytes left", payload_len);
return FALSE;
}
- if (sub_payload_len > 0) {
+ if (G_LIKELY (sub_payload_len > 0)) {
payload.buf = asf_packet_create_payload_buffer (packet,
&payload_data, &payload_len, sub_payload_len);
+ payload.buf_filled = sub_payload_len;
payload.ts = ts;
- payload.duration = ts_delta;
+ if (G_LIKELY (ts_delta))
+ payload.duration = ts_delta;
+ else
+ payload.duration = GST_CLOCK_TIME_NONE;
gst_asf_payload_queue_for_stream (demux, &payload, stream);
}
data = GST_BUFFER_DATA (buf);
size = GST_BUFFER_SIZE (buf);
+ GST_LOG_OBJECT (demux, "Buffer size: %u", size);
/* need at least two payload flag bytes, send time, and duration */
- if (size < 2 + 4 + 2)
+ if (G_UNLIKELY (size < 2 + 4 + 2))
goto short_packet;
packet.buf = buf;
ec_len_type);
ec_len = 2;
}
- GST_LOG ("packet has error correction (%u bytes)", ec_len);
+ GST_LOG_OBJECT (demux, "packet has error correction (%u bytes)", ec_len);
/* still need at least two payload flag bytes, send time, and duration */
if (size <= (1 + ec_len) + 2 + 4 + 2)
packet.padding = asf_packet_read_varlen_int (flags1, 3, &data, &size);
- if (size < 6)
+ if (G_UNLIKELY (size < 6))
goto short_packet;
packet.send_time = GST_READ_UINT32_LE (data) * GST_MSECOND;
data += 4 + 2;
size -= 4 + 2;
+ GST_LOG_OBJECT (demux, "flags : 0x%x", flags1);
GST_LOG_OBJECT (demux, "multiple payloads: %u", has_multiple_payloads);
GST_LOG_OBJECT (demux, "packet length : %u", packet.length);
GST_LOG_OBJECT (demux, "sequence : %u", packet.sequence);
GST_LOG_OBJECT (demux, "duration : %" GST_TIME_FORMAT,
GST_TIME_ARGS (packet.duration));
- if (packet.padding == (guint) - 1 || size < packet.padding)
+ if (G_UNLIKELY (packet.padding == (guint) - 1 || size < packet.padding))
goto short_packet;
size -= packet.padding;
/* adjust available size for parsing if there's less actual packet data for
* parsing than there is data in bytes (for sample see bug 431318) */
- if (packet.length != 0 && packet.length < demux->packet_size) {
- GST_LOG_OBJECT (demux, "shortened packet, adjusting available data size");
- size -= (demux->packet_size - packet.length);
+ if (G_UNLIKELY (packet.length != 0 && packet.padding == 0
+ && packet.length < demux->packet_size)) {
+ GST_LOG_OBJECT (demux, "shortened packet with implicit padding, "
+ "adjusting available data size");
+ if (size < demux->packet_size - packet.length) {
+ /* the buffer is smaller than the implicit padding */
+ goto short_packet;
+ } else {
+ /* subtract the implicit padding */
+ size -= (demux->packet_size - packet.length);
+ }
}
if (has_multiple_payloads) {
guint i, num, lentype;
- if (size < 1)
+ if (G_UNLIKELY (size < 1))
goto short_packet;
num = (GST_READ_UINT8 (data) & 0x3F) >> 0;
GST_LOG_OBJECT (demux, "num payloads : %u", num);
for (i = 0; i < num; ++i) {
- GST_LOG_OBJECT (demux, "Parsing payload %u/%u", i + 1, num);
+ GST_LOG_OBJECT (demux, "Parsing payload %u/%u, size left: %u", i + 1, num,
+ size);
ret = gst_asf_demux_parse_payload (demux, &packet, lentype, &data, &size);
- if (!ret) {
+ if (G_UNLIKELY (!ret)) {
GST_WARNING_OBJECT (demux, "Failed to parse payload %u/%u", i + 1, num);
break;
}