[FFmpeg-soc] [soc]: r5891 - seek2010/seek2010_08_12_r24750.patch

mchinen subversion at mplayerhq.hu
Thu Aug 12 11:19:11 CEST 2010


Author: mchinen
Date: Thu Aug 12 11:19:10 2010
New Revision: 5891

Log:
ffplay integration
fix off by one bug for index tail seeking
style fixups
pre-devel patch commit

Added:
   seek2010/seek2010_08_12_r24750.patch

Added: seek2010/seek2010_08_12_r24750.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ seek2010/seek2010_08_12_r24750.patch	Thu Aug 12 11:19:10 2010	(r5891)
@@ -0,0 +1,780 @@
+Index: ffplay.c
+===================================================================
+--- ffplay.c	(revision 24750)
++++ ffplay.c	(working copy)
+@@ -224,6 +224,8 @@
+ /* options specified by the user */
+ static AVInputFormat *file_iformat;
+ static const char *input_filename;
++static const char *index_load_filename;
++static const char *index_save_filename;
+ static const char *window_title;
+ static int fs_screen_width;
+ static int fs_screen_height;
+@@ -265,6 +267,10 @@
+ static int exit_on_mousedown;
+ static int loop=1;
+ static int framedrop=1;
++static int build_table    = 0;
++static int parallel_table = 0;
++static int load_table = 0;
++static int save_table = 0;
+ 
+ static int rdftspeed=20;
+ #if CONFIG_AVFILTER
+@@ -2369,6 +2375,27 @@
+     return (global_video_state && global_video_state->abort_request);
+ }
+ 
++static int build_index_thread(void *arg)
++{
++    int i;
++    int done            = 0;
++    AVFormatContext *ic = arg;
++    av_build_index(ic, AV_BUILD_INDEX_PARALLEL);
++    while(save_table && !done) {
++        SDL_Delay(100);
++        done = 1;
++        for(i = 0; i < ic->nb_streams; i++) {
++            if(!(ic->streams[i]->seek_table_flags & AV_SEEKTABLE_FINISHED))
++                done = 0;
++        }
++    }
++    if (index_save_filename) {
++        av_save_index_file(ic, index_save_filename);
++    }
++    //av_free(index_filename);
++    return 0;
++}
++
+ /* this thread gets the stream from the disk or the network */
+ static int decode_thread(void *arg)
+ {
+@@ -2382,6 +2409,7 @@
+     AVFormatParameters params, *ap = &params;
+     int eof=0;
+     int pkt_in_play_range = 0;
++    char *index_filename = NULL;
+ 
+     ic = avformat_alloc_context();
+ 
+@@ -2498,6 +2526,38 @@
+         goto fail;
+     }
+ 
++    if (ic->filename) {
++        index_filename = av_malloc(strlen(ic->filename) + 7);
++        strcpy(index_filename, ic->filename);
++        strcat(index_filename, ".ffidx");
++        /* Check if save or load name was accidentally set to the input file. */
++        if(save_table && (!index_save_filename ||
++                          !strcmp(ic->filename, index_save_filename))) {
++            index_save_filename = index_filename;
++        }
++        if(load_table && (!index_load_filename ||
++                          !strcmp(ic->filename, index_load_filename))) {
++            index_load_filename = index_filename;
++        }
++    }
++
++    if (!load_table || av_load_index_file(ic, index_load_filename) < 0) {
++        if(build_table) {
++            if (parallel_table) {
++                SDL_Thread *thread = SDL_CreateThread(build_index_thread, ic);
++                if (!thread)
++                    parallel_table = 0;
++            }
++            if (!parallel_table)
++                av_build_index(ic, 0);
++            if (index_save_filename) {
++                av_save_index_file(ic, index_save_filename);
++            }
++        }
++    }
++    if(!(save_table && parallel_table))
++        av_free(index_filename);
++
+     for(;;) {
+         if (is->abort_request)
+             break;
+@@ -3073,7 +3133,13 @@
+     { "exitonmousedown", OPT_BOOL | OPT_EXPERT, {(void*)&exit_on_mousedown}, "exit on mouse down", "" },
+     { "loop", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&loop}, "set number of times the playback shall be looped", "loop count" },
+     { "framedrop", OPT_BOOL | OPT_EXPERT, {(void*)&framedrop}, "drop frames when cpu is too slow", "" },
++    { "buildtable", OPT_BOOL | OPT_EXPERT, {(void*)&build_table}, "build index table for seeking", "" },
++    { "bgtable", OPT_BOOL | OPT_EXPERT, {(void*)&parallel_table}, "build index table in background thread", "" },
++    { "loadtable", OPT_BOOL | OPT_EXPERT, {(void*)&load_table}, "try to open table", "" },
++    { "savetable", OPT_BOOL | OPT_EXPERT, {(void*)&save_table}, "save table upon building", "" },
+     { "window_title", OPT_STRING | HAS_ARG, {(void*)&window_title}, "set window title", "window title" },
++    { "loadtablefile", OPT_STRING | HAS_ARG, {(void*)&index_load_filename}, "open index table file", "filename" },
++    { "savetablefile", OPT_STRING | HAS_ARG, {(void*)&index_save_filename}, "save index table file upon building", "filename" },
+ #if CONFIG_AVFILTER
+     { "vf", OPT_STRING | HAS_ARG, {(void*)&vfilters}, "video filters", "filter list" },
+ #endif
+Index: libavformat/avformat.h
+===================================================================
+--- libavformat/avformat.h	(revision 24750)
++++ libavformat/avformat.h	(working copy)
+@@ -586,6 +586,19 @@
+      * Number of frames that have been demuxed during av_find_stream_info()
+      */
+     int codec_info_nb_frames;
++
++    /* new av_seek_frame_table() support */
++#define AV_SEEKTABLE_BUILDING   0x0001 /**< if set the index is being built   */
++#define AV_SEEKTABLE_CBR        0x0002 /**< if set the file is constant bit
++                                            rate and the index is implicit    */
++#define AV_SEEKTABLE_FINISHED   0x0004 /**< if set the index table is done    */
++#define AV_SEEKTABLE_COPIED     0x0008 /**< if set the seek table was copied
++                                            (not built by av_build_index)     */
++    int seek_table_flags;
++    AVIndexEntry* parallel_index_entries; /**< used for thread-safety when
++                                             building parallel indexes        */
++    int parallel_nb_index_entries;
++    unsigned int parallel_index_entries_allocated_size;
+ } AVStream;
+ 
+ #define AV_PROGRAM_RUNNING 1
+@@ -724,6 +737,7 @@
+ #define AVFMT_FLAG_NOFILLIN     0x0010 ///< Do not infer any values from other values, just return what is stored in the container
+ #define AVFMT_FLAG_NOPARSE      0x0020 ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled
+ #define AVFMT_FLAG_RTP_HINT     0x0040 ///< Add RTP hinting to the output file
++#define AVFMT_FLAG_TABLE_READY  0x0080 ///< Copy the seek table from parallel context
+ 
+     int loop_input;
+ 
+@@ -1204,6 +1218,27 @@
+ int av_index_search_timestamp(AVStream *st, int64_t timestamp, int flags);
+ 
+ /**
++ * Builds a complete index for seeking in each stream where it is possible.
++ * Requires that the streams have been opened.
++ * Part of the new seeking api.  incomplete.
++ */
++int av_build_index(AVFormatContext *s, int flags);
++#define AV_BUILD_INDEX_PARALLEL 0x0001 ///< Builds the index via a copied demuxer streams.  av_build_index with this flag can be called on a seperate thread while decoding is happening on another.
++#define AV_BUILD_INDEX_USE_CBR  0x0002 ///< Detect and use CBR instead of index seeking.
++
++/**
++ * Saves the index table built with av_build_index to a file
++ * Returns 0 if succesful or a negative value on failure.
++ */
++int av_save_index_file(AVFormatContext *ic, const char *filename);
++
++/**
++ * Loads the index saved with av_save_index.
++ * Returns 0 if successful or a negative value on failure.
++ */
++int av_load_index_file(AVFormatContext *ic, const char *filename);
++
++/**
+  * Ensure the index uses less memory than the maximum specified in
+  * AVFormatContext.max_index_size by discarding entries if it grows
+  * too large.
+Index: libavformat/utils.c
+===================================================================
+--- libavformat/utils.c	(revision 24750)
++++ libavformat/utils.c	(working copy)
+@@ -1331,8 +1331,28 @@
+ 
+     if((unsigned)st->nb_index_entries >= max_entries){
+         int i;
+-        for(i=0; 2*i<st->nb_index_entries; i++)
+-            st->index_entries[i]= st->index_entries[2*i];
++        if(st->seek_table_flags & AV_SEEKTABLE_BUILDING) {
++            int j            = 0;
++            int nb_reduced   = max_entries / 2;
++            int64_t ts_inc   = (st->index_entries[st->nb_index_entries - 1].timestamp
++                                - st->index_entries[0].timestamp) / nb_reduced;
++            int64_t start_ts = st->index_entries[0].timestamp;
++            /* reinsert for even distribution of timestamps */
++            for (i = 0; i < nb_reduced; i++) {
++                if (j >= st->nb_index_entries)
++                    break;
++                st->index_entries[i] = st->index_entries[j++];
++                while (j < st->nb_index_entries &&
++                       (st->index_entries[j].timestamp <
++                        start_ts + (i + 1) * ts_inc)) {
++                    j++;
++                }
++            }
++            av_log(s,AV_LOG_DEBUG,"reduced index to size %d (max = %d)\n", i, max_entries);
++        } else {
++            for (i = 0; 2 * i < st->nb_index_entries; i++)
++                st->index_entries[i] = st->index_entries[2*i];
++        }
+         st->nb_index_entries= i;
+     }
+ }
+@@ -1343,6 +1363,10 @@
+     AVIndexEntry *entries, *ie;
+     int index;
+ 
++    /* Don't add index if we already have a complete one */
++    if (st->seek_table_flags & AV_SEEKTABLE_FINISHED)
++        return -1;
++
+     if((unsigned)st->nb_index_entries + 1 >= UINT_MAX / sizeof(AVIndexEntry))
+         return -1;
+ 
+@@ -1355,21 +1379,32 @@
+ 
+     st->index_entries= entries;
+ 
+-    index= av_index_search_timestamp(st, timestamp, AVSEEK_FLAG_ANY);
++    /* If we are building the table, the indicies added in order, so we don't
++       have to do expensive searching.                                        */
++    if (st->seek_table_flags & AV_SEEKTABLE_BUILDING) {
++        index = st->nb_index_entries++;
++        ie = &st->index_entries[index];
++    }
++    else {
++        index = av_index_search_timestamp(st, timestamp, AVSEEK_FLAG_ANY);
+ 
+-    if(index<0){
+-        index= st->nb_index_entries++;
+-        ie= &entries[index];
+-        assert(index==0 || ie[-1].timestamp < timestamp);
+-    }else{
+-        ie= &entries[index];
+-        if(ie->timestamp != timestamp){
+-            if(ie->timestamp <= timestamp)
+-                return -1;
+-            memmove(entries + index + 1, entries + index, sizeof(AVIndexEntry)*(st->nb_index_entries - index));
+-            st->nb_index_entries++;
+-        }else if(ie->pos == pos && distance < ie->min_distance) //do not reduce the distance
+-            distance= ie->min_distance;
++        if (index < 0){
++            index = st->nb_index_entries++;
++            ie = &entries[index];
++            assert(index == 0 || ie[-1].timestamp < timestamp);
++        } else {
++            ie= &entries[index];
++            if (ie->timestamp != timestamp){
++                if (ie->timestamp <= timestamp)
++                    return -1;
++                memmove(entries + index + 1, entries + index,
++                        sizeof(AVIndexEntry) * (st->nb_index_entries - index));
++                st->nb_index_entries++;
++            } else if (ie->pos == pos && distance < ie->min_distance) {
++                /* do not reduce the distance */
++                distance = ie->min_distance;
++            }
++        }
+     }
+ 
+     ie->pos = pos;
+@@ -1685,6 +1720,78 @@
+     return 0;
+ }
+ 
++/**
++ * Perform a dictionary search on the seek table.
++ * av_build_index must be successfully called before using this function.
++ * @param timestamp target timestamp in the time base of the given stream
++ * @param stream_index stream number
++ */
++static int av_seek_frame_table(AVFormatContext *s, int stream_index,
++                              int64_t timestamp, int flags)
++{
++    int index;
++    int64_t ret;
++    AVStream *st;
++    AVIndexEntry *ie;
++
++    st = s->streams[stream_index];
++
++    if (st->seek_table_flags & AV_SEEKTABLE_CBR) {
++        int64_t target_pos;
++        int64_t start_time;
++        //If there is a constant frame length, we can compute where to jump to
++        //but CBR may not have constant frame length.
++
++        start_time = st->start_time == AV_NOPTS_VALUE ? 0 : st->start_time;
++        target_pos = s->data_offset + s->file_size * (timestamp - start_time) *
++            av_q2d(st->time_base) / (st->duration * av_q2d(st->time_base));
++        if ((ret = url_fseek(s->pb, target_pos, SEEK_SET)) < 0)
++            return ret;
++        av_update_cur_dts(s, st, timestamp);
++        av_log(s, AV_LOG_DEBUG,
++               "SEEK TABLE: cbr seek ok to %lli of %lli bytes\n",
++               target_pos, s->file_size);
++        return 0;
++    }
++
++    index = av_index_search_timestamp(st, timestamp, flags);
++
++    /* this function should only be called after the table is complete. */
++    if (index < 0 || index >= st->nb_index_entries)
++        return -1;
++
++    ff_read_frame_flush(s);
++    /* we use the native seek function if it exists */
++    if (s->iformat->read_seek){
++        av_log(s, AV_LOG_DEBUG,
++               "index %d, pos %lld, ts %lld",
++               index, st->index_entries[index].pos, st->index_entries[index].timestamp);
++        if (s->iformat->read_seek(s, stream_index, timestamp, flags) >= 0) {
++            av_log(s, AV_LOG_DEBUG,
++                   "SEEK TABLE: table seeked using native function\n");
++            return 0;
++        }
++    }
++
++    ie = &st->index_entries[index];
++    if ((ret = url_fseek(s->pb, ie->pos, SEEK_SET)) < 0)
++        return ret;
++    av_update_cur_dts(s, st, ie->timestamp);
++
++    {
++        float request_time  = (((float)timestamp / st->time_base.den) *
++                               st->time_base.num);
++        float actual_time   = (((float)ie->timestamp / st->time_base.den) *
++                               st->time_base.num);
++        float time_base_inv = ((float)st->time_base.den / st->time_base.num);
++        av_log(s, AV_LOG_DEBUG,
++               "SEEK TABLE: table seeked to %.2fs (actual request was "
++               "%.2fs), timebaseinv %f, samplerate of %d\n",
++               actual_time, request_time, time_base_inv, st->codec->sample_rate);
++    }
++    return 0;
++}
++
+ int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
+ {
+     int ret;
+@@ -1705,6 +1812,28 @@
+         timestamp = av_rescale(timestamp, st->time_base.den, AV_TIME_BASE * (int64_t)st->time_base.num);
+     }
+ 
++    /* if we've built a seek table, use it. */
++    //see if we need to move the parallel table over
++    if (s->flags & AVFMT_FLAG_TABLE_READY) {
++        int i;
++        s->flags ^= AVFMT_FLAG_TABLE_READY;
++        av_log(s, AV_LOG_DEBUG,
++               "SEEK TABLE: copying over parallel frames\n");
++        for(i = 0; i < s->nb_streams; i++) {
++            st = s->streams[i];
++            av_free(st->index_entries);
++            st->index_entries                = st->parallel_index_entries;
++            st->nb_index_entries             = st->parallel_nb_index_entries;
++            st->index_entries_allocated_size = st->parallel_index_entries_allocated_size;
++            st->parallel_index_entries       = NULL;
++            st->seek_table_flags |= AV_SEEKTABLE_FINISHED;
++        }
++    }
++
++    st = s->streams[stream_index];
++    if (st->seek_table_flags & AV_SEEKTABLE_FINISHED)
++        return av_seek_frame_table(s, stream_index, timestamp, flags);
++
+     /* first, we try the format specific seek */
+     if (s->iformat->read_seek)
+         ret = s->iformat->read_seek(s, stream_index, timestamp, flags);
+@@ -1742,6 +1871,405 @@
+     // try some generic seek like av_seek_frame_generic() but with new ts semantics
+ }
+ 
++//if we don't have the number of frames, we just guess.
++#define AV_DETECT_CBR_FPS_GUESS 10
++static int av_detect_cbr(AVFormatContext *s, AVStream *st) {
++    //check the stream's codec to see if a number of frames are listed,
++    //but we can't assume that it is available.
++    if (s->data_offset > 0 && s->file_size > 0 &&
++        s->data_offset != s->file_size && st->codec && st->duration > 0) {
++        double sec_duration = st->duration * av_q2d(st->time_base);
++        int nb_frames       = st->nb_frames ? st->nb_frames :
++                                              (sec_duration *
++                                               AV_DETECT_CBR_FPS_GUESS);
++        int64_t br_dur_len  = (st->codec->bit_rate * sec_duration)/8;
++        int64_t min_len     = (s->file_size - s->data_offset) * (1 - 2.0 /
++                                                                 nb_frames);
++        int64_t max_len     = (s->file_size - s->data_offset) * (1 + 2.0 /
++                                                                 nb_frames);
++
++        if (min_len < br_dur_len && br_dur_len < max_len) {
++            av_log(s, AV_LOG_DEBUG,
++                   "cbr ok (%lli < %lli < %lli) with bitrate=%i, timebase=%i"
++                   "/%i, duration = %f(%lli), nb_frames = %lli, offset= %lli\n",
++                   min_len, br_dur_len, max_len, st->codec->bit_rate,
++                   st->time_base.num, st->time_base.den,
++                   st->duration * av_q2d(st->time_base), st->duration,
++                   st->nb_frames, s->data_offset);
++            return 1;
++        }
++        av_log(s, AV_LOG_DEBUG,
++               "cbr failed !(%lli < %lli < %lli) with bitrate=%i, timebase=%i"
++               "/%i, duration = %f(%lli), nb_frames = %lli, offset= %lli\n",
++               min_len, br_dur_len, max_len, st->codec->bit_rate,
++               st->time_base.num, st->time_base.den,
++               st->duration * av_q2d(st->time_base), st->duration,
++               st->nb_frames, s->data_offset);
++    } else {
++        av_log(s, AV_LOG_DEBUG,
++               "no cbr test with bitrate=%i, timebase=%i/%i, "
++               "duration = %f(%lli), nb_frames = %lli, offset= %lli\n",
++               st->codec ? st->codec->bit_rate : -1,
++               st->time_base.num, st->time_base.den,
++               st->duration * av_q2d(st->time_base),
++               st->duration, st->nb_frames,s->data_offset);
++    }
++
++    return 0;
++}
++
++/**
++ * Starts building a index for seeking.
++ **/
++int av_build_index(AVFormatContext *s, int flags)
++{
++    AVStream *st;
++    int ret;
++    int stream_index;
++    int i;
++    int orig_flags;
++    int64_t start_time = av_gettime();
++
++    av_log(s, AV_LOG_DEBUG, "SEEK TABLE: starting building index\n");
++    stream_index = av_find_default_stream_index(s);
++    if (stream_index < 0)
++        return -1;
++
++    st = s->streams[stream_index];
++    /* Check if this stream is CBR and the codec has a seek by timestamp. */
++    if (flags & AV_BUILD_INDEX_USE_CBR && av_detect_cbr(s, st)) {
++        st->seek_table_flags |= AV_SEEKTABLE_CBR | AV_SEEKTABLE_FINISHED;
++    } else if (st->nb_frames != 0 && st->nb_index_entries > st->nb_frames / 2) {
++        /* Some demuxers load a complete index upon file open. */
++           av_log(s, AV_LOG_DEBUG,
++                  "SEEK TABLE: copying index - %d of %d frames available\n",
++                  st->nb_index_entries, st->nb_frames);
++        st->seek_table_flags |= AV_SEEKTABLE_COPIED | AV_SEEKTABLE_FINISHED;
++    } else {
++        AVFormatContext *build_ic;
++        AVPacket pkt;
++        int data_offset;
++
++        av_log(s, AV_LOG_DEBUG, "SEEK TABLE: building index from scratch\n");
++
++        /* If building should be threadsafe, make a dedicated format context.  */
++        if (flags & AV_BUILD_INDEX_PARALLEL) {
++            av_log(s, AV_LOG_DEBUG,
++                   "SEEK TABLE: making thread-safe copy of streams\n");
++            build_ic = avformat_alloc_context();
++            ret = av_open_input_file(&build_ic, s->filename, s->iformat, 0,
++                                     NULL);
++
++            if (ret < 0) {
++                av_log(s, AV_LOG_DEBUG,
++                       "SEEK TABLE: error re-opening file/streams: %i\n",
++                       ret);
++                goto cleanup;
++            }
++            if (build_ic->nb_streams != s->nb_streams) {
++                ret = -1;
++                av_log(s, AV_LOG_DEBUG,
++                       "SEEK TABLE: cloned AVFormatContext has different "
++                       "number of streams!");
++                goto cleanup;
++            }
++
++            for (i = 0; i < build_ic->nb_streams; i++) {
++                AVStream *build_st    = build_ic->streams[i];
++                AVCodecContext *avctx = build_st->codec;
++                AVCodec *pcodec;
++                build_ic->streams[i]->discard = AVDISCARD_DEFAULT;
++
++                //compare with the orignal stream's context, and if opened
++                //copy settings and open the clone
++                if (s->streams[i]->codec->priv_data) {
++                    av_log(s, AV_LOG_DEBUG,
++                           "SEEK TABLE: copying stream based on"
++                           " priv_data\n");
++                    if ((ret = avcodec_copy_context(avctx, s->streams[i]->codec)) < 0) {
++                        av_log(s, AV_LOG_DEBUG,
++                               "SEEK TABLE: error copying codec:%i\n",
++                               ret);
++                        goto cleanup;
++                    }
++                    pcodec = avcodec_find_decoder(avctx->codec_id);
++                    if ((ret = avcodec_open(avctx, pcodec)) < 0) {
++                        av_log(s, AV_LOG_DEBUG,
++                               "SEEK TABLE: error opening codec:%i\n",
++                               ret);
++                        goto cleanup;
++                    }
++                }
++            }
++        } else {
++            build_ic = s;
++        }
++
++        /* TODO: see why s->data_offset is the file length for avi/mp4 and others */
++        data_offset = s->data_offset < s->file_size ?
++            s->data_offset : 0;
++        if ((ret = url_fseek(build_ic->pb, data_offset, SEEK_SET)) < 0){
++            av_log(s, AV_LOG_DEBUG,
++                   "SEEK TABLE: error building index: %i\n",
++                   ret);
++            goto cleanup;
++        }
++
++        /* Delete old index entries to get a clean table. */
++        for(i = 0; i < build_ic->nb_streams; i++) {
++            build_ic->streams[i]->seek_table_flags |= AV_SEEKTABLE_BUILDING;
++            build_ic->streams[i]->nb_index_entries = 0;
++        }
++
++        orig_flags = build_ic->flags;
++        build_ic->flags |= AVFMT_FLAG_GENPTS;
++        for (;;) {
++            do {
++                ret = av_read_frame(build_ic, &pkt);
++            } while (ret == AVERROR(EAGAIN));
++            if (ret < 0)
++                break;
++            av_free_packet(&pkt);
++        }
++        build_ic->flags = orig_flags;
++        for(i = 0; i < build_ic->nb_streams; i++)
++            build_ic->streams[i]->seek_table_flags ^= AV_SEEKTABLE_BUILDING;
++
++        ret = 0;
++    cleanup:
++        /* If built on a parallel context, mark the original context. */
++        if (flags & AV_BUILD_INDEX_PARALLEL) {
++            if (build_ic) {
++                for (i = 0; i < build_ic->nb_streams; i++) {
++                    if (ret >= 0) {
++                        av_log(s, AV_LOG_DEBUG,
++                               "SEEK TABLE: marking %i frames from"
++                               " parallel stream ready for copy\n",
++                               build_ic->streams[i]->nb_index_entries);
++                        s->streams[i]->parallel_index_entries =
++                            build_ic->streams[i]->index_entries;
++                        s->streams[i]->parallel_nb_index_entries =
++                            build_ic->streams[i]->nb_index_entries;
++                        s->streams[i]->parallel_index_entries_allocated_size =
++                            build_ic->streams[i]->index_entries_allocated_size;
++                        build_ic->streams[i]->index_entries = NULL;
++                    }
++                    avcodec_close(build_ic->streams[i]->codec);
++                }
++                s->flags |= AVFMT_FLAG_TABLE_READY;
++                av_close_input_file(build_ic);
++            }
++        }
++        if (ret < 0)
++            return ret;
++    }
++
++    /* Since we probably have moved the read cursor to the end,
++       return seek to start of stream for non-parallel clients.
++       Not sure if this the desired behavior, but it seems convinient. */
++    if (!(flags & AV_BUILD_INDEX_PARALLEL) &&
++        !(st->seek_table_flags & AV_SEEKTABLE_COPIED) ) {
++        ff_read_frame_flush(s);
++        for(i = 0; i < s->nb_streams; i++)
++            if (s->streams[i]->nb_index_entries)
++                s->streams[i]->seek_table_flags |= AV_SEEKTABLE_FINISHED;
++
++        if (st->start_time == AV_NOPTS_VALUE ||
++            (ret = av_seek_frame(s, stream_index,
++                                 st->start_time, AVSEEK_FLAG_ANY) ) < 0 ) {
++            int data_offset =  (s->data_offset < 0) ||
++                (s->data_offset >= s->file_size) ? 0 : s->data_offset;
++
++            av_log(s, AV_LOG_DEBUG,
++                   "SEEK TABLE: error seeking using new index: %i,"
++                   " trying url_fseek\n",
++                   ret);
++
++            /* last ditch effort to seek using the file pointer. */
++            if ((ret = url_fseek(s->pb, data_offset, SEEK_SET)) < 0) {
++                av_log(s,AV_LOG_DEBUG,
++                       "SEEK TABLE: error seeking with url_fseek: %i\n",
++                       ret);
++                return ret;
++            }
++        }
++    }
++    av_log(s, AV_LOG_DEBUG,
++           "SEEK TABLE: finished building index in %lld ms\n",
++           (av_gettime() - start_time) / 1000);
++    return 0;
++}
++
++/**
++ * Save the flags, size, and differences for the index table
++ */
++#define FF_SAVEINDEX_PACKETSIZE 1000
++static int av_save_index_stream(ByteIOContext *bc, AVStream *st) {
++    AVIndexEntry start_ie;
++    AVIndexEntry *last_ie, *curr_ie;
++    int i;
++
++    //we may need to save something like codec id to be safe.
++    ff_put_v(bc, st->seek_table_flags);
++    //we only write the index if we have a finished table.
++    if (st->seek_table_flags & AV_SEEKTABLE_FINISHED) {
++        ff_put_v(bc, st->nb_index_entries);
++
++        memset(&start_ie, 0, sizeof(AVIndexEntry));
++        last_ie = &start_ie;
++        for (i = 0; i < st->nb_index_entries; i++) {
++            curr_ie = &st->index_entries[i];
++            ff_put_v(bc, curr_ie->pos - last_ie->pos);
++            ff_put_v(bc, curr_ie->timestamp - last_ie->timestamp);
++            ff_put_v(bc, curr_ie->flags - last_ie->flags);
++            ff_put_v(bc, curr_ie->size - last_ie->size);
++            ff_put_v(bc, curr_ie->min_distance - last_ie->min_distance);
++            last_ie = curr_ie;
++            if (i % FF_SAVEINDEX_PACKETSIZE == 0)
++                put_flush_packet(bc);
++        }
++        av_log(NULL, AV_LOG_DEBUG,
++               "successfully wrote index stream with %i entries.\n",
++               st->nb_index_entries);
++    }
++    put_flush_packet(bc);
++
++    return 0;
++}
++
++/**
++ * load the flags, size, and differences for the index table
++ */
++static int av_load_index_stream(ByteIOContext *bc, AVStream *st) {
++    AVIndexEntry *ie, *last_ie;
++    uint64_t v;
++
++    //check to see if we have a table saved in this stream.
++    v = ff_get_v(bc);
++    if (v & AV_SEEKTABLE_FINISHED) {
++        st->seek_table_flags = v;
++        st->nb_index_entries = v = ff_get_v(bc);
++
++        //dump the old index and make the new one.
++        av_free(st->index_entries);
++        st->index_entries = av_malloc(v * sizeof(AVIndexEntry));
++
++        //the first index entry is not a diff, so it gets special treatment.
++        ie = st->index_entries;
++        if (v--) {
++            ie->pos          = ff_get_v(bc);
++            ie->timestamp    = ff_get_v(bc);
++            ie->flags        = ff_get_v(bc);
++            ie->size         = ff_get_v(bc);
++            ie->min_distance = ff_get_v(bc);
++            last_ie          = ie;
++            ie++;
++        }
++
++        while (v--) {
++            ie->pos =  ff_get_v(bc) + last_ie->pos;
++            ie->timestamp =  ff_get_v(bc) + last_ie->timestamp;
++            ie->flags =  ff_get_v(bc) + last_ie->flags;
++            ie->size =  ff_get_v(bc) + last_ie->size;
++            ie->min_distance =  ff_get_v(bc) + last_ie->min_distance;
++            last_ie = ie;
++            ie++;
++        }
++        av_log(NULL, AV_LOG_DEBUG,
++               "successfully loaded index stream with %i entries.\n",
++               st->nb_index_entries);
++    }
++    return 0;
++}
++
++/**
++ * Saves the table index built with av_build_index to a file.
++ * Returns 0 if successful, or a negative number if not.
++ *  this is not mean to be human-readable
++ * we save the following things in order:
++ * - A strz "FFmpegTableIndex" (17 bytes)
++ * - The libavformat version number
++ * - The file name (null terminated)
++ * - the number of streams (4 bytes)
++ * - each of the streams' index tables via av_save_index_stream
++ */
++#define AV_INDEX_IDENTIFIER "FFmpegTableIndex"
++#define AV_INDEX_NOFILENAME "nofilename"
++static int av_save_index(ByteIOContext *bc, AVFormatContext *ic) {
++    int i, ret;
++
++    put_strz(bc, AV_INDEX_IDENTIFIER);
++    ff_put_v(bc, LIBAVFORMAT_VERSION_INT);
++    put_strz(bc, ic->filename ? ic->filename : AV_INDEX_NOFILENAME);
++    ff_put_v(bc, ic->nb_streams);
++    for (i = 0; i < ic->nb_streams; i++) {
++        ret = av_save_index_stream(bc, ic->streams[i]);
++        if (ret < 0)
++            return ret;
++    }
++    put_flush_packet(bc);
++    av_log(ic, AV_LOG_DEBUG, "successfully wrote index.\n");
++    return 0;
++}
++
++static int av_load_index(ByteIOContext *bc, AVFormatContext *ic) {
++    int i, ret;
++    uint64_t v;
++    char read_str[256];
++
++    get_strz(bc, read_str, 255);
++    if (strcmp(read_str, AV_INDEX_IDENTIFIER))
++        return -1;
++    //version
++    v = ff_get_v(bc);
++    //make sure the filename matches the context we will write to.
++    get_strz(bc, read_str, 255);
++    if (strlen(read_str) < 255 && strcmp(read_str, ic->filename) &&
++        strcmp(read_str, AV_INDEX_NOFILENAME))
++       return -1;
++
++    v = ff_get_v(bc);
++    if (v != ic->nb_streams)
++        return -1;
++    for (i = 0; i < v; i++) {
++        ret = av_load_index_stream(bc, ic->streams[i]);
++        if (ret < 0)
++            return ret;
++    }
++    av_log(ic, AV_LOG_DEBUG, "successfully read index.\n");
++    return 0;
++}
++
++int av_save_index_file(AVFormatContext *ic, const char* filename) {
++    ByteIOContext* bc;
++    int i, ret, table_completed = 0;
++
++    //do a first pass to see if we have any tables that need saving.
++    //only write the file if it exists.
++    for (i = 0; i < ic->nb_streams; i++)
++        table_completed |= ic->streams[i]->seek_table_flags & AV_SEEKTABLE_FINISHED;
++    if (!table_completed)
++        return -1;
++
++    if ((ret=url_fopen(&bc, filename, URL_WRONLY)) < 0) {
++        return -1;
++    }
++    ret = av_save_index(bc, ic);
++    url_fclose(bc);
++    return ret;
++}
++
++int av_load_index_file(AVFormatContext *ic, const char* filename) {
++    ByteIOContext* bc;
++    int ret;
++
++    if ((ret = url_fopen(&bc, filename, URL_RDONLY)) < 0) {
++        return -1;
++    }
++    ret = av_load_index(bc, ic);
++    url_fclose(bc);
++    return ret;
++}
+ /*******************************************************/
+ 
+ /**


More information about the FFmpeg-soc mailing list