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

Reply via email to