Quoting Alexandra Hájková (2015-03-26 10:06:07)
> The old one is the result of the reverse engineering and guesswork.
> The new one has been written following the now-available specification.
>
> This work is part of Outreach Program for Women Summer 2014 activities
> for the Libav project.
>
> The fate references had to be changed because the old demuxer truncates
> the last frame in some cases, the new one handles it properly.
> The seek-test reference is changed because seeking works differently
> in the new demuxer. When seeking, the packet is not read from the stream
> directly, but it is rather constructed by the demuxer. That is why
> position is -1 now in the reference.
> ---
> libavformat/asf.h | 21 -
> libavformat/asfdec.c | 2494
> +++++++++++++++++++++--------------------
> libavformat/asfenc.c | 24 +-
> tests/fate/wma.mak | 2 +-
> tests/ref/fate/lossless-wma | 2 +-
> tests/ref/fate/wmv8-drm-nodec | 1 -
> tests/ref/seek/lavf-asf | 54 +-
> 7 files changed, 1357 insertions(+), 1241 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 fac5806..ca2fb3f 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,153 +39,291 @@
> #include "asf.h"
> #include "asfcrypt.h"
>
> +#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 ASF_FLAG_BROADCAST 0x1
> +#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;
> + 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 ASFStream {
> + uint8_t stream_index; //from packet header
> + int index; // stream index in AVFormatContext, set in
> asf_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 lang_idx;
> + ASFPacket *pkt;
Why is this allocated separately?
> +} ASFStream;
> +
> typedef struct ASFContext {
> - 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;
> - int export_xmp;
> + int data_reached;
> + int is_simple_index; // is simple index present or not 1/0
> + int is_header;
> +
> + uint64_t preroll;
> + uint64_t nb_packets; // ASF packets
> + uint32_t packet_size;
> + int64_t send_time;
> + int duration;
> +
> + uint32_t b_flags; // flags with broadcast flag
> + uint32_t prop_flags; // file properties object flags
> +
> + uint64_t data_size; // data object size
> + int64_t header_obj_size;
> + int64_t unknown_size; // size of the unknown object
Why are those signed?
> +
> + // presentation time offset, equal for all streams, should be equal to
> send time, !100-nanosecond units
> + uint64_t time_offset;
1) the comment is confusing
2) this value should be per-stream
2) it does not seem to be actually used
> + int64_t offset; // offset of the current object
This looks more like current state rather than an immutable file
property, so it should be placed with other state variables
> + int64_t data_offset;
> + int64_t first_packet_offset; // packet offset
> + int64_t unknown_offset; // for top level header objects or subobjects
> without specified behavior
> +
> + // ASF file must not contain more than 128 streams according to the
> specification
> + ASFStream *asf_st[ASF_MAX_STREAMS];
> + int nb_streams;
> + int stream_index; //from packet header, for the subpayload case
As above.
> + char langs[ASF_MAX_STREAMS][100];
> +
> + // packet parameteres
> + uint64_t sub_header_offset; // offset of subplayload header
> + int64_t sub_dts;
> + uint8_t dts_delta; // for subpayloads
> + uint32_t packet_size_internal; // packet size stored inside ASFPacket,
> can be 0
> + int64_t dts;
> + int64_t packet_offset; // offset of the current packet inside Data Object
> + uint32_t pad_len; // padding after payload
> + uint32_t rep_data_len;
> +
> + // packet state
> + uint64_t sub_left; // subpayloads left or not
> + int nb_sub; // number of subpayloads read so far from the current ASF
> packet
> + uint16_t mult_sub_len; // total length of subpayloads array inside
> multiple payload
> + uint64_t nb_mult_left; // multiple payloads left
> + int return_subpayload;
> + enum {
> + PARSE_PACKET_HEADER,
> + READ_SINGLE,
> + READ_MULTI,
> + READ_MULTI_SUB
> + } state;
> } ASFContext;
>
> -static const AVOption options[] = {
> - { "no_resync_search", "Don't try to resynchronize by looking for a
> certain optional start code", offsetof(ASFContext, no_resync_search),
> AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
> - { "export_xmp", "Export full XMP metadata", offsetof(ASFContext,
> export_xmp), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM
> },
> - { NULL },
> -};
> +static int detect_unknown_subobject(AVFormatContext *s, int64_t offset,
> int64_t size);
> +static const GUIDParseTable *find_guid(ff_asf_guid guid);
>
> -static const AVClass asf_class = {
> - .class_name = "asf demuxer",
> - .item_name = av_default_item_name,
> - .option = options,
> - .version = LIBAVUTIL_VERSION_INT,
> -};
> +static int asf_probe(AVProbeData *pd)
> +{
> + /* check file header */
> + if (!ff_guidcmp(pd->buf, &ff_asf_header))
> + return AVPROBE_SCORE_MAX;
> + else
> + return 0;
> +}
>
> -#undef NDEBUG
> -#include <assert.h>
> +static void swap_guid(ff_asf_guid guid)
> +{
> + FFSWAP(unsigned char, guid[0], guid[3]);
> + FFSWAP(unsigned char, guid[1], guid[2]);
> + FFSWAP(unsigned char, guid[4], guid[5]);
> + FFSWAP(unsigned char, guid[6], guid[7]);
> +}
>
> -#define ASF_MAX_STREAMS 127
> -#define FRAME_HEADER_SIZE 17
> -// Fix Me! FRAME_HEADER_SIZE may be different.
> +static void align_position(AVIOContext *pb, int64_t offset, uint64_t size)
> +{
> + if (avio_tell(pb) != offset + size)
> + avio_seek(pb, offset + size, SEEK_SET);
> +}
>
> -static const ff_asf_guid index_guid = {
> - 0x90, 0x08, 0x00, 0x33, 0xb1, 0xe5, 0xcf, 0x11, 0x89, 0xf4, 0x00, 0xa0,
> 0xc9, 0x03, 0x49, 0xcb
> -};
> +static int asf_read_unknown(AVFormatContext *s, const GUIDParseTable *g)
> +{
> + ASFContext *asf = s->priv_data;
> + AVIOContext *pb = s->pb;
> + uint64_t size = avio_rl64(pb);
> + int ret;
> +
> + if (asf->is_header)
> + asf->unknown_size = size;
> + asf->is_header = 0;
> + if (!g->is_subobject) {
> + if (!(ret = strcmp(g->name, "Header Extension")))
> + avio_skip(pb, 22); // skip reserved fields and Data Size
> + if ((ret = detect_unknown_subobject(s, asf->unknown_offset,
> + asf->unknown_size)) < 0)
> + return ret;
> + } else
> + avio_skip(pb, size - 24);
>
> -#ifdef DEBUG
> -static const ff_asf_guid stream_bitrate_guid = { /* (http://get.to/sdp) */
> - 0xce, 0x75, 0xf8, 0x7b, 0x8d, 0x46, 0xd1, 0x11, 0x8d, 0x82, 0x00, 0x60,
> 0x97, 0xc9, 0xa2, 0xb2
> -};
> + return 0;
> +}
>
> -#define PRINT_IF_GUID(g, cmp) \
> - if (!ff_guidcmp(g, &cmp)) \
> - av_dlog(NULL, "(GUID: %s) ", # cmp)
> +static int get_asf_string(AVIOContext *pb, int maxlen, char *buf, int buflen)
> +{
> + char *q = buf;
> + int ret = 0;
> + if (buflen <= 0)
> + return AVERROR(EINVAL);
> + while (ret + 1 < maxlen) {
> + uint8_t tmp;
> + uint32_t ch;
> + GET_UTF16(ch, (ret += 2) <= maxlen ? avio_rl16(pb) : 0, break;);
> + PUT_UTF8(ch, tmp, if (q - buf < buflen - 1) *q++ = tmp;)
> + }
> + *q = 0;
> +
> + return ret;
> +}
>
> -static void print_guid(ff_asf_guid *g)
> +static int asf_read_marker(AVFormatContext *s, const GUIDParseTable *g)
> {
> - int i;
> - PRINT_IF_GUID(g, ff_asf_header);
> - else PRINT_IF_GUID(g, ff_asf_file_header);
> - else PRINT_IF_GUID(g, ff_asf_stream_header);
> - else PRINT_IF_GUID(g, ff_asf_audio_stream);
> - else PRINT_IF_GUID(g, ff_asf_audio_conceal_none);
> - else PRINT_IF_GUID(g, ff_asf_video_stream);
> - else PRINT_IF_GUID(g, ff_asf_video_conceal_none);
> - else PRINT_IF_GUID(g, ff_asf_command_stream);
> - else PRINT_IF_GUID(g, ff_asf_comment_header);
> - else PRINT_IF_GUID(g, ff_asf_codec_comment_header);
> - else PRINT_IF_GUID(g, ff_asf_codec_comment1_header);
> - else PRINT_IF_GUID(g, ff_asf_data_header);
> - else PRINT_IF_GUID(g, index_guid);
> - else PRINT_IF_GUID(g, ff_asf_head1_guid);
> - else PRINT_IF_GUID(g, ff_asf_head2_guid);
> - else PRINT_IF_GUID(g, ff_asf_my_guid);
> - else PRINT_IF_GUID(g, ff_asf_ext_stream_header);
> - else PRINT_IF_GUID(g, ff_asf_extended_content_header);
> - else PRINT_IF_GUID(g, ff_asf_ext_stream_embed_stream_header);
> - else PRINT_IF_GUID(g, ff_asf_ext_stream_audio_stream);
> - else PRINT_IF_GUID(g, ff_asf_metadata_header);
> - else PRINT_IF_GUID(g, ff_asf_metadata_library_header);
> - else PRINT_IF_GUID(g, ff_asf_marker_header);
> - else PRINT_IF_GUID(g, stream_bitrate_guid);
> - else PRINT_IF_GUID(g, ff_asf_language_guid);
> - else
> - av_dlog(NULL, "(GUID: unknown) ");
> - for (i = 0; i < 16; i++)
> - av_dlog(NULL, " 0x%02x,", (*g)[i]);
> - av_dlog(NULL, "}\n");
> + ASFContext *asf = s->priv_data;
> + AVIOContext *pb = s->pb;
> + uint64_t size = avio_rl64(pb);
> + int i, nb_markers, ret;
> + size_t len;
> + char name[1024];
> +
> + avio_skip(pb, 8);
> + avio_skip(pb, 8); // skip reserved GUID
> + nb_markers = avio_rl32(pb);
> + avio_skip(pb, 2); // skip reserved field
> + len = avio_rl16(pb);
> + for (i = 0; i < len; i++)
> + avio_skip(pb, 1);
> +
> + for (i = 0; i < nb_markers; i++) {
> + int64_t pts;
> +
> + avio_skip(pb, 8);
> + pts = avio_rl64(pb);
> + pts -= asf->preroll * 10000;
> + avio_skip(pb, 2); // entry length
> + avio_skip(pb, 4); // send time
> + avio_skip(pb, 4); // flags
> + len = avio_rl32(pb);
> +
> + if ((ret = avio_get_str16le(pb, len, name,
> + sizeof(name))) < len)
> + avio_skip(pb, len - ret);
> + avpriv_new_chapter(s, i, (AVRational) { 1, 10000000 }, pts,
> + AV_NOPTS_VALUE, name);
> + }
> + align_position(pb, asf->offset, size);
> +
> + return 0;
> }
> -#undef PRINT_IF_GUID
> -#else
> -#define print_guid(g)
> -#endif
>
> -static int asf_probe(AVProbeData *pd)
> +static int asf_read_metadata(AVFormatContext *s, const char *title, uint16_t
> len,
> + unsigned char *ch, uint16_t buflen)
> {
> - /* check file header */
> - if (!ff_guidcmp(pd->buf, &ff_asf_header))
> - return AVPROBE_SCORE_MAX;
> - else
> - return 0;
> + AVIOContext *pb = s->pb;
> +
> + avio_get_str16le(pb, len, ch, buflen);
> + if (av_dict_set(&s->metadata, title, ch, 0) < 0)
> + av_log(s, AV_LOG_WARNING, "av_dict_set failed.\n");
> +
> + return 0;
> +}
> +
> +static int asf_read_value(AVFormatContext *s, uint8_t *name,
> + uint16_t val_len, int type, AVDictionary **met)
Broken indentation.
> +{
> + int ret;
> + uint8_t *value = av_malloc(val_len);
The malloc is easy to miss there, it would be better to have it after
the variable declarations.
> + uint16_t buflen = 2 * val_len + 1;
> + AVIOContext *pb = s->pb;
> +
> + if (!value)
> + return AVERROR(ENOMEM);
> + if (type == ASF_UNICODE) {
> + // get_asf_string reads UTF-16 and converts it to UTF-8 which needs
> longer buffer
> + if ((ret = get_asf_string(pb, val_len, value, buflen)) < 0)
The buffer size is wrong.
> + return ret;
memleak
same below
> + if (av_dict_set(met, name, value, 0) < 0)
> + av_log(s, AV_LOG_WARNING, "av_dict_set failed.\n");
> + } else {
> + char buf[32];
> + if ((ret = avio_read(pb, value, val_len)) < 0)
> + return ret;
> + if (ret < 2 * val_len)
> + value[ret] = '\0';
> + else
> + value[2 * val_len - 1] = '\0';
> + snprintf(buf, sizeof(buf), "%s", value);
> + if (av_dict_set(met, name, buf, 0) < 0)
> + av_log(s, AV_LOG_WARNING, "av_dict_set failed.\n");
> + }
> + av_freep(&value);
> +
> + return 0;
> }
>
> -/* size of type 2 (BOOL) is 32bit for "Extended Content Description Object"
> - * but 16 bit for "Metadata Object" and "Metadata Library Object" */
> -static int get_value(AVIOContext *pb, int type, int type2_size)
> +static int asf_read_generic_value(AVFormatContext *s, int8_t *name, int type)
> {
> + AVIOContext *pb = s->pb;
> + uint64_t value;
> + char buf[32];
> +
> switch (type) {
> - case 2:
> - return (type2_size == 32) ? avio_rl32(pb) : avio_rl16(pb);
> - case 3:
> - return avio_rl32(pb);
> - case 4:
> - return avio_rl64(pb);
> - case 5:
> - return avio_rl16(pb);
> + case ASF_BOOL:
> + value = avio_rl32(pb);
> + break;
> + case ASF_DWORD:
> + value = avio_rl32(pb);
> + break;
> + case ASF_QWORD:
> + value = avio_rl64(pb);
> + break;
> + case ASF_WORD:
> + value = avio_rl16(pb);
> + break;
> default:
> - return INT_MIN;
> + return AVERROR_INVALIDDATA;
> }
> + snprintf(buf, sizeof(buf), "%"PRIu64, value);
> + if (av_dict_set(&s->metadata, name, buf, 0) < 0)
> + av_log(s, AV_LOG_WARNING, "av_dict_set failed.\n");
> +
> + return 0;
> }
>
> /* MSDN claims that this should be "compatible with the ID3 frame, APIC",
> * but in reality this is only loosely similar */
> static int asf_read_picture(AVFormatContext *s, int len)
> {
> + ASFContext *asf = s->priv_data;
> AVPacket pkt = { 0 };
> const CodecMime *mime = ff_id3v2_mime_tags;
> enum AVCodecID id = AV_CODEC_ID_NONE;
> @@ -193,6 +331,7 @@ static int asf_read_picture(AVFormatContext *s, int len)
> uint8_t *desc = NULL;
> AVStream *st = NULL;
> int ret, type, picsize, desc_len;
> + ASFStream *asf_st;
>
> /* type + picsize + mime + desc */
> if (len < 1 + 4 + 2 + 2) {
> @@ -249,19 +388,29 @@ static int asf_read_picture(AVFormatContext *s, int len)
> ret = AVERROR(ENOMEM);
> goto fail;
> }
> + asf->asf_st[asf->nb_streams] = av_mallocz(sizeof(*asf_st));
> + asf_st = asf->asf_st[asf->nb_streams];
> + if (!asf_st)
> + return AVERROR(ENOMEM);
> + asf_st->pkt = NULL;
> +
> st->disposition |= AV_DISPOSITION_ATTACHED_PIC;
> - st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
> + st->codec->codec_type = asf_st->type = AVMEDIA_TYPE_VIDEO;
> st->codec->codec_id = id;
> st->attached_pic = pkt;
> - st->attached_pic.stream_index = st->index;
> + st->attached_pic.stream_index = asf_st->index = st->index;
> st->attached_pic.flags |= AV_PKT_FLAG_KEY;
>
> - if (*desc)
> - av_dict_set(&st->metadata, "title", desc, AV_DICT_DONT_STRDUP_VAL);
> - else
> + asf->nb_streams++;
> +
> + if (*desc) {
> + if (av_dict_set(&st->metadata, "title", desc,
> AV_DICT_DONT_STRDUP_VAL) < 0)
> + av_log(s, AV_LOG_WARNING, "av_dict_set failed.\n");
> + } else
> av_freep(&desc);
>
> - av_dict_set(&st->metadata, "comment", ff_id3v2_picture_types[type], 0);
> + if (av_dict_set(&st->metadata, "comment", ff_id3v2_picture_types[type],
> 0) < 0)
> + av_log(s, AV_LOG_WARNING, "av_dict_set failed.\n");
>
> return 0;
>
> @@ -281,1236 +430,1206 @@ static void get_id3_tag(AVFormatContext *s, int len)
> ff_id3v2_free_extra_meta(&id3v2_extra_meta);
> }
>
> -static void get_tag(AVFormatContext *s, const char *key, int type, int len,
> int type2_size)
> +static int process_metadata(AVFormatContext *s, uint8_t *name, uint16_t
> val_len,
> + uint16_t type, AVDictionary **met)
> {
> - ASFContext *asf = s->priv_data;
> - char *value = NULL;
> - int64_t off = avio_tell(s->pb);
> -
> - if ((unsigned)len >= (UINT_MAX - 1) / 2)
> - return;
> -
> - if (!asf->export_xmp && !strncmp(key, "xmp", 3))
> - goto finish;
> + int ret;
> + ff_asf_guid guid;
>
> - value = av_malloc(2 * len + 1);
> - if (!value)
> - goto finish;
> -
> - if (type == 0) { // UTF16-LE
> - avio_get_str16le(s->pb, len, value, 2 * len + 1);
> - } else if (type == 1) { // byte array
> - if (!strcmp(key, "WM/Picture")) { // handle cover art
> - asf_read_picture(s, len);
> - } else if (!strcmp(key, "ID3")) { // handle ID3 tag
> - get_id3_tag(s, len);
> - } else {
> - av_log(s, AV_LOG_VERBOSE, "Unsupported byte array in tag %s.\n",
> key);
> + if (val_len) {
> + switch (type) {
> + case ASF_UNICODE:
> + asf_read_value(s, name, val_len, type, met);
> + break;
> + case ASF_BYTE_ARRAY:
> + if (!strcmp(name, "WM/Picture")) // handle cover art
> + asf_read_picture(s, val_len);
> + else if (!strcmp(name, "ID3")) // handle ID3 tag
> + get_id3_tag(s, val_len);
> + else
> + asf_read_value(s, name, val_len, type, met);
> + break;
> + case ASF_GUID:
> + ff_get_guid(s->pb, &guid);
> + break;
> + default:
> + if ((ret = asf_read_generic_value(s, name, type)) < 0)
> + return ret;
> + break;
> }
> - goto finish;
> - } else if (type > 1 && type <= 5) { // boolean or DWORD or QWORD or WORD
> - uint64_t num = get_value(s->pb, type, type2_size);
> - snprintf(value, len, "%"PRIu64, num);
> - } else if (type == 6) { // (don't) handle GUID
> - av_log(s, AV_LOG_DEBUG, "Unsupported GUID value in tag %s.\n", key);
> - goto finish;
> - } else {
> - av_log(s, AV_LOG_DEBUG,
> - "Unsupported value type %d in tag %s.\n", type, key);
> - goto finish;
> }
> - if (*value)
> - av_dict_set(&s->metadata, key, value, 0);
> + av_freep(&name);
>
> -finish:
> - av_freep(&value);
> - avio_seek(s->pb, off + len, SEEK_SET);
> + return 0;
> }
>
> -static int asf_read_file_properties(AVFormatContext *s, int64_t size)
> +static int asf_read_ext_content(AVFormatContext *s, const GUIDParseTable *g)
> {
> - ASFContext *asf = s->priv_data;
> - AVIOContext *pb = s->pb;
> + ASFContext *asf = s->priv_data;
> + AVIOContext *pb = s->pb;
> + uint64_t size = avio_rl64(pb);
> + uint16_t nb_desc = avio_rl16(pb);
> + int i, ret;
>
> - ff_get_guid(pb, &asf->hdr.guid);
> - asf->hdr.file_size = avio_rl64(pb);
> - asf->hdr.create_time = avio_rl64(pb);
> - avio_rl64(pb); /* number of packets */
> - asf->hdr.play_time = avio_rl64(pb);
> - asf->hdr.send_time = avio_rl64(pb);
> - asf->hdr.preroll = avio_rl32(pb);
> - asf->hdr.ignore = avio_rl32(pb);
> - asf->hdr.flags = avio_rl32(pb);
> - asf->hdr.min_pktsize = avio_rl32(pb);
> - asf->hdr.max_pktsize = avio_rl32(pb);
> - if (asf->hdr.min_pktsize >= (1U << 29))
> - return AVERROR_INVALIDDATA;
> - asf->hdr.max_bitrate = avio_rl32(pb);
> - s->packet_size = asf->hdr.max_pktsize;
> + for (i = 0; i < nb_desc; i++) {
> + uint16_t name_len, type, val_len;
> + uint8_t *name = NULL;
>
> + name_len = avio_rl16(pb);
> + if (!name_len)
> + return AVERROR_INVALIDDATA;
> + name = av_malloc(name_len);
> + if (!name)
> + return AVERROR(ENOMEM);
> + avio_get_str16le(pb, name_len, name,
> + name_len);
> + type = avio_rl16(pb);
> + val_len = avio_rl16(pb);
> +
> + if ((ret = process_metadata(s, name, val_len, type, &s->metadata)) <
> 0)
> + return ret;
> + }
> +
> + align_position(pb, asf->offset, size);
> return 0;
> }
>
> -static int asf_read_stream_properties(AVFormatContext *s, int64_t size)
> +static AVStream *find_stream(AVFormatContext *s, ASFContext *asf, uint16_t
> st_num)
> {
> - ASFContext *asf = s->priv_data;
> - AVIOContext *pb = s->pb;
> - AVStream *st;
> - ASFStream *asf_st;
> - ff_asf_guid g;
> - enum AVMediaType type;
> - int type_specific_size, sizeX;
> - unsigned int tag1;
> - int64_t pos1, pos2, start_time;
> - int test_for_ext_stream_audio, is_dvr_ms_audio = 0;
> -
> - if (s->nb_streams == ASF_MAX_STREAMS) {
> - av_log(s, AV_LOG_ERROR, "too many streams\n");
> - return AVERROR(EINVAL);
> - }
> -
> - pos1 = avio_tell(pb);
> -
> - st = avformat_new_stream(s, NULL);
> - if (!st)
> - return AVERROR(ENOMEM);
> - avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
> - asf_st = av_mallocz(sizeof(ASFStream));
> - if (!asf_st)
> - return AVERROR(ENOMEM);
> - st->priv_data = asf_st;
> - st->start_time = 0;
> - start_time = asf->hdr.preroll;
> -
> - asf_st->stream_language_index = 128; // invalid stream index means no
> language info
> -
> - if (!(asf->hdr.flags & 0x01)) { // if we aren't streaming...
> - st->duration = asf->hdr.play_time /
> - (10000000 / 1000) - start_time;
> - }
> - ff_get_guid(pb, &g);
> + AVStream *st = NULL;
> + int i;
>
> - test_for_ext_stream_audio = 0;
> - if (!ff_guidcmp(&g, &ff_asf_audio_stream)) {
> - type = AVMEDIA_TYPE_AUDIO;
> - } else if (!ff_guidcmp(&g, &ff_asf_video_stream)) {
> - type = AVMEDIA_TYPE_VIDEO;
> - } else if (!ff_guidcmp(&g, &ff_asf_jfif_media)) {
> - type = AVMEDIA_TYPE_VIDEO;
> - st->codec->codec_id = AV_CODEC_ID_MJPEG;
> - } else if (!ff_guidcmp(&g, &ff_asf_command_stream)) {
> - type = AVMEDIA_TYPE_DATA;
> - } else if (!ff_guidcmp(&g, &ff_asf_ext_stream_embed_stream_header)) {
> - test_for_ext_stream_audio = 1;
> - type = AVMEDIA_TYPE_UNKNOWN;
> - } else {
> - return -1;
> - }
> - ff_get_guid(pb, &g);
> - avio_skip(pb, 8); /* total_size */
> - type_specific_size = avio_rl32(pb);
> - avio_rl32(pb);
> - st->id = avio_rl16(pb) & 0x7f; /* stream id */
> - // mapping of asf ID to AV stream ID;
> - asf->asfid2avid[st->id] = s->nb_streams - 1;
> -
> - avio_rl32(pb);
> -
> - if (test_for_ext_stream_audio) {
> - ff_get_guid(pb, &g);
> - if (!ff_guidcmp(&g, &ff_asf_ext_stream_audio_stream)) {
> - type = AVMEDIA_TYPE_AUDIO;
> - is_dvr_ms_audio = 1;
> - ff_get_guid(pb, &g);
> - avio_rl32(pb);
> - avio_rl32(pb);
> - avio_rl32(pb);
> - ff_get_guid(pb, &g);
> - avio_rl32(pb);
> + for (i = 0; i < asf->nb_streams; i++) {
> + if (asf->asf_st[i]->stream_index == st_num) {
> + st = s->streams[asf->asf_st[i]->index];
> + break;
> }
> }
>
> - st->codec->codec_type = type;
> - if (type == AVMEDIA_TYPE_AUDIO) {
> - int ret = ff_get_wav_header(pb, st->codec, type_specific_size);
> - if (ret < 0)
> - return ret;
> - if (is_dvr_ms_audio) {
> - // codec_id and codec_tag are unreliable in dvr_ms
> - // files. Set them later by probing stream.
> - st->codec->codec_id = AV_CODEC_ID_PROBE;
> - st->codec->codec_tag = 0;
> - }
> - if (st->codec->codec_id == AV_CODEC_ID_AAC)
> - st->need_parsing = AVSTREAM_PARSE_NONE;
> - else
> - st->need_parsing = AVSTREAM_PARSE_FULL;
> - /* We have to init the frame size at some point .... */
> - pos2 = avio_tell(pb);
> - if (size >= (pos2 + 8 - pos1 + 24)) {
> - asf_st->ds_span = avio_r8(pb);
> - asf_st->ds_packet_size = avio_rl16(pb);
> - asf_st->ds_chunk_size = avio_rl16(pb);
> - avio_rl16(pb); // ds_data_size
> - avio_r8(pb); // ds_silence_data
> - }
> - if (asf_st->ds_span > 1) {
> - if (!asf_st->ds_chunk_size ||
> - (asf_st->ds_packet_size / asf_st->ds_chunk_size <= 1) ||
> - asf_st->ds_packet_size % asf_st->ds_chunk_size)
> - asf_st->ds_span = 0; // disable descrambling
> - }
> - } else if (type == AVMEDIA_TYPE_VIDEO &&
> - size - (avio_tell(pb) - pos1 + 24) >= 51) {
> - avio_rl32(pb);
> - avio_rl32(pb);
> - avio_r8(pb);
> - avio_rl16(pb); /* size */
> - sizeX = avio_rl32(pb); /* size */
> - st->codec->width = avio_rl32(pb);
> - st->codec->height = avio_rl32(pb);
> - /* not available for asf */
> - avio_rl16(pb); /* panes */
> - st->codec->bits_per_coded_sample = avio_rl16(pb); /* depth */
> - tag1 = avio_rl32(pb);
> - avio_skip(pb, 20);
> - if (sizeX > 40) {
> - st->codec->extradata_size = sizeX - 40;
> - st->codec->extradata = av_mallocz(st->codec->extradata_size
> +
> -
> FF_INPUT_BUFFER_PADDING_SIZE);
> - avio_read(pb, st->codec->extradata, st->codec->extradata_size);
> - }
> -
> - /* Extract palette from extradata if bpp <= 8 */
> - /* This code assumes that extradata contains only palette */
> - /* This is true for all paletted codecs implemented in libavcodec */
> - if (st->codec->extradata_size && (st->codec->bits_per_coded_sample
> <= 8)) {
> -#if HAVE_BIGENDIAN
> - int i;
> - for (i = 0; i < FFMIN(st->codec->extradata_size, AVPALETTE_SIZE)
> / 4; i++)
> - asf_st->palette[i] = av_bswap32(((uint32_t
> *)st->codec->extradata)[i]);
> -#else
> - memcpy(asf_st->palette, st->codec->extradata,
> - FFMIN(st->codec->extradata_size, AVPALETTE_SIZE));
> -#endif
> - asf_st->palette_changed = 1;
> - }
> + return st;
> +}
>
> - st->codec->codec_tag = tag1;
> - st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag1);
> - if (tag1 == MKTAG('D', 'V', 'R', ' ')) {
> - st->need_parsing = AVSTREAM_PARSE_FULL;
> - /* issue658 contains wrong w/h and MS even puts a fake seq header
> - * with wrong w/h in extradata while a correct one is in the
> stream.
> - * maximum lameness */
> - st->codec->width =
> - st->codec->height = 0;
> - av_freep(&st->codec->extradata);
> - st->codec->extradata_size = 0;
> +static int asf_read_metadata_obj(AVFormatContext *s, const GUIDParseTable *g)
> +{
> + ASFContext *asf = s->priv_data;
> + AVIOContext *pb = s->pb;
> + AVDictionary **met = NULL;
> + uint64_t size = avio_rl64(pb);
> + uint16_t nb_recs = avio_rl16(pb); // number of records in the
> Description Records list
> + AVStream *st = NULL;
> + int i, ret;
> +
> + for (i = 0; i < nb_recs; i++) {
> + uint16_t name_len, buflen, type, val_len, st_num = 0;
> + uint8_t *name = NULL;
> +
> + avio_skip(pb, 2); // skip reserved field
> + st_num = avio_rl16(pb);
> + name_len = avio_rl16(pb);
> + buflen = 2 * name_len + 1;
> + if (!name_len)
> + break;
> + type = avio_rl16(pb);
> + val_len = avio_rl32(pb);
> + name = av_malloc(name_len);
> + if (!name)
> + return AVERROR(ENOMEM);
> + avio_get_str16le(pb, name_len, name,
> + buflen);
> + find_stream(s, asf, st_num);
Seriously? How about actually testing this stuff once in a while.
Also, this won't work if this object is stored before the streams
properties.
> + if (st)
> + met = &st->metadata;
> + else {
> + av_log(s, AV_LOG_WARNING, "Can't find matching stream for the
> processed "
> + "metadata"".\n");
> + met = &s->metadata;
> }
> - if (st->codec->codec_id == AV_CODEC_ID_H264)
> - st->need_parsing = AVSTREAM_PARSE_FULL_ONCE;
> + if ((ret = process_metadata(s, name, val_len, type, met)) < 0)
> + break;
> }
> - pos2 = avio_tell(pb);
> - avio_skip(pb, size - (pos2 - pos1 + 24));
>
> + align_position(pb, asf->offset, size);
> return 0;
> }
>
> -static int asf_read_ext_stream_properties(AVFormatContext *s, int64_t size)
> +static int asf_read_content_desc(AVFormatContext *s, const GUIDParseTable *g)
> {
> ASFContext *asf = s->priv_data;
> AVIOContext *pb = s->pb;
> - ff_asf_guid g;
> - int ext_len, payload_ext_ct, stream_ct, i;
> - uint32_t leak_rate, stream_num;
> - unsigned int stream_languageid_index;
> -
> - avio_rl64(pb); // starttime
> - avio_rl64(pb); // endtime
> - leak_rate = avio_rl32(pb); // leak-datarate
> - avio_rl32(pb); // bucket-datasize
> - avio_rl32(pb); // init-bucket-fullness
> - avio_rl32(pb); // alt-leak-datarate
> - avio_rl32(pb); // alt-bucket-datasize
> - avio_rl32(pb); // alt-init-bucket-fullness
> - avio_rl32(pb); // max-object-size
> - avio_rl32(pb); // flags
> (reliable,seekable,no_cleanpoints?,resend-live-cleanpoints, rest of bits
> reserved)
> - stream_num = avio_rl16(pb); // stream-num
> -
> - stream_languageid_index = avio_rl16(pb); // stream-language-id-index
> - if (stream_num < 128)
> - asf->streams[stream_num].stream_language_index =
> stream_languageid_index;
> -
> - avio_rl64(pb); // avg frametime in 100ns units
> - stream_ct = avio_rl16(pb); // stream-name-count
> - payload_ext_ct = avio_rl16(pb); // payload-extension-system-count
> -
> - if (stream_num < 128)
> - asf->stream_bitrates[stream_num] = leak_rate;
> -
> - for (i = 0; i < stream_ct; i++) {
> - avio_rl16(pb);
> - ext_len = avio_rl16(pb);
> - avio_skip(pb, ext_len);
> + int i;
> + static const char *const titles[] =
> + { "Title", "Author", "Copyright", "Description", "Rate" };
> + uint16_t len[5], buflen = 0;
> + uint8_t *ch;
> + uint64_t size = avio_rl64(pb);
> +
> + for (i = 0; i < 5; i++) {
> + len[i] = avio_rl16(pb);
> + // utf8 string should be <= 2 * utf16 string, extra byte for the
> terminator
> + buflen = 2 * len[i] + 1;
This does not look right.
> }
> -
> - for (i = 0; i < payload_ext_ct; i++) {
> - ff_get_guid(pb, &g);
> - avio_skip(pb, 2);
> - ext_len = avio_rl32(pb);
> - avio_skip(pb, ext_len);
> + if (buflen) {
Can buflen actually be zero?
> + ch = av_malloc(sizeof(buflen));
sizeof?
> + if (!ch)
> + return(AVERROR(ENOMEM));
> + for (i = 0; i < 5; i++)
> + asf_read_metadata(s, titles[i], len[i], ch, buflen);
> + av_freep(&ch);
> }
> + align_position(pb, asf->offset, size);
>
> return 0;
> }
>
> -static int asf_read_content_desc(AVFormatContext *s, int64_t size)
> +static int asf_read_properties(AVFormatContext *s, const GUIDParseTable *g)
> {
> + ASFContext *asf = s->priv_data;
> AVIOContext *pb = s->pb;
> - int len1, len2, len3, len4, len5;
> -
> - len1 = avio_rl16(pb);
> - len2 = avio_rl16(pb);
> - len3 = avio_rl16(pb);
> - len4 = avio_rl16(pb);
> - len5 = avio_rl16(pb);
> - get_tag(s, "title", 0, len1, 32);
> - get_tag(s, "author", 0, len2, 32);
> - get_tag(s, "copyright", 0, len3, 32);
> - get_tag(s, "comment", 0, len4, 32);
> - avio_skip(pb, len5);
> + uint64_t creation_time;
> + struct tm tmbuf;
> + struct tm *tm;
> + char buf[64] = {0};
Those variables should be moved into the block where they are used. And
there does not seem to be any need to zero all of buf.
> +static int asf_read_ext_stream_properties(AVFormatContext *s, const
> GUIDParseTable *g)
> {
> - AVIOContext *pb = s->pb;
> ASFContext *asf = s->priv_data;
> - int j, ret;
> - int stream_count = avio_rl16(pb);
> - for (j = 0; j < stream_count; j++) {
> - char lang[6];
> - unsigned int lang_len = avio_r8(pb);
> - if ((ret = avio_get_str16le(pb, lang_len, lang,
> - sizeof(lang))) < lang_len)
> - avio_skip(pb, lang_len - ret);
> - if (j < 128)
> - av_strlcpy(asf->stream_languages[j], lang,
> - sizeof(*asf->stream_languages));
> + AVIOContext *pb = s->pb;
> + AVStream *st = NULL;
> + ff_asf_guid guid;
> + uint16_t nb_st_name, nb_pay_exts, st_num, lang_idx;
> + int i, ret;
> + uint32_t bitrate;
> + uint64_t start_time, end_time, time_per_frame;
> + uint64_t size = avio_rl64(pb);
> +
> + start_time = avio_rl64(pb);
> + end_time = avio_rl64(pb);
> + bitrate = avio_rl32(pb);
> + avio_skip(pb, 28); // skip some unused values
> + st_num = avio_rl16(pb);
> + st_num &= ASF_STREAM_NUM;
> + lang_idx = avio_rl16(pb); // Stream Language ID Index
> + for (i = 0; i < asf->nb_streams; i++) {
> + if (st_num == asf->asf_st[i]->stream_index) {
> + st = s->streams[asf->asf_st[i]->index];
> + asf->asf_st[i]->lang_idx = lang_idx;
> + break;
> + }
> + }
> + time_per_frame = avio_rl64(pb); // average time per frame
> + if (st) {
> + st->start_time = start_time;
> + st->duration = end_time - start_time;
> + st->codec->bit_rate = bitrate;
> + st->avg_frame_rate.num = 10000000;
> + st->avg_frame_rate.den = time_per_frame;
> + }
> + nb_st_name = avio_rl16(pb);
> + nb_pay_exts = avio_rl16(pb);
> + for (i = 0; i < nb_st_name; i++) {
> + uint16_t len;
> +
> + avio_rl16(pb); // Language ID Index
> + len = avio_rl16(pb);
> + avio_skip(pb, len);
> + }
> +
> + for (i = 0; i < nb_pay_exts; i++) {
> + uint32_t len;
> + avio_skip(pb, 16); // Extension System ID
> + avio_skip(pb, 2); // Extension Data Size
> + len = avio_rl32(pb);
> + avio_skip(pb, len);
> }
>
> + if ((ret = ff_get_guid(pb, &guid)) < 0) {
> + align_position(pb, asf->offset, size);
> +
> + return 0;
> + }
> +
> + g = find_guid(guid);
> + if (g && !(strcmp(g->name, "Stream Properties"))) {
Will this actually work? Because it seems to me it will fail horribly.
> + if ((ret = g->read_object(s, g)) < 0)
> + return ret;
> + }
> +
> + align_position(pb, asf->offset, size);
> return 0;
> }
>
> -static int asf_read_metadata(AVFormatContext *s, int64_t size)
> +static int asf_read_language_list(AVFormatContext *s, const GUIDParseTable
> *g)
> {
> - AVIOContext *pb = s->pb;
> - ASFContext *asf = s->priv_data;
> - int n, stream_num, name_len, value_len;
> - int ret, i;
> - n = avio_rl16(pb);
> -
> - for (i = 0; i < n; i++) {
> - char name[1024];
> - int value_type;
> -
> - avio_rl16(pb); // lang_list_index
> - stream_num = avio_rl16(pb);
> - name_len = avio_rl16(pb);
> - value_type = avio_rl16(pb); /* value_type */
> - value_len = avio_rl32(pb);
> -
> - if ((ret = avio_get_str16le(pb, name_len, name, sizeof(name))) <
> name_len)
> - avio_skip(pb, name_len - ret);
> - av_dlog(s, "%d stream %d name_len %2d type %d len %4d <%s>\n",
> - i, stream_num, name_len, value_type, value_len, name);
> -
> - if (!strcmp(name, "AspectRatioX")){
> - int aspect_x = get_value(s->pb, value_type, 16);
> - if(stream_num < 128)
> - asf->dar[stream_num].num = aspect_x;
> - } else if(!strcmp(name, "AspectRatioY")){
> - int aspect_y = get_value(s->pb, value_type, 16);
> - if(stream_num < 128)
> - asf->dar[stream_num].den = aspect_y;
> - } else {
> - get_tag(s, name, value_type, value_len, 16);
> + ASFContext *asf = s->priv_data;
> + AVIOContext *pb = s->pb;
> + int i, ret;
> + uint64_t size = avio_rl64(pb);
> + uint16_t nb_langs = avio_rl16(pb);
> +
> + for (i = 0; i < nb_langs; i++) {
> + size_t len;
> + len = avio_r8(pb);
> + if (!len)
> + len = 6;
> + // len is number of unicode characters - 2 bytes for each char
The comment does not match the code -- one of them is probably wrong.
> +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;
Won't this cause an inifinet loop if there's no data object in the file?
I believe I already asked this once without an answer.
> }
> + 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)
So, if it breaks immediately after data is reached, when does the index
get read?
--
Anton Khirnov
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel