[FFmpeg-devel] [PATCH] support for flvtool2 "keyframes based" generated index in FLV format decoder
Vladimir Pantelic
vladoman
Thu Jan 20 18:23:42 CET 2011
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
More information about the ffmpeg-devel
mailing list