Quoting Alexandra Hájková (2015-02-28 11:17:37)
> From: alexandra <alexandra@sai>
>
> The old one is result of reverse engeneering and guesswork.
> The new one had been written following the now-available specification.
>
> This work is part of Outreach Program for Women Summer 2014 activities.
> ---
> libavformat/asf.h | 21 -
> libavformat/asfdec.c | 2508
> ++++++++++++++++++++++-------------------
> libavformat/asfenc.c | 21 +
> tests/ref/fate/lossless-wma | 2 +-
> tests/ref/fate/wmv8-drm-nodec | 1 -
> tests/ref/seek/lavf-asf | 54 +-
> 6 files changed, 1375 insertions(+), 1232 deletions(-)
>
> diff --git a/libavformat/asf.h b/libavformat/asf.h
> index 2f6722a..7ac28db 100644
> --- a/libavformat/asf.h
> +++ b/libavformat/asf.h
> @@ -28,27 +28,6 @@
>
> #define PACKET_SIZE 3200
>
> -typedef struct ASFStream {
> - int num;
> - unsigned char seq;
> - /* use for reading */
> - AVPacket pkt;
> - int frag_offset;
> - int timestamp;
> - int64_t duration;
> -
> - int ds_span; /* descrambling */
> - int ds_packet_size;
> - int ds_chunk_size;
> -
> - int64_t packet_pos;
> -
> - uint16_t stream_language_index;
> -
> - int palette_changed;
> - uint32_t palette[256];
> -} ASFStream;
> -
> typedef struct ASFMainHeader {
> ff_asf_guid guid; ///< generated by client computer
> uint64_t file_size; /**< in bytes
> diff --git a/libavformat/asfdec.c b/libavformat/asfdec.c
> index 85e800d..bc25931 100644
> --- a/libavformat/asfdec.c
> +++ b/libavformat/asfdec.c
> @@ -1,6 +1,6 @@
> /*
> - * ASF compatible demuxer
> - * Copyright (c) 2000, 2001 Fabrice Bellard
> + * Microsoft Advanced Streaming Format demuxer
> + * Copyright (c) 2014 Alexandra Hájková
> *
> * This file is part of Libav.
> *
> @@ -19,8 +19,6 @@
> * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA
> */
>
> -#include <inttypes.h>
> -
> #include "libavutil/attributes.h"
> #include "libavutil/avassert.h"
> #include "libavutil/avstring.h"
> @@ -30,6 +28,8 @@
> #include "libavutil/internal.h"
> #include "libavutil/mathematics.h"
> #include "libavutil/opt.h"
> +#include "libavutil/time_internal.h"
> +
> #include "avformat.h"
> #include "avio_internal.h"
> #include "avlanguage.h"
> @@ -39,151 +39,296 @@
> #include "asf.h"
> #include "asfcrypt.h"
>
> -typedef struct {
> - const AVClass *class;
> - int asfid2avid[128]; ///< conversion table from asf ID 2
> AVStream ID
> - ASFStream streams[128]; ///< it's max number and it's not
> that big
> - uint32_t stream_bitrates[128]; ///< max number of streams, bitrate
> for each (for streaming)
> - AVRational dar[128];
> - char stream_languages[128][6]; ///< max number of streams,
> language for each (RFC1766, e.g. en-US)
> - /* non streamed additonnal info */
> - /* packet filling */
> - int packet_size_left;
> - /* only for reading */
> - uint64_t data_offset; ///< beginning of the first data
> packet
> - uint64_t data_object_offset; ///< data object offset (excl. GUID
> & size)
> - uint64_t data_object_size; ///< size of the data object
> - int index_read;
> -
> - ASFMainHeader hdr;
> -
> - int packet_flags;
> - int packet_property;
> - int packet_timestamp;
> - int packet_segsizetype;
> - int packet_segments;
> - int packet_seq;
> - int packet_replic_size;
> - int packet_key_frame;
> - int packet_padsize;
> - unsigned int packet_frag_offset;
> - unsigned int packet_frag_size;
> - int64_t packet_frag_timestamp;
> - int packet_multi_size;
> - int packet_obj_size;
> - int packet_time_delta;
> - int packet_time_start;
> - int64_t packet_pos;
> -
> - int stream_index;
> -
> - ASFStream *asf_st; ///< currently decoded stream
> -
> - int no_resync_search;
> +#define ASF_BOOL 0x2
> +#define ASF_WORD 0x5
> +#define ASF_GUID 0x6
> +#define ASF_DWORD 0x3
> +#define ASF_QWORD 0x4
> +#define ASF_UNICODE 0x0
> +#define IS_BROADCAST 1
> +#define ASF_BYTE_ARRAY 0x1
> +#define ASF_TYPE_AUDIO 0x2
> +#define ASF_TYPE_VIDEO 0x1
> +#define ASF_STREAM_NUM 0x7F
> +#define ASF_MAX_STREAMS 128
> +#define BMP_HEADER_SIZE 40
> +#define ASF_NUM_OF_PAYLOADS 0x3F
> +#define ASF_ERROR_CORRECTION_LENGTH_TYPE 0x60
> +#define ASF_PACKET_ERROR_CORRECTION_DATA_SIZE 0x2
> +
> +
> +typedef struct GUIDParseTable {
> + const char *name;
> + ff_asf_guid guid;
> + int (*read_object)(AVFormatContext *, const struct GUIDParseTable *);
> + int is_subobject;
> +} GUIDParseTable;
> +
> +typedef struct ASFPacket {
> + AVPacket *avpkt;
Is there any reason for this to be allocated separately?
> + int64_t dts;
> + uint32_t frame_num; // ASF payloads with the same number are parts of
> the same frame
> + int flags;
> + int data_size;
> + int duration;
> + int size_left;
> + uint8_t stream_index;
> +} ASFPacket;
> +
> +typedef struct ASFStream2 {
ASFDemuxerStream?
ASFDecStream?
ASFStream2 is not a very good name.
> + uint8_t stream_index; //from packet header
> + int index; // stream index in AVFormatContext, set in
> read_stream_properties
> + int type;
> + int indexed; // added index entries from the Simple Index Object or not
> + int8_t span; // for deinterleaving
> + uint16_t virtual_pkt_len;
> + uint16_t virtual_chunk_len;
> + int16_t idx_type; // index type in the Index Parameters Object
> + int16_t lang_idx;
> + char lang[100];
This seems to be unused.
> return 0;
> }
>
> -static int asf_read_marker(AVFormatContext *s, int64_t size)
> +static int read_parameters(AVFormatContext *s, const GUIDParseTable *g)
> {
> - AVIOContext *pb = s->pb;
> ASFContext *asf = s->priv_data;
> - int i, count, name_len, ret;
> - char name[1024];
> -
> - avio_rl64(pb); // reserved 16 bytes
> - avio_rl64(pb); // ...
> - count = avio_rl32(pb); // markers count
> - avio_rl16(pb); // reserved 2 bytes
> - name_len = avio_rl16(pb); // name length
> - for (i = 0; i < name_len; i++)
> - avio_r8(pb); // skip the name
> -
> - for (i = 0; i < count; i++) {
> - int64_t pres_time;
> - int name_len;
> -
> - avio_rl64(pb); // offset, 8 bytes
> - pres_time = avio_rl64(pb); // presentation time
> - pres_time -= asf->hdr.preroll * 10000;
> - avio_rl16(pb); // entry length
> - avio_rl32(pb); // send time
> - avio_rl32(pb); // flags
> - name_len = avio_rl32(pb); // name length
> - if ((ret = avio_get_str16le(pb, name_len * 2, name,
> - sizeof(name))) < name_len)
> - avio_skip(pb, name_len - ret);
> - avpriv_new_chapter(s, i, (AVRational) { 1, 10000000 }, pres_time,
> - AV_NOPTS_VALUE, name);
> + AVIOContext *pb = s->pb;
> + uint64_t size = avio_rl64(pb);
> + uint16_t nb_idx_specs;
> + int i, j;
> +
> +
Too many newlines.
> + avio_skip(pb, 4);
> + nb_idx_specs = avio_rl16(pb);
> + for (i = 0; i < nb_idx_specs; i++) {
> + uint16_t stream_index = avio_rl16(pb);
> + for (j = 0; j < asf->nb_streams; j++) {
> + if (asf->asf_st[j]->stream_index == stream_index)
> + asf->asf_st[j]-> idx_type = avio_rl16(pb);
Extra space.
> +/*
> + * Find a timestamp for the requested position within the payload
> + * where pos (position) is the offset inside the Data Object.
> + * When position is not on the packet boundary, asf_read_timestamp tries
> + * to find the closest packet offset after this position. If this packet
> + * is a key frame, this packet timestamp is read and an index entry is
> created
> + * for the packet. If this packet belongs to the requested stream,
> + * asf_read_timestamp upgrades pos to the packet beginning offset and
> + * returns this packet's dts. So returned dts is the dts of the first key
> frame with
> + * matching stream number after given position.
> + */
> +static int64_t asf_read_timestamp(AVFormatContext *s, int stream_index,
> + int64_t *pos, int64_t pos_limit)
> +{
> + ASFContext *asf = s->priv_data;
> + int64_t pkt_pos = *pos, pkt_offset, dts = AV_NOPTS_VALUE, data_end;
> + AVPacket *pkt = av_mallocz(sizeof(*pkt));
The packet can live on stack.
> + int n;
>
> - asf_st = s->streams[i]->priv_data;
> - av_assert0(asf_st);
> + if (!pkt)
> + return AVERROR(ENOMEM);
> + data_end = asf->data_offset + asf->data_size;
>
> -// assert((asf_st->packet_pos - s->data_offset) % s->packet_size
> == 0);
> - pos = asf_st->packet_pos;
> + n = (pkt_pos - asf->first_packet_offset + asf->packet_size - 1) /
> + asf->packet_size;
> + n = av_clip_c(n, 0, ((data_end - asf->first_packet_offset) /
> asf->packet_size - 1));
> + pkt_pos = asf->first_packet_offset + n * asf->packet_size;
>
> - av_add_index_entry(s->streams[i], pos, pts, pkt->size,
> - pos - start_pos[i] + 1, AVINDEX_KEYFRAME);
> - start_pos[i] = asf_st->packet_pos + 1;
> + avio_seek(s->pb, pkt_pos, SEEK_SET);
> + pkt_offset = pkt_pos;
>
> - if (pkt->stream_index == stream_index)
> - break;
> - }
> - }
> + reset_packet_state(s);
> + while (avio_tell(s->pb) < data_end) {
>
> - *ppos = pos;
> - return pts;
> -}
> + int i, ret, st_found;
>
> -static int asf_build_simple_index(AVFormatContext *s, int stream_index)
> -{
> - ff_asf_guid g;
> - ASFContext *asf = s->priv_data;
> - int64_t current_pos = avio_tell(s->pb);
> - int i, ret = 0;
> -
> - avio_seek(s->pb, asf->data_object_offset + asf->data_object_size,
> SEEK_SET);
> - if ((ret = ff_get_guid(s->pb, &g)) < 0)
> - goto end;
> -
> - /* the data object can be followed by other top-level objects,
> - * skip them until the simple index object is reached */
> - while (ff_guidcmp(&g, &index_guid)) {
> - int64_t gsize = avio_rl64(s->pb);
> - if (gsize < 24 || s->pb->eof_reached) {
> - goto end;
> + av_init_packet(pkt);
> + pkt_offset = avio_tell(s->pb);
> + if ((ret = asf_read_packet(s, pkt)) < 0) {
> + dts = AV_NOPTS_VALUE;
> + av_freep(&pkt);
> + return ret;
> }
> - avio_skip(s->pb, gsize - 24);
> - if ((ret = ff_get_guid(s->pb, &g)) < 0)
> - goto end;
> - }
> -
> - {
> - int64_t itime, last_pos = -1;
> - int pct, ict;
> - int64_t av_unused gsize = avio_rl64(s->pb);
> - if ((ret = ff_get_guid(s->pb, &g)) < 0)
> - goto end;
> - itime = avio_rl64(s->pb);
> - pct = avio_rl32(s->pb);
> - ict = avio_rl32(s->pb);
> - av_log(s, AV_LOG_DEBUG,
> - "itime:0x%"PRIx64", pct:%d, ict:%d\n", itime, pct, ict);
> -
> - for (i = 0; i < ict; i++) {
> - int pktnum = avio_rl32(s->pb);
> - int pktct = avio_rl16(s->pb);
> - int64_t pos = s->data_offset + s->packet_size *
> (int64_t)pktnum;
> - int64_t index_pts = FFMAX(av_rescale(itime, i, 10000) -
> asf->hdr.preroll, 0);
> -
> - if (pos != last_pos) {
> - av_log(s, AV_LOG_DEBUG, "pktnum:%d, pktct:%d pts:
> %"PRId64"\n",
> - pktnum, pktct, index_pts);
> - av_add_index_entry(s->streams[stream_index], pos, index_pts,
> - s->packet_size, 0, AVINDEX_KEYFRAME);
> - last_pos = pos;
> + // ASFPacket may contain fragments of packets belonging to different
> streams,
> + // pkt_offset is the offset of the first fragment within it.
> + if ((pkt_offset >= (pkt_pos + asf->packet_size)))
> + pkt_pos += asf->packet_size;
> + for (i = 0; i < asf->nb_streams; i++) {
> + ASFStream2 *st = asf->asf_st[i];
> +
> + st_found = 0;
> + if (pkt->flags & AV_PKT_FLAG_KEY) {
> + dts = pkt->dts;
> + if (dts) {
> + av_add_index_entry(s->streams[pkt->stream_index],
> pkt_pos,
> + dts, pkt->size, 0, AVINDEX_KEYFRAME);
> + if (stream_index == st->index) {
> + st_found = 1;
> + break;
> + }
> + }
> }
> }
> - asf->index_read = ict > 0;
> + if (st_found)
> + break;
> + av_free_packet(pkt);
> }
> -end:
> - if (s->pb->eof_reached)
> - ret = 0;
> - avio_seek(s->pb, current_pos, SEEK_SET);
> - return ret;
> + *pos = pkt_pos;
> +
> + av_free_packet(pkt);
> + av_freep(&pkt);
> + return dts;
> }
>
> static int asf_read_seek(AVFormatContext *s, int stream_index,
> - int64_t pts, int flags)
> + int64_t timestamp, int flags)
> {
> ASFContext *asf = s->priv_data;
> - AVStream *st = s->streams[stream_index];
> - int64_t pos;
> - int index, ret = 0;
> -
> - if (s->packet_size <= 0)
> - return -1;
> -
> - /* Try using the protocol's read_seek if available */
> - if (s->pb) {
> - int ret = avio_seek_time(s->pb, stream_index, pts, flags);
> - if (ret >= 0)
> - asf_reset_header(s);
> - if (ret != AVERROR(ENOSYS))
> + int idx, ret;
> +
> + if (s->streams[stream_index]->nb_index_entries && asf->is_simple_index) {
> + idx = av_index_search_timestamp(s->streams[stream_index], timestamp,
> flags);
> + if (idx < 0 || idx >= s->streams[stream_index]->nb_index_entries)
> + return AVERROR_INVALIDDATA;
> + avio_seek(s->pb, s->streams[stream_index]->index_entries[idx].pos,
> SEEK_SET);
> + } else {
> + if ((ret = ff_seek_frame_binary(s, stream_index, timestamp, flags))
> < 0)
> return ret;
> +
> + // read_timestamp is called inside ff_seek_frame_binary and leaves
> state dirty,
> + // so reset_packet_state have to be called after it.
> + reset_packet_state(s);
> }
>
> - /* explicitly handle the case of seeking to 0 */
> - if (!pts) {
> - asf_reset_header(s);
> - avio_seek(s->pb, s->data_offset, SEEK_SET);
> - return 0;
> + return 0;
> +}
> +
> +static const GUIDParseTable *find_guid(ff_asf_guid guid)
> +{
> + int j, ret;
> + const GUIDParseTable *g;
> +
> + swap_guid(guid);
> + g = gdef;
> + for (j = 0; j < FF_ARRAY_ELEMS(gdef); j++) {
> + if (!(ret = memcmp(guid, g->guid, sizeof(g->guid))))
> + return g;
> + g++;
> }
>
> - if (!asf->index_read)
> - ret = asf_build_simple_index(s, stream_index);
> + return NULL;
> +}
> +
> +static int detect_unknown_subobject(AVFormatContext *s, int64_t offset,
> int64_t size)
> +{
> + ASFContext *asf = s->priv_data;
> + AVIOContext *pb = s->pb;
> + const GUIDParseTable *g = NULL;
> + ff_asf_guid guid;
> + int ret;
> +
> + while (avio_tell(pb) <= offset + size) {
> + asf->offset = avio_tell(pb);
> + if ((ret = ff_get_guid(pb, &guid)) < 0)
> + return ret;
> + g = find_guid(guid);
> + if (g) {
> + if ((ret = g->read_object(s, g)) < 0)
> + return ret;
> + } else {
> + GUIDParseTable *g2 = av_mallocz(sizeof(GUIDParseTable));
The table can live on stack, no need for it to be malloced.
> + if (!g2)
> + return AVERROR(ENOMEM);
> + g2->name = "Unknown";
> + g2->is_subobject = 1;
> + read_unknown(s, g2);
> + av_freep(&g2);
> + }
> + }
> + align_position(pb, offset, size);
>
> - if (!ret && asf->index_read && st->index_entries) {
> - index = av_index_search_timestamp(st, pts, flags);
> - if (index >= 0) {
> - /* find the position */
> - pos = st->index_entries[index].pos;
> + return 0;
> +}
>
> - /* do the seek */
> - av_log(s, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos);
> - avio_seek(s->pb, pos, SEEK_SET);
> - asf_reset_header(s);
> - return 0;
> +static int asf_read_header(AVFormatContext *s)
> +{
> + ASFContext *asf = s->priv_data;
> + AVIOContext *pb = s->pb;
> + const GUIDParseTable *g = NULL;
> + ff_asf_guid guid;
> + int i, ret;
> + uint64_t size;
> +
> + asf->preroll = 0;
> + asf->is_simple_index = 0;
> + ff_get_guid(pb, &guid);
> + if (ff_guidcmp(&guid, &ff_asf_header))
> + return AVERROR_INVALIDDATA;
> + asf->header_obj_size = avio_rl64(pb);
> + avio_skip(pb, 6); // skip number of header objects and 2 reserved bytes
> + asf->data_reached = 0;
> +
> + // 1 is here instead of pb->eof_reached because, Data are skipped for
> the first time,
> + // Index object is processed and got eof and then seeking back to the
> Data is performed.
> + while (1) {
> + asf->offset = avio_tell(pb);
> + if (asf->offset > asf->header_obj_size && !asf->data_reached) {
> + avio_seek(pb, asf->header_obj_size, SEEK_SET);
> + asf->offset = asf->header_obj_size;
> }
> + if ((ret = ff_get_guid(pb, &guid)) < 0) {
> + if (ret == AVERROR_EOF && asf->data_reached) {
> + avio_seek(pb, asf->first_packet_offset, SEEK_SET);
> + break;
> + } else
> + return ret;
> + }
> + g = find_guid(guid);
> + if (g) {
> + asf->unknown_offset = asf->offset;
> + asf->is_header = 1;
> + if ((ret = g->read_object(s, g)) < 0)
> + return ret;
> + } else {
> + size = avio_rl64(pb);
> + align_position(pb, asf->offset, size);
> + }
> + if (asf->data_reached || !pb->seekable)
> + break;
> + }
> +
> + for (i = 0; i < asf->nb_streams; i++) {
> + const char *rfc1766 = asf->langs[asf->asf_st[i]->lang_idx];
> + set_language(s, rfc1766);
This looks wrong. The language is per stream.
--
Anton Khirnov
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel