| /* GStreamer ASF/WMV/WMA demuxer |
| * Copyright (C) 2007 Tim-Philipp Müller <tim centricular net> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| /* FIXME: |
| * file:///home/tpm/samples/video/asf//336370-regis-velo862.wmv |
| * file:///home/tpm/samples/video/asf//336370-eichhoer.wmv |
| * throw errors (not always necessarily) in this code path |
| * (looks like they carry broken payloads/packets though) */ |
| |
| #include "asfpacket.h" |
| |
| #include <gst/gstutils.h> |
| #include <gst/gstinfo.h> |
| #include <string.h> |
| |
| #define GST_ASF_PAYLOAD_KF_COMPLETE(stream, payload) (stream->is_video && payload->keyframe && payload->buf_filled >= payload->mo_size) |
| |
| /* 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 inline gint |
| asf_packet_read_varlen_int (guint lentype_flags, guint lentype_bit_offset, |
| const guint8 ** p_data, guint * p_size) |
| { |
| 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 (G_UNLIKELY (*p_size < len)) { |
| GST_WARNING ("need %u bytes, but only %u bytes available", len, *p_size); |
| return -1; |
| } |
| |
| switch (len) { |
| case 0: |
| val = 0; |
| break; |
| case 1: |
| val = GST_READ_UINT8 (*p_data); |
| break; |
| case 2: |
| val = GST_READ_UINT16_LE (*p_data); |
| break; |
| case 4: |
| val = GST_READ_UINT32_LE (*p_data); |
| break; |
| default: |
| val = 0; |
| g_assert_not_reached (); |
| } |
| |
| *p_data += len; |
| *p_size -= len; |
| |
| return (gint) val; |
| } |
| |
| static GstBuffer * |
| asf_packet_create_payload_buffer (AsfPacket * packet, const guint8 ** p_data, |
| guint * p_size, guint payload_len) |
| { |
| guint off; |
| |
| g_assert (payload_len <= *p_size); |
| |
| off = (guint) (*p_data - packet->bdata); |
| g_assert (off < gst_buffer_get_size (packet->buf)); |
| |
| *p_data += payload_len; |
| *p_size -= payload_len; |
| |
| return gst_buffer_copy_region (packet->buf, GST_BUFFER_COPY_ALL, off, |
| payload_len); |
| } |
| |
| static AsfPayload * |
| asf_payload_search_payloads_queue (AsfPayload * payload, GArray * payload_list) |
| { |
| AsfPayload *ret = NULL; |
| gint idx; |
| for (idx = payload_list->len - 1; idx >= 0; idx--) { |
| ret = &g_array_index (payload_list, AsfPayload, idx); |
| |
| if (G_UNLIKELY (ret->mo_size == payload->mo_size && |
| ret->mo_number == payload->mo_number)) { |
| return ret; |
| } |
| } |
| return NULL; |
| } |
| |
| static AsfPayload * |
| asf_payload_find_previous_fragment (GstASFDemux * demux, AsfPayload * payload, |
| AsfStream * stream) |
| { |
| AsfPayload *ret = NULL; |
| |
| if (GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment)) { |
| |
| /* Search in queued payloads list */ |
| ret = asf_payload_search_payloads_queue (payload, stream->payloads); |
| if (ret) { |
| GST_DEBUG |
| ("previous fragments found in payloads queue for reverse playback : object ID %d", |
| ret->mo_number); |
| return ret; |
| } |
| |
| /* Search in payloads 'to be queued' list */ |
| ret = asf_payload_search_payloads_queue (payload, stream->payloads_rev); |
| if (ret) { |
| GST_DEBUG |
| ("previous fragments found in temp payload queue for reverse playback : object ID %d", |
| ret->mo_number); |
| return ret; |
| } |
| } else { |
| 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 (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_WARNING ("Merged fragments would be bigger than the media object"); |
| return FALSE; |
| } |
| #endif |
| |
| return ret; |
| } |
| |
| /* TODO: if we have another payload already queued for this stream and that |
| * payload doesn't have a duration, maybe we can calculate a duration for it |
| * (if the previous timestamp is smaller etc. etc.) */ |
| static void |
| gst_asf_payload_queue_for_stream_forward (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)); |
| |
| /* make timestamps start from 0; first_ts will be determined during activation (once we have enough data), |
| which will also update ts of all packets queued before we knew first_ts; */ |
| if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (demux->first_ts) |
| && GST_CLOCK_TIME_IS_VALID (payload->ts))) { |
| if (payload->ts > demux->first_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; |
| guint idx_last; |
| |
| idx_last = stream->payloads->len - 1; |
| prev = &g_array_index (stream->payloads, AsfPayload, idx_last); |
| |
| if (G_UNLIKELY (gst_asf_payload_is_complete (prev))) |
| break; |
| |
| GST_DEBUG_OBJECT (demux, "Dropping incomplete fragmented media object " |
| "queued for stream %u", stream->id); |
| |
| gst_buffer_replace (&prev->buf, NULL); |
| g_array_remove_index (stream->payloads, idx_last); |
| |
| /* there's data missing, so there's a discontinuity now */ |
| GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT); |
| } |
| |
| /* If we're about to queue a key frame that is before the segment start, we |
| * can ditch any previously queued payloads (which would also be before the |
| * segment start). This makes sure the decoder doesn't decode more than |
| * 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 (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); |
| while (stream->payloads->len > 0) { |
| AsfPayload *last; |
| guint idx_last; |
| |
| idx_last = stream->payloads->len - 1; |
| last = &g_array_index (stream->payloads, AsfPayload, idx_last); |
| gst_buffer_replace (&last->buf, NULL); |
| g_array_remove_index (stream->payloads, idx_last); |
| } |
| |
| /* Mark discontinuity (should be done via stream->discont anyway though) */ |
| GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT); |
| } |
| |
| g_array_append_vals (stream->payloads, payload, 1); |
| } |
| |
| static void |
| gst_asf_payload_queue_for_stream_reverse (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)); |
| |
| if (demux->multiple_payloads) { |
| /* store the payload in temporary buffer, until we parse all payloads in this packet */ |
| g_array_append_vals (stream->payloads_rev, payload, 1); |
| } else { |
| if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (payload->ts))) { |
| g_array_append_vals (stream->payloads, payload, 1); |
| if (GST_ASF_PAYLOAD_KF_COMPLETE (stream, payload)) { |
| stream->kf_pos = stream->payloads->len - 1; |
| } |
| } else { |
| gst_buffer_unref (payload->buf); |
| } |
| } |
| } |
| |
| |
| static void |
| 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)); |
| |
| if (GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment)) { |
| gst_asf_payload_queue_for_stream_reverse (demux, payload, stream); |
| } else { |
| gst_asf_payload_queue_for_stream_forward (demux, payload, stream); |
| } |
| |
| } |
| |
| static void |
| asf_payload_parse_replicated_data_extensions (AsfStream * stream, |
| AsfPayload * payload) |
| { |
| AsfPayloadExtension *ext; |
| guint off; |
| guint16 ext_len; |
| |
| if (!stream->ext_props.valid || stream->ext_props.payload_extensions == NULL) |
| return; |
| |
| off = 8; |
| for (ext = stream->ext_props.payload_extensions; ext->len > 0; ++ext) { |
| ext_len = ext->len; |
| if (ext_len == 0xFFFF) { /* extension length is determined by first two bytes in replicated data */ |
| ext_len = GST_READ_UINT16_LE (payload->rep_data + off); |
| off += 2; |
| } |
| 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 (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; |
| case ASF_PAYLOAD_EXTENSION_TIMING: |
| { |
| /* dvr-ms timing - this will override packet timestamp */ |
| guint64 time = GST_READ_UINT64_LE (payload->rep_data + off + 8); |
| if (time != 0xFFFFFFFFFFFFFFFF) |
| payload->ts = time * 100; |
| else |
| payload->ts = GST_CLOCK_TIME_NONE; |
| } |
| break; |
| default: |
| GST_LOG ("UNKNOWN PAYLOAD EXTENSION!"); |
| break; |
| } |
| off += ext_len; |
| } |
| } |
| |
| static gboolean |
| gst_asf_demux_parse_payload (GstASFDemux * demux, AsfPacket * packet, |
| gint lentype, const guint8 ** p_data, guint * p_size) |
| { |
| AsfPayload payload = { 0, }; |
| AsfStream *stream; |
| gboolean is_compressed; |
| guint payload_len; |
| guint stream_num; |
| |
| if (G_UNLIKELY (*p_size < 1)) { |
| GST_WARNING_OBJECT (demux, "Short packet!"); |
| return FALSE; |
| } |
| |
| stream_num = GST_READ_UINT8 (*p_data) & 0x7f; |
| payload.keyframe = ((GST_READ_UINT8 (*p_data) & 0x80) != 0); |
| |
| *p_data += 1; |
| *p_size -= 1; |
| |
| 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); |
| payload.mo_offset = |
| asf_packet_read_varlen_int (packet->prop_flags, 2, p_data, p_size); |
| payload.rep_data_len = |
| asf_packet_read_varlen_int (packet->prop_flags, 0, p_data, p_size); |
| |
| is_compressed = (payload.rep_data_len == 1); |
| |
| GST_LOG_OBJECT (demux, "payload for stream %u", stream_num); |
| GST_LOG_OBJECT (demux, "keyframe : %s", (payload.keyframe) ? "yes" : "no"); |
| GST_LOG_OBJECT (demux, "compressed : %s", (is_compressed) ? "yes" : "no"); |
| |
| 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; |
| } |
| |
| memcpy (payload.rep_data, *p_data, |
| MIN (sizeof (payload.rep_data), payload.rep_data_len)); |
| |
| *p_data += payload.rep_data_len; |
| *p_size -= payload.rep_data_len; |
| |
| 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 (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", |
| payload_len, *p_size); |
| return FALSE; |
| } |
| } else { |
| payload_len = *p_size; |
| } |
| |
| GST_LOG_OBJECT (demux, "payload length: %u", payload_len); |
| |
| stream = gst_asf_demux_get_stream (demux, stream_num); |
| |
| if (G_UNLIKELY (stream == NULL)) { |
| if (gst_asf_demux_is_unknown_stream (demux, stream_num)) { |
| GST_WARNING_OBJECT (demux, "Payload for unknown stream %u, skipping", |
| stream_num); |
| } |
| if (*p_size < payload_len) { |
| *p_data += *p_size; |
| *p_size = 0; |
| } else { |
| *p_data += payload_len; |
| *p_size -= payload_len; |
| } |
| return TRUE; |
| } |
| |
| if (!stream->is_video) |
| stream->kf_pos = 0; |
| |
| 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; |
| 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_LOG_OBJECT (demux, "media object ts : %" GST_TIME_FORMAT, |
| GST_TIME_ARGS (payload.ts)); |
| GST_LOG_OBJECT (demux, "media object dur : %" GST_TIME_FORMAT, |
| GST_TIME_ARGS (payload.duration)); |
| } else if (payload.rep_data_len == 0) { |
| payload.mo_size = 0; |
| } 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 (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 if (GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment)) { |
| /* Handle fragmented payloads for reverse playback */ |
| AsfPayload *prev; |
| const guint8 *payload_data = *p_data; |
| prev = asf_payload_find_previous_fragment (demux, &payload, stream); |
| |
| if (prev) { |
| gint idx; |
| AsfPayload *p; |
| gst_buffer_fill (prev->buf, payload.mo_offset, |
| payload_data, payload_len); |
| prev->buf_filled += payload_len; |
| if (payload.keyframe && payload.mo_offset == 0) { |
| stream->reverse_kf_ready = TRUE; |
| |
| for (idx = stream->payloads->len - 1; idx >= 0; idx--) { |
| p = &g_array_index (stream->payloads, AsfPayload, idx); |
| if (p->mo_number == payload.mo_number) { |
| /* Mark position of KF for reverse play */ |
| stream->kf_pos = idx; |
| } |
| } |
| } |
| } else { |
| payload.buf = gst_buffer_new_allocate (NULL, payload.mo_size, NULL); /* can we use (mo_size - offset) for size? */ |
| gst_buffer_fill (payload.buf, payload.mo_offset, |
| payload_data, payload_len); |
| payload.buf_filled = payload.mo_size - (payload.mo_offset); |
| gst_asf_payload_queue_for_stream (demux, &payload, stream); |
| } |
| *p_data += payload_len; |
| *p_size -= payload_len; |
| } 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 (demux, &payload, stream))) { |
| if (prev->buf == NULL || (payload.mo_size > 0 |
| && payload.mo_size != prev->mo_size) |
| || payload.mo_offset >= gst_buffer_get_size (prev->buf) |
| || payload.mo_offset + payload_len > |
| gst_buffer_get_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); |
| } |
| gst_buffer_fill (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); |
| } |
| } else { |
| GST_DEBUG_OBJECT (demux, "n-th payload fragment, but don't have " |
| "any previous fragment, ignoring payload"); |
| } |
| } else { |
| GST_LOG_OBJECT (demux, "allocating buffer of size %u for fragmented " |
| "media object", payload.mo_size); |
| payload.buf = gst_buffer_new_allocate (NULL, payload.mo_size, NULL); |
| gst_buffer_fill (payload.buf, 0, payload_data, payload_len); |
| payload.buf_filled = payload_len; |
| |
| gst_asf_payload_queue_for_stream (demux, &payload, stream); |
| } |
| } |
| } else { |
| const guint8 *payload_data; |
| GstClockTime ts, ts_delta; |
| guint num; |
| |
| GST_LOG_OBJECT (demux, "Compressed payload, length=%u", payload_len); |
| |
| payload_data = *p_data; |
| |
| *p_data += payload_len; |
| *p_size -= payload_len; |
| |
| 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) { |
| guint sub_payload_len; |
| |
| sub_payload_len = GST_READ_UINT8 (payload_data); |
| |
| GST_LOG_OBJECT (demux, "subpayload #%u: len=%u, ts=%" GST_TIME_FORMAT, |
| num, sub_payload_len, GST_TIME_ARGS (ts)); |
| |
| ++payload_data; |
| --payload_len; |
| |
| if (G_UNLIKELY (payload_len < sub_payload_len)) { |
| GST_WARNING_OBJECT (demux, "Short payload! %u bytes left", payload_len); |
| return FALSE; |
| } |
| |
| 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; |
| 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); |
| } |
| |
| ts += ts_delta; |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| GstAsfDemuxParsePacketError |
| gst_asf_demux_parse_packet (GstASFDemux * demux, GstBuffer * buf) |
| { |
| AsfPacket packet = { 0, }; |
| GstMapInfo map; |
| const guint8 *data; |
| gboolean has_multiple_payloads; |
| GstAsfDemuxParsePacketError ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_NONE; |
| guint8 ec_flags, flags1; |
| guint size; |
| |
| gst_buffer_map (buf, &map, GST_MAP_READ); |
| data = map.data; |
| size = map.size; |
| GST_LOG_OBJECT (demux, "Buffer size: %u", size); |
| |
| /* need at least two payload flag bytes, send time, and duration */ |
| if (G_UNLIKELY (size < 2 + 4 + 2)) { |
| GST_WARNING_OBJECT (demux, "Packet size is < 8"); |
| ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE; |
| goto done; |
| } |
| |
| packet.buf = buf; |
| /* evidently transient */ |
| packet.bdata = data; |
| |
| ec_flags = GST_READ_UINT8 (data); |
| |
| /* skip optional error correction stuff */ |
| if ((ec_flags & 0x80) != 0) { |
| guint ec_len_type, ec_len; |
| |
| ec_len_type = (ec_flags & 0x60) >> 5; |
| if (ec_len_type == 0) { |
| ec_len = ec_flags & 0x0f; |
| } else { |
| GST_WARNING_OBJECT (demux, "unexpected error correction length type %u", |
| ec_len_type); |
| ec_len = 2; |
| } |
| 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) { |
| GST_WARNING_OBJECT (demux, "Packet size is < 8 with Error Correction"); |
| ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_FATAL; |
| goto done; |
| } |
| |
| data += 1 + ec_len; |
| size -= 1 + ec_len; |
| } |
| |
| /* parse payload info */ |
| flags1 = GST_READ_UINT8 (data); |
| packet.prop_flags = GST_READ_UINT8 (data + 1); |
| |
| data += 2; |
| size -= 2; |
| |
| has_multiple_payloads = (flags1 & 0x01) != 0; |
| |
| packet.length = asf_packet_read_varlen_int (flags1, 5, &data, &size); |
| |
| packet.sequence = asf_packet_read_varlen_int (flags1, 1, &data, &size); |
| |
| packet.padding = asf_packet_read_varlen_int (flags1, 3, &data, &size); |
| |
| if (G_UNLIKELY (size < 6)) { |
| GST_WARNING_OBJECT (demux, "Packet size is < 6"); |
| ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_FATAL; |
| goto done; |
| } |
| |
| packet.send_time = GST_READ_UINT32_LE (data) * GST_MSECOND; |
| packet.duration = GST_READ_UINT16_LE (data + 4) * 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, "padding : %u", packet.padding); |
| GST_LOG_OBJECT (demux, "send time : %" GST_TIME_FORMAT, |
| GST_TIME_ARGS (packet.send_time)); |
| |
| GST_LOG_OBJECT (demux, "duration : %" GST_TIME_FORMAT, |
| GST_TIME_ARGS (packet.duration)); |
| |
| if (GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment) |
| && demux->seek_to_cur_pos == TRUE) { |
| /* For reverse playback, initially parse packets forward until we reach packet with 'seek' timestamp */ |
| if (packet.send_time - demux->preroll > demux->segment.stop) { |
| demux->seek_to_cur_pos = FALSE; |
| } |
| ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_NONE; |
| goto done; |
| } |
| |
| if (G_UNLIKELY (packet.padding == (guint) - 1 || size < packet.padding)) { |
| GST_WARNING_OBJECT (demux, "No padding, or padding bigger than buffer"); |
| ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE; |
| goto done; |
| } |
| |
| 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 (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 */ |
| GST_WARNING_OBJECT (demux, "Buffer is smaller than the implicit padding"); |
| ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE; |
| goto done; |
| } else { |
| /* subtract the implicit padding */ |
| size -= (demux->packet_size - packet.length); |
| } |
| } |
| |
| if (has_multiple_payloads) { |
| guint i, num, lentype; |
| demux->multiple_payloads = TRUE; |
| |
| if (G_UNLIKELY (size < 1)) { |
| GST_WARNING_OBJECT (demux, "No room more in buffer"); |
| ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE; |
| goto done; |
| } |
| |
| num = (GST_READ_UINT8 (data) & 0x3F) >> 0; |
| lentype = (GST_READ_UINT8 (data) & 0xC0) >> 6; |
| |
| ++data; |
| --size; |
| |
| GST_LOG_OBJECT (demux, "num payloads : %u", num); |
| |
| for (i = 0; i < num; ++i) { |
| GST_LOG_OBJECT (demux, "Parsing payload %u/%u, size left: %u", i + 1, num, |
| size); |
| |
| if (G_UNLIKELY (!gst_asf_demux_parse_payload (demux, &packet, lentype, |
| &data, &size))) { |
| GST_WARNING_OBJECT (demux, "Failed to parse payload %u/%u", i + 1, num); |
| ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_FATAL; |
| break; |
| } |
| } |
| |
| if (GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment)) { |
| /* In reverse playback, we parsed the packet (with multiple payloads) and stored the payloads in temporary queue. |
| Now, add them to the stream's payload queue */ |
| for (i = 0; i < demux->num_streams; i++) { |
| AsfStream *s = &demux->stream[i]; |
| while (s->payloads_rev->len > 0) { |
| AsfPayload *p; |
| p = &g_array_index (s->payloads_rev, AsfPayload, |
| s->payloads_rev->len - 1); |
| g_array_append_vals (s->payloads, p, 1); |
| if (GST_ASF_PAYLOAD_KF_COMPLETE (s, p)) { |
| /* Mark position of KF for reverse play */ |
| s->kf_pos = s->payloads->len - 1; |
| } |
| g_array_remove_index (s->payloads_rev, (s->payloads_rev->len - 1)); |
| } |
| } |
| } |
| |
| } else { |
| GST_LOG_OBJECT (demux, "Parsing single payload"); |
| demux->multiple_payloads = FALSE; |
| if (G_UNLIKELY (!gst_asf_demux_parse_payload (demux, &packet, -1, &data, |
| &size))) { |
| GST_WARNING_OBJECT (demux, "Failed to parse payload"); |
| ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE; |
| } |
| } |
| |
| done: |
| gst_buffer_unmap (buf, &map); |
| return ret; |
| } |