[FFmpeg-devel] [PATCH] support for flvtool2 "keyframes based" generated index in FLV format decoder

Kharkov Alexander kharkovalexander
Fri Jan 21 06:05:18 CET 2011


New patch attached, after processing comments.
Changes:
* structures, types, constants renamed to not mention flvtool2
* use av_mallocz for allocation to avoid unnecessary initialization
* free metadata index data when it is no longer required

On 21 January 2011 07:34, Kharkov Alexander <kharkovalexander at gmail.com> wrote:
> Thanks for review, agree with all your points, will fix and resent.
> Details:
> * "keyframes" metatag does not stated in FLV specs but often
> recommeded, I found at least one more free tool which inject it FLVMDI
> * index data really can be free right after it is passed to av_add_index_entry
> * code spread all over just because I want to touch exsting metadata
> parser behavior minmal (all this recursion, loops, etc.) that is why I
> did not create separate function which simply parse keyframes object
>
> On 20 January 2011 23:23, Vladimir Pantelic <vladoman at gmail.com> wrote:
>> Kharkov Alexander wrote:
>>>
>>> Hi,
>>>
>>> Attached patch parse flvtool2 metadata "keyframes
>>> (times/fileopstions)" tags and use this data
>>> as index at least for seek operations.
>>> It is required in case when mplayer which use this part of ffmpeg code
>>> attempts to play FLV file via HTTP for example.
>>> Existing implementation attempt to download ALL content up to seek point
>>> before it can play it, but it is unacceptable to download unneded data
>>> (whole file if you seek to the end)
>>> as in case of low bandwidth seek operation will 'never' end.
>>> With this patch 'mplayer' can seek fast in such situations using this
>>> index.
>>
>>> Index: libavformat/flvdec.c
>>> ===================================================================
>>> --- libavformat/flvdec.c ? ? ? ?(revision 26327)
>>> +++ libavformat/flvdec.c ? ? ? ?(working copy)
>>> @@ -30,10 +30,51 @@
>>> ?#include "avformat.h"
>>> ?#include "flv.h"
>>>
>>> +#define FLVTOOL2_KEYFRAMES_TAG "keyframes"
>>> +#define FLVTOOL2_TIMESTAMP_TAG "times"
>>> +#define FLVTOOL2_BYTEOFFSET_TAG "filepositions"
>>
>> is this really FLVTOOL2 specific? does it have to end up
>> in the tag/variable name?
>>
>>> +
>>> ?typedef struct {
>>> + ? ?int64_t byte_offset;
>>> + ? ?int64_t time_offset;
>>> +} FLVTool2KeyframesIndex;
>>
>> dito
>>
>>> +typedef struct {
>>> + ? ?int keyframes_object_parse;
>>> +
>>> + ? ?int filepositions_array_parse;
>>> + ? ?int fill_filepositions;
>>> + ? ?int num_filepositions_processed;
>>> +
>>> + ? ?int times_array_parse;
>>> + ? ?int fill_timestamps;
>>> + ? ?int num_timestamps_processed;
>>> +
>>> + ? ?int num_indexed_keyframes;
>>> +
>>> + ? ?FLVTool2KeyframesIndex *indexes;
>>> +} FLVTool2Index;
>>
>> dito
>>
>>> +typedef struct {
>>> ? ? ?int wrong_dts; ///< ?wrong dts due to negative cts
>>> + ? ?FLVTool2Index *flvtool2_index;
>>> ?} FLVContext;
>>>
>>> +static void init_flv_context(FLVContext* flv)
>>> +{
>>> + ? ?// initialize flvtool2 "keyframes" based index
>>> + ? ?flv->flvtool2_index = av_malloc(sizeof(FLVTool2Index) * 1);
>>> + ? ?flv->flvtool2_index->keyframes_object_parse = 0;
>>> + ? ?flv->flvtool2_index->filepositions_array_parse = 0;
>>> + ? ?flv->flvtool2_index->fill_filepositions = 0;
>>> + ? ?flv->flvtool2_index->num_filepositions_processed = 0;
>>> + ? ?flv->flvtool2_index->times_array_parse = 0;
>>> + ? ?flv->flvtool2_index->fill_timestamps = 0;
>>> + ? ?flv->flvtool2_index->num_timestamps_processed = 0;
>>> + ? ?flv->flvtool2_index->num_indexed_keyframes = 0;
>>> + ? ?flv->flvtool2_index->indexes = NULL;
>>> +};
>>
>> av_mallocz...
>>
>>> +
>>> ?static int flv_probe(AVProbeData *p)
>>> ?{
>>> ? ? ?const uint8_t *d;
>>> @@ -130,6 +171,8 @@
>>> ? ? ?AMFDataType amf_type;
>>> ? ? ?char str_val[256];
>>> ? ? ?double num_val;
>>> + ? ?unsigned int index_iter;
>>> + ? ?FLVContext *flv = s->priv_data;
>>>
>>> ? ? ?num_val = 0;
>>> ? ? ?ioc = s->pb;
>>> @@ -138,7 +181,16 @@
>>>
>>> ? ? ?switch(amf_type) {
>>> ? ? ? ? ?case AMF_DATA_TYPE_NUMBER:
>>> - ? ? ? ? ? ?num_val = av_int2dbl(get_be64(ioc)); break;
>>> + ? ? ? ? ? ?num_val = av_int2dbl(get_be64(ioc));
>>> + ? ? ? ? ? ?if (flv->flvtool2_index->times_array_parse&& ?depth == 3&&
>>> + ? ? ? ? ? ? ? ?flv->flvtool2_index->num_timestamps_processed<
>>> ?flv->flvtool2_index->num_indexed_keyframes) {
>>> +
>>> ?flv->flvtool2_index->indexes[flv->flvtool2_index->num_timestamps_processed++].time_offset
>>> = num_val;
>>> + ? ? ? ? ? ?}
>>> + ? ? ? ? ? ?if (flv->flvtool2_index->filepositions_array_parse&& ?depth
>>> == 3
>>> +&& ?flv->flvtool2_index->num_filepositions_processed<
>>> ?flv->flvtool2_index->num_indexed_keyframes) {
>>> +
>>> ?flv->flvtool2_index->indexes[flv->flvtool2_index->num_filepositions_processed++].byte_offset
>>> = num_val;
>>> + ? ? ? ? ? ?}
>>> + ? ? ? ? ? ?break;
>>> ? ? ? ? ?case AMF_DATA_TYPE_BOOL:
>>> ? ? ? ? ? ? ?num_val = get_byte(ioc); break;
>>> ? ? ? ? ?case AMF_DATA_TYPE_STRING:
>>> @@ -149,9 +201,21 @@
>>> ? ? ? ? ? ? ?unsigned int keylen;
>>>
>>> ? ? ? ? ? ? ?while(url_ftell(ioc)< ?max_pos - 2&& ?(keylen =
>>> get_be16(ioc))) {
>>> - ? ? ? ? ? ? ? ?url_fskip(ioc, keylen); //skip key string
>>> + ? ? ? ? ? ? ? ?get_buffer(ioc, str_val, keylen);
>>> + ? ? ? ? ? ? ? ?str_val[keylen] = '\0';
>>> + ? ? ? ? ? ? ? ?if (flv->flvtool2_index->keyframes_object_parse&&
>>> ?!strcmp(FLVTOOL2_TIMESTAMP_TAG, str_val)&& ?depth == 1) {
>>> + ? ? ? ? ? ? ? ? ? ?flv->flvtool2_index->times_array_parse = 1;
>>> + ? ? ? ? ? ? ? ?}
>>> + ? ? ? ? ? ? ? ?if (flv->flvtool2_index->keyframes_object_parse&&
>>> ?!strcmp(FLVTOOL2_BYTEOFFSET_TAG, str_val)&& ?depth == 1) {
>>> + ? ? ? ? ? ? ? ? ? ?flv->flvtool2_index->filepositions_array_parse = 1;
>>> + ? ? ? ? ? ? ? ?}
>>> +
>>> + ? ? ? ? ? ? ? ?//url_fskip(ioc, keylen); //skip key string
>>> ? ? ? ? ? ? ? ? ?if(amf_parse_object(s, NULL, NULL, NULL, max_pos, depth +
>>> 1)< ?0)
>>> ? ? ? ? ? ? ? ? ? ? ?return -1; //if we couldn't skip, bomb out.
>>> +
>>> + ? ? ? ? ? ? ? ?flv->flvtool2_index->times_array_parse = 0;
>>> + ? ? ? ? ? ? ? ?flv->flvtool2_index->filepositions_array_parse = 0;
>>
>>> ? ? ? ? ? ? ?}
>>> ? ? ? ? ? ? ?if(get_byte(ioc) != AMF_END_OF_OBJECT)
>>> ? ? ? ? ? ? ? ? ?return -1;
>>> @@ -164,9 +228,14 @@
>>> ? ? ? ? ?case AMF_DATA_TYPE_MIXEDARRAY:
>>> ? ? ? ? ? ? ?url_fskip(ioc, 4); //skip 32-bit max array index
>>> ? ? ? ? ? ? ?while(url_ftell(ioc)< ?max_pos - 2&& ?amf_get_string(ioc,
>>> str_val, sizeof(str_val))> ?0) {
>>> + ? ? ? ? ? ? ? ?if (!strcmp(FLVTOOL2_KEYFRAMES_TAG, str_val)&& ?depth ==
>>> 0) {
>>> + ? ? ? ? ? ? ? ? ? ? flv->flvtool2_index->keyframes_object_parse = 1;
>>> + ? ? ? ? ? ? ? ?}
>>> ? ? ? ? ? ? ? ? ?//this is the only case in which we would want a nested
>>> parse to not skip over the object
>>> ? ? ? ? ? ? ? ? ?if(amf_parse_object(s, astream, vstream, str_val,
>>> max_pos, depth + 1)< ?0)
>>> ? ? ? ? ? ? ? ? ? ? ?return -1;
>>> +
>>> + ? ? ? ? ? ? ? ?flv->flvtool2_index->keyframes_object_parse = 0;
>>> ? ? ? ? ? ? ?}
>>> ? ? ? ? ? ? ?if(get_byte(ioc) != AMF_END_OF_OBJECT)
>>> ? ? ? ? ? ? ? ? ?return -1;
>>> @@ -175,10 +244,28 @@
>>> ? ? ? ? ? ? ?unsigned int arraylen, i;
>>>
>>> ? ? ? ? ? ? ?arraylen = get_be32(ioc);
>>> + ? ? ? ? ? ?if (flv->flvtool2_index->filepositions_array_parse&& ?depth
>>> == 2) {
>>> + ? ? ? ? ? ? ? ?// allocate array to keep indexes
>>> + ? ? ? ? ? ? ? ?if (flv->flvtool2_index->indexes == NULL) {
>>> + ? ? ? ? ? ? ? ? ? ?flv->flvtool2_index->indexes =
>>> av_malloc(sizeof(FLVTool2KeyframesIndex) * arraylen);
>>> + ? ? ? ? ? ? ? ? ? ?flv->flvtool2_index->num_indexed_keyframes =
>>> arraylen;
>>> + ? ? ? ? ? ? ? ?}
>>> + ? ? ? ? ? ? ? ?flv->flvtool2_index->fill_filepositions = 1;
>>> + ? ? ? ? ? }
>>> + ? ? ? ? ? ?if (flv->flvtool2_index->times_array_parse&& ?depth == 2) {
>>> + ? ? ? ? ? ? ? ?// allocate array to keep indexes
>>> + ? ? ? ? ? ? ? ?if (flv->flvtool2_index->indexes == NULL) {
>>> + ? ? ? ? ? ? ? ? ? ?flv->flvtool2_index->indexes =
>>> av_malloc(sizeof(FLVTool2KeyframesIndex) * arraylen);
>>> + ? ? ? ? ? ? ? ? ? ?flv->flvtool2_index->num_indexed_keyframes =
>>> arraylen;
>>> + ? ? ? ? ? ? ? ?}
>>> + ? ? ? ? ? ? ? ?flv->flvtool2_index->fill_timestamps = 1;
>>> + ? ? ? ? ? }
>>> ? ? ? ? ? ? ?for(i = 0; i< ?arraylen&& ?url_ftell(ioc)< ?max_pos - 1; i++)
>>> {
>>> ? ? ? ? ? ? ? ? ?if(amf_parse_object(s, NULL, NULL, NULL, max_pos, depth +
>>> 1)< ?0)
>>> ? ? ? ? ? ? ? ? ? ? ?return -1; //if we couldn't skip, bomb out.
>>> ? ? ? ? ? ? ?}
>>> + ? ? ? ? ? ?flv->flvtool2_index->fill_filepositions = 0;
>>> + ? ? ? ? ? ?flv->flvtool2_index->fill_timestamps = 0;
>>> ? ? ? ? ?}
>>
>> I don't like how this is spaced out all over the code, but I guess this more
>> due to
>> how the flv reader works...
>>
>>> ? ? ? ? ? ? ?break;
>>> ? ? ? ? ?case AMF_DATA_TYPE_DATE:
>>> @@ -205,6 +292,14 @@
>>> ? ? ? ? ? ? ? ? ?acodec->bit_rate = num_val * 1024.0;
>>> ? ? ? ? ?} else if (amf_type == AMF_DATA_TYPE_STRING)
>>> ? ? ? ? ? ? ?av_metadata_set2(&s->metadata, key, str_val, 0);
>>> +
>>> + ? ? ? ?for (index_iter = 0; index_iter<
>>> ?flv->flvtool2_index->num_indexed_keyframes; ++index_iter) {
>>> + ? ? ? ? ? ?int64_t ts =
>>> flv->flvtool2_index->indexes[index_iter].time_offset;
>>> + ? ? ? ? ? ?int64_t byte_offset =
>>> flv->flvtool2_index->indexes[index_iter].byte_offset;
>>> + ? ? ? ? ? ?av_add_index_entry(vstream, byte_offset, ts*1000, 0, 0,
>>> AVINDEX_KEYFRAME);
>>> + ? ? ? ? ? ?av_log(s, AV_LOG_DEBUG,
>>> + ? ? ? ? ? ? ? ?"add flvtool2 index entry: timestamp: %16"PRIX64", byte
>>> offset: %16"PRIX64"\n", ts, byte_offset);
>>> + ? ? ? ?}
>>
>> you could drop the indexes right after this step to free the mem, no?
>>
>>> ? ? ?}
>>>
>>> ? ? ?return 0;
>>> @@ -214,8 +309,11 @@
>>> ? ? ?AMFDataType type;
>>> ? ? ?AVStream *stream, *astream, *vstream;
>>> ? ? ?ByteIOContext *ioc;
>>> +
>>> ? ? ?int i;
>>> ? ? ?char buffer[11]; //only needs to hold the string "onMetaData".
>>> Anything longer is something we don't want.
>>> + ? ?FLVContext *flv = s->priv_data;
>>> + ? ?init_flv_context(flv);
>>>
>>> ? ? ?astream = NULL;
>>> ? ? ?vstream = NULL;
>>> @@ -491,6 +589,15 @@
>>> ?}
>>> ?#endif
>>>
>>> +static int flv_read_close(AVFormatContext *s)
>>> +{
>>> + ? ?FLVContext *flv = s->priv_data;
>>> + ? ?if (flv->flvtool2_index->indexes != NULL)
>>> + ? ? ? ?av_free(flv->flvtool2_index->indexes);
>>> + ? ?av_free(flv->flvtool2_index);
>>> + ? ?return 0;
>>> +}
>>> +
>>> ?AVInputFormat flv_demuxer = {
>>> ? ? ?"flv",
>>> ? ? ?NULL_IF_CONFIG_SMALL("FLV format"),
>>> @@ -498,6 +605,7 @@
>>> ? ? ?flv_probe,
>>> ? ? ?flv_read_header,
>>> ? ? ?flv_read_packet,
>>> + ? ?flv_read_close,
>>> ? ? ?.read_seek = flv_read_seek,
>>> ?#if 0
>>> ? ? ?.read_seek2 = flv_read_seek2,
>>>
>>>
>>>
>>> _______________________________________________
>>> ffmpeg-devel mailing list
>>> ffmpeg-devel at mplayerhq.hu
>>> https://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-devel
>>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel at mplayerhq.hu
>> https://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-devel
>>
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: flvdec.c.diff
Type: text/x-patch
Size: 7575 bytes
Desc: not available
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20110121/b2c2f87a/attachment.bin>



More information about the ffmpeg-devel mailing list