[Ffmpeg-devel] [PATCH] FLV decoder metadata reading

Michael Niedermayer michaelni
Fri Dec 8 21:49:30 CET 2006


Hi

On Fri, Dec 08, 2006 at 12:41:32AM -0800, Allan Hsu wrote:
> This is my second try at the FLV decoder metadata patch. I've tried to
> adhere to the submission guidelines and Michael's suggestions. Please
> let me know if this patch is acceptable or if I still need to do
> anything before it can be merged in SVN.
> 
> 	-Allan
> -- 
> Allan Hsu <allan at counterpop dot net>
> 1E64 E20F 34D9 CBA7 1300 1457 AC37 CBBB 0E92 C779

> Index: libavformat/flvdec.c
> ===================================================================
> --- libavformat/flvdec.c	(revision 7260)
> +++ libavformat/flvdec.c	(working copy)
> @@ -27,6 +27,317 @@
>  #include "avformat.h"
>  #include "flv.h"
>  
> +typedef struct {
> +    int hasVideo;
> +    int hasAudio;
> +    int videocodecid;
> +    int audiocodecid;
> +    int width;
> +    int height;
> +    int samplerate;
> +    int samplesize;
> +    int isStereo;
> +} FLVDemuxContext;
> +
> +typedef struct {
> +    unsigned int pos;
> +    unsigned int prev_tag_size;
> +    unsigned int next_pos;
> +    int type;
> +    int body_size;
> +    int pts;
> +} FLVTagInfo;
> +
> +inline static void create_vp6_extradata(AVStream *stream) {
> +    if(stream->codec->extradata_size != 1) {
> +        stream->codec->extradata_size = 1;
> +        stream->codec->extradata = av_malloc(1);
> +    }
> +}
> +
> +static int amf_skip_object(ByteIOContext *ioc, AMFDataType *type) {
> +    AMFDataType objectType;
> +
> +    objectType = (type != NULL ? *type : get_byte(ioc));
> +    switch(objectType) {
> +        case      AMF_DATA_TYPE_NUMBER:
> +            url_fskip(ioc, 8); break; //double precision float
> +        case        AMF_DATA_TYPE_BOOL:
> +            url_fskip(ioc, 1); break; //byte
> +        case      AMF_DATA_TYPE_STRING:
> +            url_fskip(ioc, get_be16(ioc)); break;
> +        case      AMF_DATA_TYPE_OBJECT: {
> +            unsigned int keylen;
> +
> +            for(keylen = get_be16(ioc); keylen != 0; keylen = get_be16(ioc)) {
> +                url_fskip(ioc, keylen); //skip key string
> +                if(amf_skip_object(ioc, NULL) < 0) //skip the following object
> +                    return -1; //if we couldn't skip, bomb out.
> +            }

while(keylen = get_be16(ioc)){
}

is slightly simpler (no keylen = get_be16(ioc) duplication)


> +            if(get_byte(ioc) != AMF_END_OF_OBJECT)
> +                return -1;
> +        }
> +            break;
> +        case        AMF_DATA_TYPE_NULL:
> +        case   AMF_DATA_TYPE_UNDEFINED:
> +        case AMF_DATA_TYPE_UNSUPPORTED:
> +            break; //these take up no additional space
> +        case       AMF_DATA_TYPE_ARRAY: {
> +            unsigned int arraylen, i;
> +
> +            arraylen = get_be32(ioc);
> +            for(i = 0; i < arraylen; i++)
> +                amf_skip_object(ioc, NULL);
> +        }
> +            break;
> +        case        AMF_DATA_TYPE_DATE:
> +            url_fskip(ioc, 8 + 2); //timestamp (double) and UTC offset (int16)
> +            break;
> +        default: //unsupported, we couldn't skip.
> +            return -1;
> +    }
> +
> +    return 0;
> +}

is amf_skip_object() really needed? isnt a simple loop which reads
double/string/bool/date and either assigns it to something or not
depending on what it was enough?


> +
> +static int amf_get_object(ByteIOContext *ioc, AMFDataType type, void *dest) {
> +    AMFDataType actualType = get_byte(ioc);
> +
> +    if(actualType != type) {
> +        //type was not the one we expected; skip object, don't touch dest, return error.
> +        amf_skip_object(ioc, &actualType);
> +        return -1;
> +    }
> +
> +    //we currently only need these two types for metadata parsing.
> +    switch(type) {
> +        case AMF_DATA_TYPE_NUMBER:
> +            *(double *)dest = av_int2dbl(get_be64(ioc));
> +            break;
> +        case   AMF_DATA_TYPE_BOOL:
> +            *(unsigned char *)dest = get_byte(ioc);
> +            break;
> +        default:
> +            return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int amf_get_string(ByteIOContext *ioc, char *buffer, int buffsize) {
> +    int length;
> +
> +    length = get_be16(ioc);
> +    if(length > buffsize)
> +        return -1; //string will not fit in buffer
> +
> +    get_buffer(ioc, buffer, length);
> +
> +    buffer[length] = '\0';
> +
> +    return length;
> +}

shouldnt it rather be?

if(length >= buffsize){
    url_fskip(ioc, length);
    return -1;
}

[...]

>  static int flv_read_header(AVFormatContext *s,
>                             AVFormatParameters *ap)
>  {
> +    FLVTagInfo taginfo;
> +    FLVDemuxContext *context = s->priv_data;
>      int offset, flags, size;
>  
> -    s->ctx_flags |= AVFMTCTX_NOHEADER; //ok we have a header but theres no fps, codec type, sample_rate, ...
> -
>      url_fskip(&s->pb, 4);
>      flags = get_byte(&s->pb);
>  
>      offset = get_be32(&s->pb);
>  
> +    if(flags & FLV_HEADER_FLAG_HASVIDEO)
> +        context->hasVideo = 1;
> +    if(flags & FLV_HEADER_FLAG_HASAUDIO)
> +        context->hasAudio = 1;
> +
> +    //0 is a valid audio codec id, so set it to something that will cause a validation error if it does not get set in flv_read_metabody
> +    context->audiocodecid = -1;
> +
> +    if(   flv_read_tag_header(&s->pb, &taginfo)  < 0 || taginfo.type != FLV_TAG_TYPE_META
> +       || flv_read_metabody(s, taginfo.next_pos) < 0 || flv_validate_context(s) < 0
> +       || flv_create_streams(s) < 0) {
> +        //error reading first tag header, first tag is not metadata, or metadata incomplete.
> +        s->ctx_flags |= AVFMTCTX_NOHEADER;
> +

isnt it simpler to simple read all the metadata in flv_read_packet() and just
call flv_read_packet() from flv_read_header() once if needed?



>      if(!url_is_streamed(&s->pb)){
>          const int fsize= url_fsize(&s->pb);
>          url_fseek(&s->pb, fsize-4, SEEK_SET);
> @@ -62,7 +387,8 @@
>          }
>      }
>  
> -    url_fseek(&s->pb, offset, SEEK_SET);
> +        url_fseek(&s->pb, offset, SEEK_SET);

this seeks backward or? if so its a matter of luck if it works, if theres
too much metadata then it will fail if the stream is not "seekable"

[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

If you really think that XML is the answer, then you definitly missunderstood
the question -- Attila Kinali




More information about the ffmpeg-devel mailing list