diff mbox

[FFmpeg-devel,v4] avformat/flvenc: add add_keyframe_index option

Message ID 20161108071356.25224-1-lq@chinaffmpeg.org
State Superseded
Headers show

Commit Message

Liu Steven Nov. 8, 2016, 7:13 a.m. UTC
Add keyframe index metadata
Used to facilitate seeking; particularly for HTTP pseudo streaming.
 1. read live streaming or file by sequence
 2. if use add_keyframe_index option, add a mark flag at the position,
    use to insert new context at the last step.
 3. add the keyframes *offset* and *timestamp* into a list
 4. if use add_keyframe_index option, shift the metadata data from
    mark flag offset
 5. insert the keyframes *offset* and *timestamp* from the list by
    sequence
 6. free the list
 7. end.

Add FATE test case;
Test pass:
PC Linux:

ffmpeg version N-82288-gacd87df Copyright (c) 2000-2016 the FFmpeg developers
  built with gcc 4.8 (Ubuntu 4.8.4-2ubuntu1~14.04.3)
  configuration: --disable-yasm
  libavutil      55. 35.100 / 55. 35.100
  libavcodec     57. 66.101 / 57. 66.101
  libavformat    57. 57.100 / 57. 57.100
  libavdevice    57.  2.100 / 57.  2.100
  libavfilter     6. 66.100 /  6. 66.100
  libswscale      4.  3.100 /  4.  3.100
  libswresample   2.  4.100 /  2.  4.100

Wine + MingW:

Application tried to create a window, but no driver could be loaded.
Make sure that your X server is running and that $DISPLAY is set correctly.
err:systray:initialize_systray Could not create tray window
ffmpeg version N-82288-gacd87df Copyright (c) 2000-2016 the FFmpeg developers
  built with gcc 4.8 (GCC)
  configuration: --cc='ccache x86_64-w64-mingw32-gcc' --arch=x86_64 --target-os=mingw32 --cross-prefix=x86_64-w64-mingw32- --disable-yasm --target_exec=wine
  libavutil      55. 35.100 / 55. 35.100
  libavcodec     57. 66.101 / 57. 66.101
  libavformat    57. 57.100 / 57. 57.100
  libavdevice    57.  2.100 / 57.  2.100
  libavfilter     6. 66.100 /  6. 66.100
  libswscale      4.  3.100 /  4.  3.100
  libswresample   2.  4.100 /  2.  4.100

qemu+mips:
ffmpeg version N-82288-gacd87df Copyright (c) 2000-2016 the FFmpeg developers
  built with gcc 4.4.5 (Debian 4.4.5-8)
  configuration: --target-exec='qemu-mips -cpu 74Kf -L /usr/mips-linux-gnu/' --samples=... --enable-gpl --cross-prefix=/usr/mips-linux-gnu/bin/ --cc='ccache mips-linux-gnu-gcc-4.4' --arch=mips --target-os=linux --enable-cross-compile --disable-mipsfpu --disable-iconv
  libavutil      55. 35.100 / 55. 35.100
  libavcodec     57. 66.101 / 57. 66.101
  libavformat    57. 57.100 / 57. 57.100
  libavdevice    57.  2.100 / 57.  2.100
  libavfilter     6. 66.100 /  6. 66.100
  libswscale      4.  3.100 /  4.  3.100
  libswresample   2.  4.100 /  2.  4.100
  libpostproc    54.  2.100 / 54.  2.100

MacBook:
ffmpeg version N-82275-g1a9e0d0 Copyright (c) 2000-2016 the FFmpeg developers
  built with Apple LLVM version 8.0.0 (clang-800.0.42.1)
  configuration: --enable-fontconfig --enable-gpl --enable-libass --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libspeex --enable-libx264 --enable-libx265 --enable-version3 --cc='ccache gcc'
  libavutil      55. 35.100 / 55. 35.100
  libavcodec     57. 66.101 / 57. 66.101
  libavformat    57. 57.100 / 57. 57.100
  libavdevice    57.  2.100 / 57.  2.100
  libavfilter     6. 66.100 /  6. 66.100
  libswscale      4.  3.100 /  4.  3.100
  libswresample   2.  4.100 /  2.  4.100
  libpostproc    54.  2.100 / 54.  2.100

Reviewed-by: Lou Logan <lou@lrcd.com>
Signed-off-by: Steven Liu <liuqi@gosun.com>

---
 doc/muxers.texi                       |    3 +
 libavformat/flvenc.c                  |  331 ++++++++++++++++++++++++++++++++-
 tests/Makefile                        |    1 +
 tests/fate-run.sh                     |    4 +
 tests/fate/flvenc.mak                 |   11 +
 tests/ref/fate/flv-add_keyframe_index |   12 ++
 6 files changed, 352 insertions(+), 10 deletions(-)
 create mode 100644 tests/fate/flvenc.mak
 create mode 100644 tests/ref/fate/flv-add_keyframe_index

Comments

Steven Liu Nov. 8, 2016, 7:17 a.m. UTC | #1
2016-11-08 15:13 GMT+08:00 Steven Liu <lq@chinaffmpeg.org>:

> Add keyframe index metadata
> Used to facilitate seeking; particularly for HTTP pseudo streaming.
>  1. read live streaming or file by sequence
>  2. if use add_keyframe_index option, add a mark flag at the position,
>     use to insert new context at the last step.
>  3. add the keyframes *offset* and *timestamp* into a list
>  4. if use add_keyframe_index option, shift the metadata data from
>     mark flag offset
>  5. insert the keyframes *offset* and *timestamp* from the list by
>     sequence
>  6. free the list
>  7. end.
>
> Add FATE test case;
> Test pass:
> PC Linux:
>
> ffmpeg version N-82288-gacd87df Copyright (c) 2000-2016 the FFmpeg
> developers
>   built with gcc 4.8 (Ubuntu 4.8.4-2ubuntu1~14.04.3)
>   configuration: --disable-yasm
>   libavutil      55. 35.100 / 55. 35.100
>   libavcodec     57. 66.101 / 57. 66.101
>   libavformat    57. 57.100 / 57. 57.100
>   libavdevice    57.  2.100 / 57.  2.100
>   libavfilter     6. 66.100 /  6. 66.100
>   libswscale      4.  3.100 /  4.  3.100
>   libswresample   2.  4.100 /  2.  4.100
>
> Wine + MingW:
>
> Application tried to create a window, but no driver could be loaded.
> Make sure that your X server is running and that $DISPLAY is set correctly.
> err:systray:initialize_systray Could not create tray window
> ffmpeg version N-82288-gacd87df Copyright (c) 2000-2016 the FFmpeg
> developers
>   built with gcc 4.8 (GCC)
>   configuration: --cc='ccache x86_64-w64-mingw32-gcc' --arch=x86_64
> --target-os=mingw32 --cross-prefix=x86_64-w64-mingw32- --disable-yasm
> --target_exec=wine
>   libavutil      55. 35.100 / 55. 35.100
>   libavcodec     57. 66.101 / 57. 66.101
>   libavformat    57. 57.100 / 57. 57.100
>   libavdevice    57.  2.100 / 57.  2.100
>   libavfilter     6. 66.100 /  6. 66.100
>   libswscale      4.  3.100 /  4.  3.100
>   libswresample   2.  4.100 /  2.  4.100
>
> qemu+mips:
> ffmpeg version N-82288-gacd87df Copyright (c) 2000-2016 the FFmpeg
> developers
>   built with gcc 4.4.5 (Debian 4.4.5-8)
>   configuration: --target-exec='qemu-mips -cpu 74Kf -L
> /usr/mips-linux-gnu/' --samples=... --enable-gpl --cross-prefix=/usr/mips-linux-gnu/bin/
> --cc='ccache mips-linux-gnu-gcc-4.4' --arch=mips --target-os=linux
> --enable-cross-compile --disable-mipsfpu --disable-iconv
>   libavutil      55. 35.100 / 55. 35.100
>   libavcodec     57. 66.101 / 57. 66.101
>   libavformat    57. 57.100 / 57. 57.100
>   libavdevice    57.  2.100 / 57.  2.100
>   libavfilter     6. 66.100 /  6. 66.100
>   libswscale      4.  3.100 /  4.  3.100
>   libswresample   2.  4.100 /  2.  4.100
>   libpostproc    54.  2.100 / 54.  2.100
>
> MacBook:
> ffmpeg version N-82275-g1a9e0d0 Copyright (c) 2000-2016 the FFmpeg
> developers
>   built with Apple LLVM version 8.0.0 (clang-800.0.42.1)
>   configuration: --enable-fontconfig --enable-gpl --enable-libass
> --enable-libbluray --enable-libfreetype --enable-libmp3lame
> --enable-libspeex --enable-libx264 --enable-libx265 --enable-version3
> --cc='ccache gcc'
>   libavutil      55. 35.100 / 55. 35.100
>   libavcodec     57. 66.101 / 57. 66.101
>   libavformat    57. 57.100 / 57. 57.100
>   libavdevice    57.  2.100 / 57.  2.100
>   libavfilter     6. 66.100 /  6. 66.100
>   libswscale      4.  3.100 /  4.  3.100
>   libswresample   2.  4.100 /  2.  4.100
>   libpostproc    54.  2.100 / 54.  2.100
>
> Reviewed-by: Lou Logan <lou@lrcd.com>
> Signed-off-by: Steven Liu <liuqi@gosun.com>
>
> ---
>  doc/muxers.texi                       |    3 +
>  libavformat/flvenc.c                  |  331
> ++++++++++++++++++++++++++++++++-
>  tests/Makefile                        |    1 +
>  tests/fate-run.sh                     |    4 +
>  tests/fate/flvenc.mak                 |   11 +
>  tests/ref/fate/flv-add_keyframe_index |   12 ++
>  6 files changed, 352 insertions(+), 10 deletions(-)
>  create mode 100644 tests/fate/flvenc.mak
>  create mode 100644 tests/ref/fate/flv-add_keyframe_index
>
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index 488ed43..806182a 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -147,6 +147,9 @@ Place AAC sequence header based on audio stream data.
>
>  @item no_sequence_end
>  Disable sequence end tag.
> +
> +@item add_keyframe_index
> +Used to facilitate seeking; particularly for HTTP pseudo streaming.
>  @end table
>  @end table
>
> diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
> index e50f8e4..0cbf561 100644
> --- a/libavformat/flvenc.c
> +++ b/libavformat/flvenc.c
> @@ -24,6 +24,8 @@
>  #include "libavutil/intfloat.h"
>  #include "libavutil/avassert.h"
>  #include "libavutil/mathematics.h"
> +#include "avio_internal.h"
> +#include "avio.h"
>  #include "avc.h"
>  #include "avformat.h"
>  #include "flv.h"
> @@ -64,8 +66,15 @@ static const AVCodecTag flv_audio_codec_ids[] = {
>  typedef enum {
>      FLV_AAC_SEQ_HEADER_DETECT = (1 << 0),
>      FLV_NO_SEQUENCE_END = (1 << 1),
> +    FLV_ADD_KEYFRAME_INDEX = (1 << 2),
>  } FLVFlags;
>
> +typedef struct FLVFileposition {
> +    int64_t keyframe_position;
> +    double keyframe_timestamp;
> +    struct FLVFileposition *next;
> +} FLVFileposition;
> +
>  typedef struct FLVContext {
>      AVClass *av_class;
>      int     reserved;
> @@ -74,6 +83,33 @@ typedef struct FLVContext {
>      int64_t duration;
>      int64_t delay;      ///< first dts delay (needed for AVC & Speex)
>
> +    int64_t datastart_offset;
> +    int64_t datasize_offset;
> +    int64_t datasize;
> +    int64_t videosize_offset;
> +    int64_t videosize;
> +    int64_t audiosize_offset;
> +    int64_t audiosize;
> +
> +    int64_t metadata_size_pos;
> +    int64_t metadata_totalsize_pos;
> +    int64_t metadata_totalsize;
> +    int64_t keyframe_index_size;
> +
> +    int64_t lasttimestamp_offset;
> +    double lasttimestamp;
> +    int64_t lastkeyframetimestamp_offset;
> +    double lastkeyframetimestamp;
> +    int64_t lastkeyframelocation_offset;
> +    int64_t lastkeyframelocation;
> +
> +    int acurframeindex;
> +    int64_t keyframes_info_offset;
> +
> +    int64_t filepositions_count;
> +    FLVFileposition *filepositions;
> +    FLVFileposition *head_filepositions;
> +
>      AVCodecParameters *audio_par;
>      AVCodecParameters *video_par;
>      double framerate;
> @@ -202,6 +238,17 @@ static void put_amf_double(AVIOContext *pb, double d)
>      avio_wb64(pb, av_double2int(d));
>  }
>
> +static void put_amf_byte(AVIOContext *pb, unsigned char abyte)
> +{
> +    avio_w8(pb, abyte);
> +}
> +
> +static void put_amf_dword_array(AVIOContext *pb, uint32_t dw)
> +{
> +    avio_w8(pb, AMF_DATA_TYPE_ARRAY);
> +    avio_wb32(pb, dw);
> +}
> +
>  static void put_amf_bool(AVIOContext *pb, int b)
>  {
>      avio_w8(pb, AMF_DATA_TYPE_BOOL);
> @@ -213,12 +260,12 @@ static void write_metadata(AVFormatContext *s,
> unsigned int ts)
>      AVIOContext *pb = s->pb;
>      FLVContext *flv = s->priv_data;
>      int metadata_count = 0;
> -    int64_t metadata_size_pos, data_size, metadata_count_pos;
> +    int64_t metadata_count_pos;
>      AVDictionaryEntry *tag = NULL;
>
>      /* write meta_tag */
> -    avio_w8(pb, 18);            // tag type META
> -    metadata_size_pos = avio_tell(pb);
> +    avio_w8(pb, FLV_TAG_TYPE_META);            // tag type META
> +    flv->metadata_size_pos = avio_tell(pb);
>      avio_wb24(pb, 0);           // size of data part (sum of all parts
> below)
>      avio_wb24(pb, ts);          // timestamp
>      avio_wb32(pb, 0);           // reserved
> @@ -327,19 +374,87 @@ static void write_metadata(AVFormatContext *s,
> unsigned int ts)
>          put_amf_double(pb, 0); // delayed write
>      }
>
> +    if (flv->flags & FLV_ADD_KEYFRAME_INDEX) {
> +        flv->acurframeindex = 0;
> +        flv->keyframe_index_size = 0;
> +
> +        put_amf_string(pb, "hasVideo");
> +        put_amf_bool(pb, !!flv->video_par);
> +        metadata_count++;
> +
> +        put_amf_string(pb, "hasKeyframes");
> +        put_amf_bool(pb, 1);
> +        metadata_count++;
> +
> +        put_amf_string(pb, "hasAudio");
> +        put_amf_bool(pb, !!flv->audio_par);
> +        metadata_count++;
> +
> +        put_amf_string(pb, "hasMetadata");
> +        put_amf_bool(pb, 1);
> +        metadata_count++;
> +
> +        put_amf_string(pb, "canSeekToEnd");
> +        put_amf_bool(pb, 1);
> +        metadata_count++;
> +
> +        put_amf_string(pb, "datasize");
> +        flv->datasize_offset = avio_tell(pb);
> +        flv->datasize = 0;
> +        put_amf_double(pb, flv->datasize);
> +        metadata_count++;
> +
> +        put_amf_string(pb, "videosize");
> +        flv->videosize_offset = avio_tell(pb);
> +        flv->videosize = 0;
> +        put_amf_double(pb, flv->videosize);
> +        metadata_count++;
> +
> +        put_amf_string(pb, "audiosize");
> +        flv->audiosize_offset = avio_tell(pb);
> +        flv->audiosize = 0;
> +        put_amf_double(pb, flv->audiosize);
> +        metadata_count++;
> +
> +        put_amf_string(pb, "lasttimestamp");
> +        flv->lasttimestamp_offset = avio_tell(pb);
> +        flv->lasttimestamp = 0;
> +        put_amf_double(pb, 0);
> +        metadata_count++;
> +
> +        put_amf_string(pb, "lastkeyframetimestamp");
> +        flv->lastkeyframetimestamp_offset = avio_tell(pb);
> +        flv->lastkeyframetimestamp = 0;
> +        put_amf_double(pb, 0);
> +        metadata_count++;
> +
> +        put_amf_string(pb, "lastkeyframelocation");
> +        flv->lastkeyframelocation_offset = avio_tell(pb);
> +        flv->lastkeyframelocation = 0;
> +        put_amf_double(pb, 0);
> +        metadata_count++;
> +
> +        put_amf_string(pb, "keyframes");
> +        put_amf_byte(pb, AMF_DATA_TYPE_OBJECT);
> +        metadata_count++;
> +
> +        flv->keyframes_info_offset = avio_tell(pb);
> +    }
> +
>      put_amf_string(pb, "");
>      avio_w8(pb, AMF_END_OF_OBJECT);
>
>      /* write total size of tag */
> -    data_size = avio_tell(pb) - metadata_size_pos - 10;
> +    flv->metadata_totalsize = avio_tell(pb) - flv->metadata_size_pos - 10;
>
>      avio_seek(pb, metadata_count_pos, SEEK_SET);
>      avio_wb32(pb, metadata_count);
>
> -    avio_seek(pb, metadata_size_pos, SEEK_SET);
> -    avio_wb24(pb, data_size);
> -    avio_skip(pb, data_size + 10 - 3);
> -    avio_wb32(pb, data_size + 11);
> +    avio_seek(pb, flv->metadata_size_pos, SEEK_SET);
> +    avio_wb24(pb, flv->metadata_totalsize);
> +    avio_skip(pb, flv->metadata_totalsize + 10 - 3);
> +    flv->metadata_totalsize_pos = avio_tell(pb);
> +    avio_wb32(pb, flv->metadata_totalsize + 11);
>  }
>
>  static int unsupported_codec(AVFormatContext *s,
> @@ -416,6 +531,111 @@ static void flv_write_codec_header(AVFormatContext*
> s, AVCodecParameters* par) {
>      }
>  }
>
> +static int flv_append_keyframe_info(AVFormatContext *s, FLVContext *flv,
> double ts, int64_t pos)
> +{
> +    FLVFileposition *position = av_malloc(sizeof(FLVFileposition));
> +
> +    if (!position) {
> +        av_log(s, AV_LOG_WARNING, "no mem for add keyframe index!\n");
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    position->keyframe_timestamp = ts;
> +    position->keyframe_position = pos;
> +
> +    if (!flv->filepositions_count) {
> +        flv->filepositions = position;
> +        flv->head_filepositions = flv->filepositions;
> +        position->next = NULL;
> +    } else {
> +        flv->filepositions->next = position;
> +        position->next = NULL;
> +        flv->filepositions = flv->filepositions->next;
> +    }
> +
> +    flv->filepositions_count++;
> +
> +    return 0;
> +}
> +
> +static int shift_data(AVFormatContext *s)
> +{
> +    int ret = 0;
> +    int n = 0;
> +    int64_t metadata_size = 0;
> +    FLVContext *flv = s->priv_data;
> +    int64_t pos, pos_end = avio_tell(s->pb);
> +    uint8_t *buf, *read_buf[2];
> +    int read_buf_id = 0;
> +    int read_size[2];
> +    AVIOContext *read_pb;
> +
> +    metadata_size = flv->filepositions_count * 9 * 2 + 10; /*
> filepositions and times value */
> +    metadata_size += 2 + 13; /* filepositions String */
> +    metadata_size += 2 + 5; /* times String */
> +    metadata_size += 3; /* Object end */
> +
> +    flv->keyframe_index_size = metadata_size;
> +
> +    if (metadata_size < 0)
> +        return metadata_size;
> +
> +    buf = av_malloc_array(metadata_size, 2);
> +    if (!buf) {
> +        return AVERROR(ENOMEM);
> +    }
> +    read_buf[0] = buf;
> +    read_buf[1] = buf + metadata_size;
> +
> +    avio_seek(s->pb, flv->metadata_size_pos, SEEK_SET);
> +    avio_wb24(s->pb, flv->metadata_totalsize + metadata_size);
> +
> +    avio_seek(s->pb, flv->metadata_totalsize_pos, SEEK_SET);
> +    avio_wb32(s->pb, flv->metadata_totalsize + 11 + metadata_size);
> +    avio_seek(s->pb, pos_end, SEEK_SET);
> +
> +    /* Shift the data: the AVIO context of the output can only be used for
> +     * writing, so we re-open the same output, but for reading. It also
> avoids
> +     * a read/seek/write/seek back and forth. */
> +    avio_flush(s->pb);
> +    ret = s->io_open(s, &read_pb, s->filename, AVIO_FLAG_READ, NULL);
> +    if (ret < 0) {
> +        av_log(s, AV_LOG_ERROR, "Unable to re-open %s output file for "
> +               "the second pass (add_keyframe_index)\n", s->filename);
> +        goto end;
> +    }
> +
> +    /* mark the end of the shift to up to the last data we wrote, and get
> ready
> +     * for writing */
> +    pos_end = avio_tell(s->pb);
> +    avio_seek(s->pb, flv->keyframes_info_offset + metadata_size,
> SEEK_SET);
> +
> +    /* start reading at where the keyframe index information will be
> placed */
> +    avio_seek(read_pb, flv->keyframes_info_offset, SEEK_SET);
> +    pos = avio_tell(read_pb);
> +
> +    /* shift data by chunk of at most keyframe *filepositions* and
> *times* size */
> +    read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id],
> metadata_size);  \
> +    read_buf_id ^= 1;
> +    do {
> +
> +        read_size[read_buf_id] = avio_read(read_pb,
> read_buf[read_buf_id], metadata_size);  \
> +        read_buf_id ^= 1;
> +        n = read_size[read_buf_id];
> +        if (n < 0)
> +            break;
> +        avio_write(s->pb, read_buf[read_buf_id], n);
> +        pos += n;
> +    } while (pos <= pos_end);
> +
> +    ff_format_io_close(s, &read_pb);
> +
> +end:
> +    av_free(buf);
> +    return ret;
> +}
> +
> +
>  static int flv_write_header(AVFormatContext *s)
>  {
>      int i;
> @@ -521,17 +741,75 @@ static int flv_write_header(AVFormatContext *s)
>          flv_write_codec_header(s, s->streams[i]->codecpar);
>      }
>
> +    flv->datastart_offset = avio_tell(pb);
>      return 0;
>  }
>
>  static int flv_write_trailer(AVFormatContext *s)
>  {
>      int64_t file_size;
> -
>      AVIOContext *pb = s->pb;
>      FLVContext *flv = s->priv_data;
> -    int i;
> +    int build_keyframes_idx = flv->flags & FLV_ADD_KEYFRAME_INDEX;
> +    int i, res;
> +    int64_t cur_pos = avio_tell(s->pb);
> +
> +    if (build_keyframes_idx) {
> +        FLVFileposition *newflv_posinfo, *p;
> +
> +        avio_seek(pb, flv->videosize_offset, SEEK_SET);
> +        put_amf_double(pb, flv->videosize);
> +
> +        avio_seek(pb, flv->audiosize_offset, SEEK_SET);
> +        put_amf_double(pb, flv->audiosize);
> +
> +        avio_seek(pb, flv->lasttimestamp_offset, SEEK_SET);
> +        put_amf_double(pb, flv->lasttimestamp);
> +
> +        avio_seek(pb, flv->lastkeyframetimestamp_offset, SEEK_SET);
> +        put_amf_double(pb, flv->lastkeyframetimestamp);
>
> +        avio_seek(pb, flv->lastkeyframelocation_offset, SEEK_SET);
> +        put_amf_double(pb, flv->lastkeyframelocation +
> flv->keyframe_index_size);
> +        avio_seek(pb, cur_pos, SEEK_SET);
> +
> +        res = shift_data(s);
> +        if (res < 0) {
> +             goto end;
> +        }
> +        avio_seek(pb, flv->keyframes_info_offset, SEEK_SET);
> +        put_amf_string(pb, "filepositions");
> +        put_amf_dword_array(pb, flv->filepositions_count);
> +        for (newflv_posinfo = flv->head_filepositions; newflv_posinfo;
> newflv_posinfo = newflv_posinfo->next) {
> +            put_amf_double(pb, newflv_posinfo->keyframe_position +
> flv->keyframe_index_size);
> +        }
> +
> +        put_amf_string(pb, "times");
> +        put_amf_dword_array(pb, flv->filepositions_count);
> +        for (newflv_posinfo = flv->head_filepositions; newflv_posinfo;
> newflv_posinfo = newflv_posinfo->next) {
> +            put_amf_double(pb, newflv_posinfo->keyframe_timestamp);
> +        }
> +
> +        newflv_posinfo = flv->head_filepositions;
> +        while (newflv_posinfo) {
> +            p = newflv_posinfo->next;
> +            if (p) {
> +                newflv_posinfo->next = p->next;
> +                av_free(p);
> +                p = NULL;
> +            } else {
> +                av_free(newflv_posinfo);
> +                newflv_posinfo = NULL;
> +            }
> +        }
> +
> +        put_amf_string(pb, "");
> +        avio_w8(pb, AMF_END_OF_OBJECT);
> +
> +        avio_seek(pb, cur_pos + flv->keyframe_index_size, SEEK_SET);
> +    }
> +
> +end:
>      if (flv->flags & FLV_NO_SEQUENCE_END) {
>          av_log(s, AV_LOG_DEBUG, "FLV no sequence end mode open\n");
>      } else {
> @@ -547,6 +825,11 @@ static int flv_write_trailer(AVFormatContext *s)
>
>      file_size = avio_tell(pb);
>
> +    if (build_keyframes_idx) {
> +        flv->datasize = file_size - flv->datastart_offset;
> +        avio_seek(pb, flv->datasize_offset, SEEK_SET);
> +        put_amf_double(pb, flv->datasize);
> +    }
>      if (pb->seekable) {
>          /* update information */
>          if (avio_seek(pb, flv->duration_offset, SEEK_SET) < 0) {
> @@ -574,6 +857,7 @@ static int flv_write_packet(AVFormatContext *s,
> AVPacket *pkt)
>      int size = pkt->size;
>      uint8_t *data = NULL;
>      int flags = -1, flags_size, ret;
> +    int64_t cur_offset = avio_tell(pb);
>
>      if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id ==
> AV_CODEC_ID_VP6A ||
>          par->codec_id == AV_CODEC_ID_VP6  || par->codec_id ==
> AV_CODEC_ID_AAC)
> @@ -727,6 +1011,32 @@ static int flv_write_packet(AVFormatContext *s,
> AVPacket *pkt)
>                                pkt->pts + flv->delay + pkt->duration);
>      }
>
> +    if (flv->flags & FLV_ADD_KEYFRAME_INDEX) {
> +        switch (par->codec_type) {
> +            case AVMEDIA_TYPE_VIDEO:
> +                flv->videosize += (avio_tell(pb) - cur_offset);
> +                flv->lasttimestamp = flv->acurframeindex / flv->framerate;
> +                if (pkt->flags & AV_PKT_FLAG_KEY) {
> +                    double ts = flv->acurframeindex / flv->framerate;
> +                    int64_t pos = cur_offset;
> +
> +                    flv->lastkeyframetimestamp = flv->acurframeindex /
> flv->framerate;
> +                    flv->lastkeyframelocation = pos;
> +                    flv_append_keyframe_info(s, flv, ts, pos);
> +                }
> +                flv->acurframeindex++;
> +                break;
> +
> +            case AVMEDIA_TYPE_AUDIO:
> +                flv->audiosize += (avio_tell(pb) - cur_offset);
> +                break;
> +
> +            default:
> +                av_log(s, AV_LOG_WARNING, "par->codec_type is type =
> [%d]\n", par->codec_type);
> +                break;
> +        }
> +    }
> +
>      av_free(data);
>
>      return pb->error;
> @@ -736,6 +1046,7 @@ static const AVOption options[] = {
>      { "flvflags", "FLV muxer flags", offsetof(FLVContext, flags),
> AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX,
> AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
>      { "aac_seq_header_detect", "Put AAC sequence header based on stream
> data", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_AAC_SEQ_HEADER_DETECT}, INT_MIN,
> INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
>      { "no_sequence_end", "disable sequence end for FLV", 0,
> AV_OPT_TYPE_CONST, {.i64 = FLV_NO_SEQUENCE_END}, INT_MIN, INT_MAX,
> AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
> +    { "add_keyframe_index", "Add keyframe index metadata", 0,
> AV_OPT_TYPE_CONST, {.i64 = FLV_ADD_KEYFRAME_INDEX}, INT_MIN, INT_MAX,
> AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
>      { NULL },
>  };
>
> diff --git a/tests/Makefile b/tests/Makefile
> index 8e810ff..07e839e 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -132,6 +132,7 @@ include $(SRC_PATH)/tests/fate/fifo-muxer.mak
>  include $(SRC_PATH)/tests/fate/filter-audio.mak
>  include $(SRC_PATH)/tests/fate/filter-video.mak
>  include $(SRC_PATH)/tests/fate/flac.mak
> +include $(SRC_PATH)/tests/fate/flvenc.mak
>  include $(SRC_PATH)/tests/fate/gapless.mak
>  include $(SRC_PATH)/tests/fate/gif.mak
>  include $(SRC_PATH)/tests/fate/h264.mak
> diff --git a/tests/fate-run.sh b/tests/fate-run.sh
> index c640cc5..29a36ce 100755
> --- a/tests/fate-run.sh
> +++ b/tests/fate-run.sh
> @@ -129,6 +129,10 @@ framecrc(){
>      ffmpeg "$@" -flags +bitexact -fflags +bitexact -f framecrc -
>  }
>
> +ffmetadata(){
> +    ffmpeg "$@" -flags +bitexact -fflags +bitexact -f ffmetadata -
> +}
> +
>  framemd5(){
>      ffmpeg "$@" -flags +bitexact -fflags +bitexact -f framemd5 -
>  }
> diff --git a/tests/fate/flvenc.mak b/tests/fate/flvenc.mak
> new file mode 100644
> index 0000000..2f05d5a
> --- /dev/null
> +++ b/tests/fate/flvenc.mak
> @@ -0,0 +1,11 @@
> +tests/data/add_keyframe_index.flv: TAG = GEN
> +tests/data/add_keyframe_index.flv: ffmpeg$(PROGSSUF)$(EXESUF) |
> tests/data
> +       $(M)$(TARGET_EXEC) $(TARGET_PATH)/$< \
> +               -f lavfi -i "color=c=red:size=352x288" -metadata
> "encoder=Lavf" -pix_fmt yuv420p -c:v flv1 -b:v 100k -g 7 -f flv -flags
> +bitexact \
> +               -flvflags add_keyframe_index -s 352x288 -t 5 -y
> $(TARGET_PATH)/tests/data/add_keyframe_index.flv 2> /dev/null;
> +
> +FATE_AFILTER-$(call ALLYES, FLV_MUXER FLV_DEMUXER AVDEVICE TESTSRC_FILTER
> LAVFI_INDEV FLV_ENCODER) += fate-flv-add_keyframe_index
> +fate-flv-add_keyframe_index: tests/data/add_keyframe_index.flv
> +fate-flv-add_keyframe_index: CMD = ffmetadata -flags +bitexact -i
> $(TARGET_PATH)/tests/data/add_keyframe_index.flv
> +
> +
> diff --git a/tests/ref/fate/flv-add_keyframe_index
> b/tests/ref/fate/flv-add_keyframe_index
> new file mode 100644
> index 0000000..8e12244
> --- /dev/null
> +++ b/tests/ref/fate/flv-add_keyframe_index
> @@ -0,0 +1,12 @@
> +;FFMETADATA1
> +hasVideo=true
> +hasKeyframes=true
> +hasAudio=false
> +hasMetadata=true
> +canSeekToEnd=true
> +datasize=55566
> +videosize=55207
> +audiosize=0
> +lasttimestamp=5
> +lastkeyframetimestamp=5
> +lastkeyframelocation=52623
> --
> 1.7.1
>
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>

Hi Michael,

      Can you share the qemu+ARM test compile example into the wiki, i have
not found it, so only test with:
     macbook osx, pc Linux ubuntu, qemu + mips, wine+mingw, no
qemu+arm environment.
Michael Niedermayer Nov. 8, 2016, 8:47 a.m. UTC | #2
On Tue, Nov 08, 2016 at 03:17:26PM +0800, Steven Liu wrote:
[...]
> > diff --git a/tests/ref/fate/flv-add_keyframe_index
> > b/tests/ref/fate/flv-add_keyframe_index
> > new file mode 100644
> > index 0000000..8e12244
> > --- /dev/null
> > +++ b/tests/ref/fate/flv-add_keyframe_index
> > @@ -0,0 +1,12 @@
> > +;FFMETADATA1
> > +hasVideo=true
> > +hasKeyframes=true
> > +hasAudio=false
> > +hasMetadata=true
> > +canSeekToEnd=true
> > +datasize=55566
> > +videosize=55207
> > +audiosize=0
> > +lasttimestamp=5
> > +lastkeyframetimestamp=5
> > +lastkeyframelocation=52623
> > --
> > 1.7.1
> >
> >
> >
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> 
> Hi Michael,
> 
>       Can you share the qemu+ARM test compile example into the wiki, i have
> not found it, so only test with:

the qemu arm is very similar to qemu mips
you can see one qemu arm fate client with all configure parameters here:
http://fatebeta.ffmpeg.org/report/armel5tej-qemu-debian-gcc4.4/20161108072019


>      macbook osx, pc Linux ubuntu, qemu + mips, wine+mingw, no
> qemu+arm environment.

qemu mips fails similarly

[...]
Steven Liu Nov. 8, 2016, 8:51 a.m. UTC | #3
2016-11-08 16:47 GMT+08:00 Michael Niedermayer <michael@niedermayer.cc>:

> On Tue, Nov 08, 2016 at 03:17:26PM +0800, Steven Liu wrote:
> [...]
> > > diff --git a/tests/ref/fate/flv-add_keyframe_index
> > > b/tests/ref/fate/flv-add_keyframe_index
> > > new file mode 100644
> > > index 0000000..8e12244
> > > --- /dev/null
> > > +++ b/tests/ref/fate/flv-add_keyframe_index
> > > @@ -0,0 +1,12 @@
> > > +;FFMETADATA1
> > > +hasVideo=true
> > > +hasKeyframes=true
> > > +hasAudio=false
> > > +hasMetadata=true
> > > +canSeekToEnd=true
> > > +datasize=55566
> > > +videosize=55207
> > > +audiosize=0
> > > +lasttimestamp=5
> > > +lastkeyframetimestamp=5
> > > +lastkeyframelocation=52623
> > > --
> > > 1.7.1
> > >
> > >
> > >
> > > _______________________________________________
> > > ffmpeg-devel mailing list
> > > ffmpeg-devel@ffmpeg.org
> > > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > >
> >
> > Hi Michael,
> >
> >       Can you share the qemu+ARM test compile example into the wiki, i
> have
> > not found it, so only test with:
>
> the qemu arm is very similar to qemu mips
> you can see one qemu arm fate client with all configure parameters here:
> http://fatebeta.ffmpeg.org/report/armel5tej-qemu-debian-
> gcc4.4/20161108072019
>
>
> >      macbook osx, pc Linux ubuntu, qemu + mips, wine+mingw, no
> > qemu+arm environment.
>
> qemu mips fails similarly
>


lq@ubuntu:~/ffmpeg/mips$ make fate-flv-add_keyframe_index
GEN tests/data/add_keyframe_index.flv
ffmpeg version N-82288-gacd87df Copyright (c) 2000-2016 the FFmpeg
developers
  built with gcc 4.4.5 (Debian 4.4.5-8)
  configuration: --target-exec='qemu-mips -cpu 74Kf -L
/usr/mips-linux-gnu/' --samples=... --enable-gpl
--cross-prefix=/usr/mips-linux-gnu/bin/ --cc='ccache
mips-linux-gnu-gcc-4.4' --arch=mips --target-os=linux
--enable-cross-compile --disable-mipsfpu --disable-iconv
  libavutil      55. 35.100 / 55. 35.100
  libavcodec     57. 66.101 / 57. 66.101
  libavformat    57. 57.100 / 57. 57.100
  libavdevice    57.  2.100 / 57.  2.100
  libavfilter     6. 66.100 /  6. 66.100
  libswscale      4.  3.100 /  4.  3.100
  libswresample   2.  4.100 /  2.  4.100
  libpostproc    54.  2.100 / 54.  2.100
Input #0, lavfi, from 'color=c=red:size=352x288':
  Duration: N/A, start: 0.000000, bitrate: N/A
    Stream #0:0: Video: rawvideo (I420 / 0x30323449), yuv420p, 352x288 [SAR
1:1 DAR 11:9], 25 tbr, 25 tbn, 25 tbc
[flv @ 0x1d38d30] Setting the AVFormatContext to bitexact mode, because the
AVCodecContext is in that mode. This behavior will change in the future. To
keep the current behavior, set AVFormatContext.flags |= AVFMT_FLAG_BITEXACT.
Output #0, flv, to '/home/lq/ffmpeg/mips/tests/data/add_keyframe_index.flv':
    Stream #0:0: Video: flv1 (flv) ([2][0][0][0] / 0x0002), yuv420p,
352x288 [SAR 1:1 DAR 11:9], q=2-31, 100 kb/s, 25 fps, 1k tbn, 25 tbc
    Metadata:
      encoder         : Lavc flv
    Side data:
      cpb: bitrate max/min/avg: 0/0/100000 buffer size: 0 vbv_delay: -1
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo (native) -> flv1 (flv))
Press [q] to stop, [?] for help
frame=  125 fps=0.0 q=2.0 Lsize=      55kB time=00:00:04.96 bitrate=
 90.3kbits/s speed=5.42x
video:52kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB
muxing overhead: 5.215479%
TEST    flv-add_keyframe_index
lq@ubuntu:~/ffmpeg/mips$
lq@ubuntu:~/ffmpeg/mips$




> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> The bravest are surely those who have the clearest vision
> of what is before them, glory and danger alike, and yet
> notwithstanding go out to meet it. -- Thucydides
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
>
Steven Liu Nov. 8, 2016, 9:48 a.m. UTC | #4
2016-11-08 16:51 GMT+08:00 Steven Liu <lingjiujianke@gmail.com>:

>
>
> 2016-11-08 16:47 GMT+08:00 Michael Niedermayer <michael@niedermayer.cc>:
>
>> On Tue, Nov 08, 2016 at 03:17:26PM +0800, Steven Liu wrote:
>> [...]
>> > > diff --git a/tests/ref/fate/flv-add_keyframe_index
>> > > b/tests/ref/fate/flv-add_keyframe_index
>> > > new file mode 100644
>> > > index 0000000..8e12244
>> > > --- /dev/null
>> > > +++ b/tests/ref/fate/flv-add_keyframe_index
>> > > @@ -0,0 +1,12 @@
>> > > +;FFMETADATA1
>> > > +hasVideo=true
>> > > +hasKeyframes=true
>> > > +hasAudio=false
>> > > +hasMetadata=true
>> > > +canSeekToEnd=true
>> > > +datasize=55566
>> > > +videosize=55207
>> > > +audiosize=0
>> > > +lasttimestamp=5
>> > > +lastkeyframetimestamp=5
>> > > +lastkeyframelocation=52623
>> > > --
>> > > 1.7.1
>> > >
>> > >
>> > >
>> > > _______________________________________________
>> > > ffmpeg-devel mailing list
>> > > ffmpeg-devel@ffmpeg.org
>> > > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> > >
>> >
>> > Hi Michael,
>> >
>> >       Can you share the qemu+ARM test compile example into the wiki, i
>> have
>> > not found it, so only test with:
>>
>> the qemu arm is very similar to qemu mips
>> you can see one qemu arm fate client with all configure parameters here:
>> http://fatebeta.ffmpeg.org/report/armel5tej-qemu-debian-gcc4
>> .4/20161108072019
>>
>>
>> >      macbook osx, pc Linux ubuntu, qemu + mips, wine+mingw, no
>> > qemu+arm environment.
>>
>> qemu mips fails similarly
>>
>
>
> lq@ubuntu:~/ffmpeg/mips$ make fate-flv-add_keyframe_index
> GEN tests/data/add_keyframe_index.flv
> ffmpeg version N-82288-gacd87df Copyright (c) 2000-2016 the FFmpeg
> developers
>   built with gcc 4.4.5 (Debian 4.4.5-8)
>   configuration: --target-exec='qemu-mips -cpu 74Kf -L
> /usr/mips-linux-gnu/' --samples=... --enable-gpl --cross-prefix=/usr/mips-linux-gnu/bin/
> --cc='ccache mips-linux-gnu-gcc-4.4' --arch=mips --target-os=linux
> --enable-cross-compile --disable-mipsfpu --disable-iconv
>   libavutil      55. 35.100 / 55. 35.100
>   libavcodec     57. 66.101 / 57. 66.101
>   libavformat    57. 57.100 / 57. 57.100
>   libavdevice    57.  2.100 / 57.  2.100
>   libavfilter     6. 66.100 /  6. 66.100
>   libswscale      4.  3.100 /  4.  3.100
>   libswresample   2.  4.100 /  2.  4.100
>   libpostproc    54.  2.100 / 54.  2.100
> Input #0, lavfi, from 'color=c=red:size=352x288':
>   Duration: N/A, start: 0.000000, bitrate: N/A
>     Stream #0:0: Video: rawvideo (I420 / 0x30323449), yuv420p, 352x288
> [SAR 1:1 DAR 11:9], 25 tbr, 25 tbn, 25 tbc
> [flv @ 0x1d38d30] Setting the AVFormatContext to bitexact mode, because
> the AVCodecContext is in that mode. This behavior will change in the
> future. To keep the current behavior, set AVFormatContext.flags |=
> AVFMT_FLAG_BITEXACT.
> Output #0, flv, to '/home/lq/ffmpeg/mips/tests/
> data/add_keyframe_index.flv':
>     Stream #0:0: Video: flv1 (flv) ([2][0][0][0] / 0x0002), yuv420p,
> 352x288 [SAR 1:1 DAR 11:9], q=2-31, 100 kb/s, 25 fps, 1k tbn, 25 tbc
>     Metadata:
>       encoder         : Lavc flv
>     Side data:
>       cpb: bitrate max/min/avg: 0/0/100000 buffer size: 0 vbv_delay: -1
> Stream mapping:
>   Stream #0:0 -> #0:0 (rawvideo (native) -> flv1 (flv))
> Press [q] to stop, [?] for help
> frame=  125 fps=0.0 q=2.0 Lsize=      55kB time=00:00:04.96 bitrate=
>  90.3kbits/s speed=5.42x
> video:52kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB
> muxing overhead: 5.215479%
> TEST    flv-add_keyframe_index
> lq@ubuntu:~/ffmpeg/mips$
> lq@ubuntu:~/ffmpeg/mips$
>
>
>
>
>> [...]
>> --
>> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>>
>> The bravest are surely those who have the clearest vision
>> of what is before them, glory and danger alike, and yet
>> notwithstanding go out to meet it. -- Thucydides
>>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>>

qemu+arm ok:


liuqi@localhost:~/ffmpeg/arm$ make fate-flv-add_keyframe_index
HOSTCC tests/base64.o
HOSTLD tests/base64
HOSTCC tests/tiny_psnr.o
HOSTLD tests/tiny_psnr
HOSTCC tests/tiny_ssim.o
HOSTLD tests/tiny_ssim
HOSTCC tests/audiomatch.o
src/tests/audiomatch.c: In function ‘main’:
src/tests/audiomatch.c:83:10: warning: ignoring return value of ‘fread’,
declared with attribute warn_unused_result [-Wunused-result]
     fread(data  , 1, datlen, f[0]);
          ^
src/tests/audiomatch.c:84:10: warning: ignoring return value of ‘fread’,
declared with attribute warn_unused_result [-Wunused-result]
     fread(signal, 1, siglen, f[1]);
          ^
HOSTLD tests/audiomatch
GEN tests/data/add_keyframe_index.flv
ffmpeg version N-82288-gacd87df Copyright (c) 2000-2016 the FFmpeg
developers
  built with gcc 4.4.5 (Debian 4.4.5-8)
  configuration: --enable-gpl --enable-memory-poisoning --enable-avresample
--cc='ccache arm-linux-gnueabi-gcc' --target-exec='qemu-arm -L
/usr/arm-linux-gnueabi/' --arch=armv5te --cpu=armv5te
--enable-cross-compile --target-os=linux
--cross-prefix=/usr/arm-linux-gnueabi/bin/
  libavutil      55. 35.100 / 55. 35.100
  libavcodec     57. 66.101 / 57. 66.101
  libavformat    57. 57.100 / 57. 57.100
  libavdevice    57.  2.100 / 57.  2.100
  libavfilter     6. 66.100 /  6. 66.100
  libavresample   3.  2.  0 /  3.  2.  0
  libswscale      4.  3.100 /  4.  3.100
  libswresample   2.  4.100 /  2.  4.100
  libpostproc    54.  2.100 / 54.  2.100
Input #0, lavfi, from 'color=c=red:size=352x288':
  Duration: N/A, start: 0.000000, bitrate: N/A
    Stream #0:0: Video: rawvideo (I420 / 0x30323449), yuv420p, 352x288 [SAR
1:1 DAR 11:9], 25 tbr, 25 tbn, 25 tbc
[flv @ 0x1a8bd30] Setting the AVFormatContext to bitexact mode, because the
AVCodecContext is in that mode. This behavior will change in the future. To
keep the current behavior, set AVFormatContext.flags |= AVFMT_FLAG_BITEXACT.
Output #0, flv, to
'/home/liuqi/ffmpeg/arm/tests/data/add_keyframe_index.flv':
    Stream #0:0: Video: flv1 (flv) ([2][0][0][0] / 0x0002), yuv420p,
352x288 [SAR 1:1 DAR 11:9], q=2-31, 100 kb/s, 25 fps, 1k tbn, 25 tbc
    Metadata:
      encoder         : Lavc flv
    Side data:
      cpb: bitrate max/min/avg: 0/0/100000 buffer size: 0 vbv_delay: -1
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo (native) -> flv1 (flv))
Press [q] to stop, [?] for help
frame=  125 fps=0.0 q=2.0 Lsize=      55kB time=00:00:04.96 bitrate=
 90.3kbits/s speed=5.66x
video:52kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB
muxing overhead: 5.215479%
TEST    flv-add_keyframe_index
liuqi@localhost:~/ffmpeg/arm$
Michael Niedermayer Nov. 8, 2016, 11:07 a.m. UTC | #5
On Tue, Nov 08, 2016 at 05:48:07PM +0800, Steven Liu wrote:
> 2016-11-08 16:51 GMT+08:00 Steven Liu <lingjiujianke@gmail.com>:
> 
> >
> >
> > 2016-11-08 16:47 GMT+08:00 Michael Niedermayer <michael@niedermayer.cc>:
> >
> >> On Tue, Nov 08, 2016 at 03:17:26PM +0800, Steven Liu wrote:
> >> [...]
> >> > > diff --git a/tests/ref/fate/flv-add_keyframe_index
> >> > > b/tests/ref/fate/flv-add_keyframe_index
> >> > > new file mode 100644
> >> > > index 0000000..8e12244
> >> > > --- /dev/null
> >> > > +++ b/tests/ref/fate/flv-add_keyframe_index
> >> > > @@ -0,0 +1,12 @@
> >> > > +;FFMETADATA1
> >> > > +hasVideo=true
> >> > > +hasKeyframes=true
> >> > > +hasAudio=false
> >> > > +hasMetadata=true
> >> > > +canSeekToEnd=true
> >> > > +datasize=55566
> >> > > +videosize=55207
> >> > > +audiosize=0
> >> > > +lasttimestamp=5
> >> > > +lastkeyframetimestamp=5
> >> > > +lastkeyframelocation=52623
> >> > > --
> >> > > 1.7.1
> >> > >
> >> > >
> >> > >
> >> > > _______________________________________________
> >> > > ffmpeg-devel mailing list
> >> > > ffmpeg-devel@ffmpeg.org
> >> > > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >> > >
> >> >
> >> > Hi Michael,
> >> >
> >> >       Can you share the qemu+ARM test compile example into the wiki, i
> >> have
> >> > not found it, so only test with:
> >>
> >> the qemu arm is very similar to qemu mips
> >> you can see one qemu arm fate client with all configure parameters here:
> >> http://fatebeta.ffmpeg.org/report/armel5tej-qemu-debian-gcc4
> >> .4/20161108072019
> >>
> >>
> >> >      macbook osx, pc Linux ubuntu, qemu + mips, wine+mingw, no
> >> > qemu+arm environment.
> >>
> >> qemu mips fails similarly
> >>
> >
> >
> > lq@ubuntu:~/ffmpeg/mips$ make fate-flv-add_keyframe_index
> > GEN tests/data/add_keyframe_index.flv
> > ffmpeg version N-82288-gacd87df Copyright (c) 2000-2016 the FFmpeg
> > developers
> >   built with gcc 4.4.5 (Debian 4.4.5-8)
> >   configuration: --target-exec='qemu-mips -cpu 74Kf -L
> > /usr/mips-linux-gnu/' --samples=... --enable-gpl --cross-prefix=/usr/mips-linux-gnu/bin/
> > --cc='ccache mips-linux-gnu-gcc-4.4' --arch=mips --target-os=linux
> > --enable-cross-compile --disable-mipsfpu --disable-iconv
> >   libavutil      55. 35.100 / 55. 35.100
> >   libavcodec     57. 66.101 / 57. 66.101
> >   libavformat    57. 57.100 / 57. 57.100
> >   libavdevice    57.  2.100 / 57.  2.100
> >   libavfilter     6. 66.100 /  6. 66.100
> >   libswscale      4.  3.100 /  4.  3.100
> >   libswresample   2.  4.100 /  2.  4.100
> >   libpostproc    54.  2.100 / 54.  2.100
> > Input #0, lavfi, from 'color=c=red:size=352x288':
> >   Duration: N/A, start: 0.000000, bitrate: N/A
> >     Stream #0:0: Video: rawvideo (I420 / 0x30323449), yuv420p, 352x288
> > [SAR 1:1 DAR 11:9], 25 tbr, 25 tbn, 25 tbc
> > [flv @ 0x1d38d30] Setting the AVFormatContext to bitexact mode, because
> > the AVCodecContext is in that mode. This behavior will change in the
> > future. To keep the current behavior, set AVFormatContext.flags |=
> > AVFMT_FLAG_BITEXACT.
> > Output #0, flv, to '/home/lq/ffmpeg/mips/tests/
> > data/add_keyframe_index.flv':
> >     Stream #0:0: Video: flv1 (flv) ([2][0][0][0] / 0x0002), yuv420p,
> > 352x288 [SAR 1:1 DAR 11:9], q=2-31, 100 kb/s, 25 fps, 1k tbn, 25 tbc
> >     Metadata:
> >       encoder         : Lavc flv
> >     Side data:
> >       cpb: bitrate max/min/avg: 0/0/100000 buffer size: 0 vbv_delay: -1
> > Stream mapping:
> >   Stream #0:0 -> #0:0 (rawvideo (native) -> flv1 (flv))
> > Press [q] to stop, [?] for help
> > frame=  125 fps=0.0 q=2.0 Lsize=      55kB time=00:00:04.96 bitrate=
> >  90.3kbits/s speed=5.42x
> > video:52kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB
> > muxing overhead: 5.215479%
> > TEST    flv-add_keyframe_index
> > lq@ubuntu:~/ffmpeg/mips$
> > lq@ubuntu:~/ffmpeg/mips$
> >
> >
> >
> >
> >> [...]
> >> --
> >> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
> >>
> >> The bravest are surely those who have the clearest vision
> >> of what is before them, glory and danger alike, and yet
> >> notwithstanding go out to meet it. -- Thucydides
> >>
> >> _______________________________________________
> >> ffmpeg-devel mailing list
> >> ffmpeg-devel@ffmpeg.org
> >> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >>
> >>
> 
> qemu+arm ok:

the flv file is not regenerated sometimes it seems, i had to delete
it by hand during testing

either way scaler bitexact flags and idct/dct settings are missing

@@ -1,8 +1,8 @@
 tests/data/add_keyframe_index.flv: TAG = GEN
 tests/data/add_keyframe_index.flv: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data
        $(M)$(TARGET_EXEC) $(TARGET_PATH)/$< \
-                -f lavfi -i "testsrc=r=7:n=2:d=20" -metadata "encoder=Lavf" -pix_fmt yuv420p -c:v flv1 -g 7 -f flv -flags +bitexact \
-                -flvflags add_keyframe_index -y $(TARGET_PATH)/tests/data/add_keyframe_index.flv 2> /dev/null;
+                -f lavfi -i "sws_flags=+accurate_rnd+bitexact;testsrc=r=7:n=2:d=20" -sws_flags '+accurate_rnd+bitexact' -metadata "encoder=Lavf" -pix_fmt yuv420p -c:v flv1 -g 7 -f flv -flags +bitexact \
+                -flvflags add_keyframe_index -idct simple -dct int -y $(TARGET_PATH)/tests/data/add_keyframe_index.flv 2> /dev/null;

 FATE_AFILTER-$(call ALLYES, FLV_MUXER FLV_DEMUXER AVDEVICE TESTSRC_FILTER LAVFI_INDEV FLV_ENCODER) += fate-flv-add_keyframe_index
 fate-flv-add_keyframe_index: tests/data/add_keyframe_index.flv

above worked

[...]
Steven Liu Nov. 8, 2016, 11:44 a.m. UTC | #6
2016-11-08 19:07 GMT+08:00 Michael Niedermayer <michael@niedermayer.cc>:

> On Tue, Nov 08, 2016 at 05:48:07PM +0800, Steven Liu wrote:
> > 2016-11-08 16:51 GMT+08:00 Steven Liu <lingjiujianke@gmail.com>:
> >
> > >
> > >
> > > 2016-11-08 16:47 GMT+08:00 Michael Niedermayer <michael@niedermayer.cc
> >:
> > >
> > >> On Tue, Nov 08, 2016 at 03:17:26PM +0800, Steven Liu wrote:
> > >> [...]
> > >> > > diff --git a/tests/ref/fate/flv-add_keyframe_index
> > >> > > b/tests/ref/fate/flv-add_keyframe_index
> > >> > > new file mode 100644
> > >> > > index 0000000..8e12244
> > >> > > --- /dev/null
> > >> > > +++ b/tests/ref/fate/flv-add_keyframe_index
> > >> > > @@ -0,0 +1,12 @@
> > >> > > +;FFMETADATA1
> > >> > > +hasVideo=true
> > >> > > +hasKeyframes=true
> > >> > > +hasAudio=false
> > >> > > +hasMetadata=true
> > >> > > +canSeekToEnd=true
> > >> > > +datasize=55566
> > >> > > +videosize=55207
> > >> > > +audiosize=0
> > >> > > +lasttimestamp=5
> > >> > > +lastkeyframetimestamp=5
> > >> > > +lastkeyframelocation=52623
> > >> > > --
> > >> > > 1.7.1
> > >> > >
> > >> > >
> > >> > >
> > >> > > _______________________________________________
> > >> > > ffmpeg-devel mailing list
> > >> > > ffmpeg-devel@ffmpeg.org
> > >> > > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > >> > >
> > >> >
> > >> > Hi Michael,
> > >> >
> > >> >       Can you share the qemu+ARM test compile example into the
> wiki, i
> > >> have
> > >> > not found it, so only test with:
> > >>
> > >> the qemu arm is very similar to qemu mips
> > >> you can see one qemu arm fate client with all configure parameters
> here:
> > >> http://fatebeta.ffmpeg.org/report/armel5tej-qemu-debian-gcc4
> > >> .4/20161108072019
> > >>
> > >>
> > >> >      macbook osx, pc Linux ubuntu, qemu + mips, wine+mingw, no
> > >> > qemu+arm environment.
> > >>
> > >> qemu mips fails similarly
> > >>
> > >
> > >
> > > lq@ubuntu:~/ffmpeg/mips$ make fate-flv-add_keyframe_index
> > > GEN tests/data/add_keyframe_index.flv
> > > ffmpeg version N-82288-gacd87df Copyright (c) 2000-2016 the FFmpeg
> > > developers
> > >   built with gcc 4.4.5 (Debian 4.4.5-8)
> > >   configuration: --target-exec='qemu-mips -cpu 74Kf -L
> > > /usr/mips-linux-gnu/' --samples=... --enable-gpl
> --cross-prefix=/usr/mips-linux-gnu/bin/
> > > --cc='ccache mips-linux-gnu-gcc-4.4' --arch=mips --target-os=linux
> > > --enable-cross-compile --disable-mipsfpu --disable-iconv
> > >   libavutil      55. 35.100 / 55. 35.100
> > >   libavcodec     57. 66.101 / 57. 66.101
> > >   libavformat    57. 57.100 / 57. 57.100
> > >   libavdevice    57.  2.100 / 57.  2.100
> > >   libavfilter     6. 66.100 /  6. 66.100
> > >   libswscale      4.  3.100 /  4.  3.100
> > >   libswresample   2.  4.100 /  2.  4.100
> > >   libpostproc    54.  2.100 / 54.  2.100
> > > Input #0, lavfi, from 'color=c=red:size=352x288':
> > >   Duration: N/A, start: 0.000000, bitrate: N/A
> > >     Stream #0:0: Video: rawvideo (I420 / 0x30323449), yuv420p, 352x288
> > > [SAR 1:1 DAR 11:9], 25 tbr, 25 tbn, 25 tbc
> > > [flv @ 0x1d38d30] Setting the AVFormatContext to bitexact mode, because
> > > the AVCodecContext is in that mode. This behavior will change in the
> > > future. To keep the current behavior, set AVFormatContext.flags |=
> > > AVFMT_FLAG_BITEXACT.
> > > Output #0, flv, to '/home/lq/ffmpeg/mips/tests/
> > > data/add_keyframe_index.flv':
> > >     Stream #0:0: Video: flv1 (flv) ([2][0][0][0] / 0x0002), yuv420p,
> > > 352x288 [SAR 1:1 DAR 11:9], q=2-31, 100 kb/s, 25 fps, 1k tbn, 25 tbc
> > >     Metadata:
> > >       encoder         : Lavc flv
> > >     Side data:
> > >       cpb: bitrate max/min/avg: 0/0/100000 buffer size: 0 vbv_delay: -1
> > > Stream mapping:
> > >   Stream #0:0 -> #0:0 (rawvideo (native) -> flv1 (flv))
> > > Press [q] to stop, [?] for help
> > > frame=  125 fps=0.0 q=2.0 Lsize=      55kB time=00:00:04.96 bitrate=
> > >  90.3kbits/s speed=5.42x
> > > video:52kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB
> > > muxing overhead: 5.215479%
> > > TEST    flv-add_keyframe_index
> > > lq@ubuntu:~/ffmpeg/mips$
> > > lq@ubuntu:~/ffmpeg/mips$
> > >
> > >
> > >
> > >
> > >> [...]
> > >> --
> > >> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC7
> 87040B0FAB
> > >>
> > >> The bravest are surely those who have the clearest vision
> > >> of what is before them, glory and danger alike, and yet
> > >> notwithstanding go out to meet it. -- Thucydides
> > >>
> > >> _______________________________________________
> > >> ffmpeg-devel mailing list
> > >> ffmpeg-devel@ffmpeg.org
> > >> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > >>
> > >>
> >
> > qemu+arm ok:
>
> the flv file is not regenerated sometimes it seems, i had to delete
> it by hand during testing
>
> either way scaler bitexact flags and idct/dct settings are missing
>
> @@ -1,8 +1,8 @@
>  tests/data/add_keyframe_index.flv: TAG = GEN
>  tests/data/add_keyframe_index.flv: ffmpeg$(PROGSSUF)$(EXESUF) |
> tests/data
>         $(M)$(TARGET_EXEC) $(TARGET_PATH)/$< \
> -                -f lavfi -i "testsrc=r=7:n=2:d=20" -metadata
> "encoder=Lavf" -pix_fmt yuv420p -c:v flv1 -g 7 -f flv -flags +bitexact \
> -                -flvflags add_keyframe_index -y
> $(TARGET_PATH)/tests/data/add_keyframe_index.flv 2> /dev/null;
> +                -f lavfi -i "sws_flags=+accurate_rnd+bitexact;testsrc=r=7:n=2:d=20"
> -sws_flags '+accurate_rnd+bitexact' -metadata "encoder=Lavf" -pix_fmt
> yuv420p -c:v flv1 -g 7 -f flv -flags +bitexact \
> +                -flvflags add_keyframe_index -idct simple -dct int -y
> $(TARGET_PATH)/tests/data/add_keyframe_index.flv 2> /dev/null;
>
>  FATE_AFILTER-$(call ALLYES, FLV_MUXER FLV_DEMUXER AVDEVICE TESTSRC_FILTER
> LAVFI_INDEV FLV_ENCODER) += fate-flv-add_keyframe_index
>  fate-flv-add_keyframe_index: tests/data/add_keyframe_index.flv
>
> above worked
>
> ok, new patch coming!

> [...]
>
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> It is dangerous to be right in matters on which the established authorities
> are wrong. -- Voltaire
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
>
diff mbox

Patch

diff --git a/doc/muxers.texi b/doc/muxers.texi
index 488ed43..806182a 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -147,6 +147,9 @@  Place AAC sequence header based on audio stream data.
 
 @item no_sequence_end
 Disable sequence end tag.
+
+@item add_keyframe_index
+Used to facilitate seeking; particularly for HTTP pseudo streaming.
 @end table
 @end table
 
diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
index e50f8e4..0cbf561 100644
--- a/libavformat/flvenc.c
+++ b/libavformat/flvenc.c
@@ -24,6 +24,8 @@ 
 #include "libavutil/intfloat.h"
 #include "libavutil/avassert.h"
 #include "libavutil/mathematics.h"
+#include "avio_internal.h"
+#include "avio.h"
 #include "avc.h"
 #include "avformat.h"
 #include "flv.h"
@@ -64,8 +66,15 @@  static const AVCodecTag flv_audio_codec_ids[] = {
 typedef enum {
     FLV_AAC_SEQ_HEADER_DETECT = (1 << 0),
     FLV_NO_SEQUENCE_END = (1 << 1),
+    FLV_ADD_KEYFRAME_INDEX = (1 << 2),
 } FLVFlags;
 
+typedef struct FLVFileposition {
+    int64_t keyframe_position;
+    double keyframe_timestamp;
+    struct FLVFileposition *next;
+} FLVFileposition;
+
 typedef struct FLVContext {
     AVClass *av_class;
     int     reserved;
@@ -74,6 +83,33 @@  typedef struct FLVContext {
     int64_t duration;
     int64_t delay;      ///< first dts delay (needed for AVC & Speex)
 
+    int64_t datastart_offset;
+    int64_t datasize_offset;
+    int64_t datasize;
+    int64_t videosize_offset;
+    int64_t videosize;
+    int64_t audiosize_offset;
+    int64_t audiosize;
+
+    int64_t metadata_size_pos;
+    int64_t metadata_totalsize_pos;
+    int64_t metadata_totalsize;
+    int64_t keyframe_index_size;
+
+    int64_t lasttimestamp_offset;
+    double lasttimestamp;
+    int64_t lastkeyframetimestamp_offset;
+    double lastkeyframetimestamp;
+    int64_t lastkeyframelocation_offset;
+    int64_t lastkeyframelocation;
+
+    int acurframeindex;
+    int64_t keyframes_info_offset;
+
+    int64_t filepositions_count;
+    FLVFileposition *filepositions;
+    FLVFileposition *head_filepositions;
+
     AVCodecParameters *audio_par;
     AVCodecParameters *video_par;
     double framerate;
@@ -202,6 +238,17 @@  static void put_amf_double(AVIOContext *pb, double d)
     avio_wb64(pb, av_double2int(d));
 }
 
+static void put_amf_byte(AVIOContext *pb, unsigned char abyte)
+{
+    avio_w8(pb, abyte);
+}
+
+static void put_amf_dword_array(AVIOContext *pb, uint32_t dw)
+{
+    avio_w8(pb, AMF_DATA_TYPE_ARRAY);
+    avio_wb32(pb, dw);
+}
+
 static void put_amf_bool(AVIOContext *pb, int b)
 {
     avio_w8(pb, AMF_DATA_TYPE_BOOL);
@@ -213,12 +260,12 @@  static void write_metadata(AVFormatContext *s, unsigned int ts)
     AVIOContext *pb = s->pb;
     FLVContext *flv = s->priv_data;
     int metadata_count = 0;
-    int64_t metadata_size_pos, data_size, metadata_count_pos;
+    int64_t metadata_count_pos;
     AVDictionaryEntry *tag = NULL;
 
     /* write meta_tag */
-    avio_w8(pb, 18);            // tag type META
-    metadata_size_pos = avio_tell(pb);
+    avio_w8(pb, FLV_TAG_TYPE_META);            // tag type META
+    flv->metadata_size_pos = avio_tell(pb);
     avio_wb24(pb, 0);           // size of data part (sum of all parts below)
     avio_wb24(pb, ts);          // timestamp
     avio_wb32(pb, 0);           // reserved
@@ -327,19 +374,87 @@  static void write_metadata(AVFormatContext *s, unsigned int ts)
         put_amf_double(pb, 0); // delayed write
     }
 
+    if (flv->flags & FLV_ADD_KEYFRAME_INDEX) {
+        flv->acurframeindex = 0;
+        flv->keyframe_index_size = 0;
+
+        put_amf_string(pb, "hasVideo");
+        put_amf_bool(pb, !!flv->video_par);
+        metadata_count++;
+
+        put_amf_string(pb, "hasKeyframes");
+        put_amf_bool(pb, 1);
+        metadata_count++;
+
+        put_amf_string(pb, "hasAudio");
+        put_amf_bool(pb, !!flv->audio_par);
+        metadata_count++;
+
+        put_amf_string(pb, "hasMetadata");
+        put_amf_bool(pb, 1);
+        metadata_count++;
+
+        put_amf_string(pb, "canSeekToEnd");
+        put_amf_bool(pb, 1);
+        metadata_count++;
+
+        put_amf_string(pb, "datasize");
+        flv->datasize_offset = avio_tell(pb);
+        flv->datasize = 0;
+        put_amf_double(pb, flv->datasize);
+        metadata_count++;
+
+        put_amf_string(pb, "videosize");
+        flv->videosize_offset = avio_tell(pb);
+        flv->videosize = 0;
+        put_amf_double(pb, flv->videosize);
+        metadata_count++;
+
+        put_amf_string(pb, "audiosize");
+        flv->audiosize_offset = avio_tell(pb);
+        flv->audiosize = 0;
+        put_amf_double(pb, flv->audiosize);
+        metadata_count++;
+
+        put_amf_string(pb, "lasttimestamp");
+        flv->lasttimestamp_offset = avio_tell(pb);
+        flv->lasttimestamp = 0;
+        put_amf_double(pb, 0);
+        metadata_count++;
+
+        put_amf_string(pb, "lastkeyframetimestamp");
+        flv->lastkeyframetimestamp_offset = avio_tell(pb);
+        flv->lastkeyframetimestamp = 0;
+        put_amf_double(pb, 0);
+        metadata_count++;
+
+        put_amf_string(pb, "lastkeyframelocation");
+        flv->lastkeyframelocation_offset = avio_tell(pb);
+        flv->lastkeyframelocation = 0;
+        put_amf_double(pb, 0);
+        metadata_count++;
+
+        put_amf_string(pb, "keyframes");
+        put_amf_byte(pb, AMF_DATA_TYPE_OBJECT);
+        metadata_count++;
+
+        flv->keyframes_info_offset = avio_tell(pb);
+    }
+
     put_amf_string(pb, "");
     avio_w8(pb, AMF_END_OF_OBJECT);
 
     /* write total size of tag */
-    data_size = avio_tell(pb) - metadata_size_pos - 10;
+    flv->metadata_totalsize = avio_tell(pb) - flv->metadata_size_pos - 10;
 
     avio_seek(pb, metadata_count_pos, SEEK_SET);
     avio_wb32(pb, metadata_count);
 
-    avio_seek(pb, metadata_size_pos, SEEK_SET);
-    avio_wb24(pb, data_size);
-    avio_skip(pb, data_size + 10 - 3);
-    avio_wb32(pb, data_size + 11);
+    avio_seek(pb, flv->metadata_size_pos, SEEK_SET);
+    avio_wb24(pb, flv->metadata_totalsize);
+    avio_skip(pb, flv->metadata_totalsize + 10 - 3);
+    flv->metadata_totalsize_pos = avio_tell(pb);
+    avio_wb32(pb, flv->metadata_totalsize + 11);
 }
 
 static int unsupported_codec(AVFormatContext *s,
@@ -416,6 +531,111 @@  static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par) {
     }
 }
 
+static int flv_append_keyframe_info(AVFormatContext *s, FLVContext *flv, double ts, int64_t pos)
+{
+    FLVFileposition *position = av_malloc(sizeof(FLVFileposition));
+
+    if (!position) {
+        av_log(s, AV_LOG_WARNING, "no mem for add keyframe index!\n");
+        return AVERROR(ENOMEM);
+    }
+
+    position->keyframe_timestamp = ts;
+    position->keyframe_position = pos;
+
+    if (!flv->filepositions_count) {
+        flv->filepositions = position;
+        flv->head_filepositions = flv->filepositions;
+        position->next = NULL;
+    } else {
+        flv->filepositions->next = position;
+        position->next = NULL;
+        flv->filepositions = flv->filepositions->next;
+    }
+
+    flv->filepositions_count++;
+
+    return 0;
+}
+
+static int shift_data(AVFormatContext *s)
+{
+    int ret = 0;
+    int n = 0;
+    int64_t metadata_size = 0;
+    FLVContext *flv = s->priv_data;
+    int64_t pos, pos_end = avio_tell(s->pb);
+    uint8_t *buf, *read_buf[2];
+    int read_buf_id = 0;
+    int read_size[2];
+    AVIOContext *read_pb;
+
+    metadata_size = flv->filepositions_count * 9 * 2 + 10; /* filepositions and times value */
+    metadata_size += 2 + 13; /* filepositions String */
+    metadata_size += 2 + 5; /* times String */
+    metadata_size += 3; /* Object end */
+
+    flv->keyframe_index_size = metadata_size;
+
+    if (metadata_size < 0)
+        return metadata_size;
+
+    buf = av_malloc_array(metadata_size, 2);
+    if (!buf) {
+        return AVERROR(ENOMEM);
+    }
+    read_buf[0] = buf;
+    read_buf[1] = buf + metadata_size;
+
+    avio_seek(s->pb, flv->metadata_size_pos, SEEK_SET);
+    avio_wb24(s->pb, flv->metadata_totalsize + metadata_size);
+
+    avio_seek(s->pb, flv->metadata_totalsize_pos, SEEK_SET);
+    avio_wb32(s->pb, flv->metadata_totalsize + 11 + metadata_size);
+    avio_seek(s->pb, pos_end, SEEK_SET);
+
+    /* Shift the data: the AVIO context of the output can only be used for
+     * writing, so we re-open the same output, but for reading. It also avoids
+     * a read/seek/write/seek back and forth. */
+    avio_flush(s->pb);
+    ret = s->io_open(s, &read_pb, s->filename, AVIO_FLAG_READ, NULL);
+    if (ret < 0) {
+        av_log(s, AV_LOG_ERROR, "Unable to re-open %s output file for "
+               "the second pass (add_keyframe_index)\n", s->filename);
+        goto end;
+    }
+
+    /* mark the end of the shift to up to the last data we wrote, and get ready
+     * for writing */
+    pos_end = avio_tell(s->pb);
+    avio_seek(s->pb, flv->keyframes_info_offset + metadata_size, SEEK_SET);
+
+    /* start reading at where the keyframe index information will be placed */
+    avio_seek(read_pb, flv->keyframes_info_offset, SEEK_SET);
+    pos = avio_tell(read_pb);
+
+    /* shift data by chunk of at most keyframe *filepositions* and *times* size */
+    read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], metadata_size);  \
+    read_buf_id ^= 1;
+    do {
+
+        read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], metadata_size);  \
+        read_buf_id ^= 1;
+        n = read_size[read_buf_id];
+        if (n < 0)
+            break;
+        avio_write(s->pb, read_buf[read_buf_id], n);
+        pos += n;
+    } while (pos <= pos_end);
+
+    ff_format_io_close(s, &read_pb);
+
+end:
+    av_free(buf);
+    return ret;
+}
+
+
 static int flv_write_header(AVFormatContext *s)
 {
     int i;
@@ -521,17 +741,75 @@  static int flv_write_header(AVFormatContext *s)
         flv_write_codec_header(s, s->streams[i]->codecpar);
     }
 
+    flv->datastart_offset = avio_tell(pb);
     return 0;
 }
 
 static int flv_write_trailer(AVFormatContext *s)
 {
     int64_t file_size;
-
     AVIOContext *pb = s->pb;
     FLVContext *flv = s->priv_data;
-    int i;
+    int build_keyframes_idx = flv->flags & FLV_ADD_KEYFRAME_INDEX;
+    int i, res;
+    int64_t cur_pos = avio_tell(s->pb);
+
+    if (build_keyframes_idx) {
+        FLVFileposition *newflv_posinfo, *p;
+
+        avio_seek(pb, flv->videosize_offset, SEEK_SET);
+        put_amf_double(pb, flv->videosize);
+
+        avio_seek(pb, flv->audiosize_offset, SEEK_SET);
+        put_amf_double(pb, flv->audiosize);
+
+        avio_seek(pb, flv->lasttimestamp_offset, SEEK_SET);
+        put_amf_double(pb, flv->lasttimestamp);
+
+        avio_seek(pb, flv->lastkeyframetimestamp_offset, SEEK_SET);
+        put_amf_double(pb, flv->lastkeyframetimestamp);
 
+        avio_seek(pb, flv->lastkeyframelocation_offset, SEEK_SET);
+        put_amf_double(pb, flv->lastkeyframelocation + flv->keyframe_index_size);
+        avio_seek(pb, cur_pos, SEEK_SET);
+
+        res = shift_data(s);
+        if (res < 0) {
+             goto end;
+        }
+        avio_seek(pb, flv->keyframes_info_offset, SEEK_SET);
+        put_amf_string(pb, "filepositions");
+        put_amf_dword_array(pb, flv->filepositions_count);
+        for (newflv_posinfo = flv->head_filepositions; newflv_posinfo; newflv_posinfo = newflv_posinfo->next) {
+            put_amf_double(pb, newflv_posinfo->keyframe_position + flv->keyframe_index_size);
+        }
+
+        put_amf_string(pb, "times");
+        put_amf_dword_array(pb, flv->filepositions_count);
+        for (newflv_posinfo = flv->head_filepositions; newflv_posinfo; newflv_posinfo = newflv_posinfo->next) {
+            put_amf_double(pb, newflv_posinfo->keyframe_timestamp);
+        }
+
+        newflv_posinfo = flv->head_filepositions;
+        while (newflv_posinfo) {
+            p = newflv_posinfo->next;
+            if (p) {
+                newflv_posinfo->next = p->next;
+                av_free(p);
+                p = NULL;
+            } else {
+                av_free(newflv_posinfo);
+                newflv_posinfo = NULL;
+            }
+        }
+
+        put_amf_string(pb, "");
+        avio_w8(pb, AMF_END_OF_OBJECT);
+
+        avio_seek(pb, cur_pos + flv->keyframe_index_size, SEEK_SET);
+    }
+
+end:
     if (flv->flags & FLV_NO_SEQUENCE_END) {
         av_log(s, AV_LOG_DEBUG, "FLV no sequence end mode open\n");
     } else {
@@ -547,6 +825,11 @@  static int flv_write_trailer(AVFormatContext *s)
 
     file_size = avio_tell(pb);
 
+    if (build_keyframes_idx) {
+        flv->datasize = file_size - flv->datastart_offset;
+        avio_seek(pb, flv->datasize_offset, SEEK_SET);
+        put_amf_double(pb, flv->datasize);
+    }
     if (pb->seekable) {
         /* update information */
         if (avio_seek(pb, flv->duration_offset, SEEK_SET) < 0) {
@@ -574,6 +857,7 @@  static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
     int size = pkt->size;
     uint8_t *data = NULL;
     int flags = -1, flags_size, ret;
+    int64_t cur_offset = avio_tell(pb);
 
     if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A ||
         par->codec_id == AV_CODEC_ID_VP6  || par->codec_id == AV_CODEC_ID_AAC)
@@ -727,6 +1011,32 @@  static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
                               pkt->pts + flv->delay + pkt->duration);
     }
 
+    if (flv->flags & FLV_ADD_KEYFRAME_INDEX) {
+        switch (par->codec_type) {
+            case AVMEDIA_TYPE_VIDEO:
+                flv->videosize += (avio_tell(pb) - cur_offset);
+                flv->lasttimestamp = flv->acurframeindex / flv->framerate;
+                if (pkt->flags & AV_PKT_FLAG_KEY) {
+                    double ts = flv->acurframeindex / flv->framerate;
+                    int64_t pos = cur_offset;
+
+                    flv->lastkeyframetimestamp = flv->acurframeindex / flv->framerate;
+                    flv->lastkeyframelocation = pos;
+                    flv_append_keyframe_info(s, flv, ts, pos);
+                }
+                flv->acurframeindex++;
+                break;
+
+            case AVMEDIA_TYPE_AUDIO:
+                flv->audiosize += (avio_tell(pb) - cur_offset);
+                break;
+
+            default:
+                av_log(s, AV_LOG_WARNING, "par->codec_type is type = [%d]\n", par->codec_type);
+                break;
+        }
+    }
+
     av_free(data);
 
     return pb->error;
@@ -736,6 +1046,7 @@  static const AVOption options[] = {
     { "flvflags", "FLV muxer flags", offsetof(FLVContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
     { "aac_seq_header_detect", "Put AAC sequence header based on stream data", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_AAC_SEQ_HEADER_DETECT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
     { "no_sequence_end", "disable sequence end for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_SEQUENCE_END}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
+    { "add_keyframe_index", "Add keyframe index metadata", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_ADD_KEYFRAME_INDEX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
     { NULL },
 };
 
diff --git a/tests/Makefile b/tests/Makefile
index 8e810ff..07e839e 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -132,6 +132,7 @@  include $(SRC_PATH)/tests/fate/fifo-muxer.mak
 include $(SRC_PATH)/tests/fate/filter-audio.mak
 include $(SRC_PATH)/tests/fate/filter-video.mak
 include $(SRC_PATH)/tests/fate/flac.mak
+include $(SRC_PATH)/tests/fate/flvenc.mak
 include $(SRC_PATH)/tests/fate/gapless.mak
 include $(SRC_PATH)/tests/fate/gif.mak
 include $(SRC_PATH)/tests/fate/h264.mak
diff --git a/tests/fate-run.sh b/tests/fate-run.sh
index c640cc5..29a36ce 100755
--- a/tests/fate-run.sh
+++ b/tests/fate-run.sh
@@ -129,6 +129,10 @@  framecrc(){
     ffmpeg "$@" -flags +bitexact -fflags +bitexact -f framecrc -
 }
 
+ffmetadata(){
+    ffmpeg "$@" -flags +bitexact -fflags +bitexact -f ffmetadata -
+}
+
 framemd5(){
     ffmpeg "$@" -flags +bitexact -fflags +bitexact -f framemd5 -
 }
diff --git a/tests/fate/flvenc.mak b/tests/fate/flvenc.mak
new file mode 100644
index 0000000..2f05d5a
--- /dev/null
+++ b/tests/fate/flvenc.mak
@@ -0,0 +1,11 @@ 
+tests/data/add_keyframe_index.flv: TAG = GEN
+tests/data/add_keyframe_index.flv: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data
+	$(M)$(TARGET_EXEC) $(TARGET_PATH)/$< \
+		-f lavfi -i "color=c=red:size=352x288" -metadata "encoder=Lavf" -pix_fmt yuv420p -c:v flv1 -b:v 100k -g 7 -f flv -flags +bitexact \
+		-flvflags add_keyframe_index -s 352x288 -t 5 -y $(TARGET_PATH)/tests/data/add_keyframe_index.flv 2> /dev/null;
+
+FATE_AFILTER-$(call ALLYES, FLV_MUXER FLV_DEMUXER AVDEVICE TESTSRC_FILTER LAVFI_INDEV FLV_ENCODER) += fate-flv-add_keyframe_index
+fate-flv-add_keyframe_index: tests/data/add_keyframe_index.flv
+fate-flv-add_keyframe_index: CMD = ffmetadata -flags +bitexact -i $(TARGET_PATH)/tests/data/add_keyframe_index.flv
+
+
diff --git a/tests/ref/fate/flv-add_keyframe_index b/tests/ref/fate/flv-add_keyframe_index
new file mode 100644
index 0000000..8e12244
--- /dev/null
+++ b/tests/ref/fate/flv-add_keyframe_index
@@ -0,0 +1,12 @@ 
+;FFMETADATA1
+hasVideo=true
+hasKeyframes=true
+hasAudio=false
+hasMetadata=true
+canSeekToEnd=true
+datasize=55566
+videosize=55207
+audiosize=0
+lasttimestamp=5
+lastkeyframetimestamp=5
+lastkeyframelocation=52623