Merge commit 'd754ed41727b1fcbab335b510248a9758a73320c'
authorMichael Niedermayer <michaelni@gmx.at>
Wed, 18 Jun 2014 15:47:12 +0000 (17:47 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Wed, 18 Jun 2014 15:47:12 +0000 (17:47 +0200)
* commit 'd754ed41727b1fcbab335b510248a9758a73320c':
  riffenc: take an AVStream instead of an AVCodecContext

Conflicts:
libavformat/nutenc.c

Merged-by: Michael Niedermayer <michaelni@gmx.at>
1  2 
libavformat/avienc.c
libavformat/nutenc.c
libavformat/riff.h
libavformat/riffenc.c

diff --combined libavformat/avienc.c
@@@ -2,39 -2,29 +2,39 @@@
   * AVI muxer
   * Copyright (c) 2000 Fabrice Bellard
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * FFmpeg is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * FFmpeg is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
 - * License along with Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
 +
 +//#define DEBUG
 +
  #include "avformat.h"
  #include "internal.h"
  #include "avi.h"
  #include "avio_internal.h"
  #include "riff.h"
 +#include "mpegts.h"
 +#include "libavformat/avlanguage.h"
 +#include "libavutil/avstring.h"
  #include "libavutil/intreadwrite.h"
  #include "libavutil/dict.h"
 +#include "libavutil/avassert.h"
 +#include "libavutil/timestamp.h"
 +#include "libavutil/pixdesc.h"
 +#include "libavcodec/raw.h"
  
  /*
   * TODO:
@@@ -62,10 -52,9 +62,10 @@@ typedef struct 
  
  typedef struct  {
      int64_t frames_hdr_strm;
 -    int audio_strm_length;
 +    int64_t audio_strm_length;
      int packet_count;
      int entry;
 +    int max_size;
  
      AVIIndex indexes;
  } AVIStream;
@@@ -128,10 -117,10 +128,10 @@@ static int avi_write_counters(AVFormatC
      for (n = 0; n < s->nb_streams; n++) {
          AVIStream *avist = s->streams[n]->priv_data;
  
 -        assert(avist->frames_hdr_strm);
 +        av_assert0(avist->frames_hdr_strm);
          stream = s->streams[n]->codec;
          avio_seek(pb, avist->frames_hdr_strm, SEEK_SET);
-         ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale);
+         ff_parse_specific_params(s->streams[n], &au_byterate, &au_ssize, &au_scale);
          if (au_ssize == 0)
              avio_wl32(pb, avist->packet_count);
          else
              nb_frames = FFMAX(nb_frames, avist->packet_count);
      }
      if (riff_id == 1) {
 -        assert(avi->frames_hdr_all);
 +        av_assert0(avi->frames_hdr_all);
          avio_seek(pb, avi->frames_hdr_all, SEEK_SET);
          avio_wl32(pb, nb_frames);
      }
@@@ -157,12 -146,11 +157,12 @@@ static int avi_write_header(AVFormatCon
      AVCodecContext *video_enc;
      int64_t list1, list2, strh, strf;
      AVDictionaryEntry *t = NULL;
 +    int padding;
  
      if (s->nb_streams > AVI_MAX_STREAM_COUNT) {
          av_log(s, AV_LOG_ERROR, "AVI does not support >%d streams\n",
                 AVI_MAX_STREAM_COUNT);
 -        return -1;
 +        return AVERROR(EINVAL);
      }
  
      for (n = 0; n < s->nb_streams; n++) {
          avio_wl16(pb, 0); /* language */
          avio_wl32(pb, 0); /* initial frame */
  
-         ff_parse_specific_params(enc, &au_byterate, &au_ssize, &au_scale);
+         ff_parse_specific_params(st, &au_byterate, &au_ssize, &au_scale);
  
 +        if (   enc->codec_type == AVMEDIA_TYPE_VIDEO
 +            && enc->codec_id != AV_CODEC_ID_XSUB
 +            && au_byterate > 1000LL*au_scale) {
 +            au_byterate = 600;
 +            au_scale    = 1;
 +        }
 +        avpriv_set_pts_info(st, 64, au_scale, au_byterate);
 +        if (enc->codec_id == AV_CODEC_ID_XSUB)
 +            au_scale = au_byterate = 0;
 +
          avio_wl32(pb, au_scale); /* scale */
          avio_wl32(pb, au_byterate); /* rate */
 -        avpriv_set_pts_info(st, 64, au_scale, au_byterate);
  
          avio_wl32(pb, 0); /* start */
          /* remember this offset to fill later */
          else
              avio_wl32(pb, 0);  /* length, XXX: filled later */
  
 -        /* suggested buffer size */ //FIXME set at the end to largest chunk
 +        /* suggested buffer size, is set to largest chunk size in avi_write_trailer */
          if (enc->codec_type == AVMEDIA_TYPE_VIDEO)
              avio_wl32(pb, 1024 * 1024);
          else if (enc->codec_type == AVMEDIA_TYPE_AUDIO)
          ff_end_tag(pb, strh);
  
          if (enc->codec_type != AVMEDIA_TYPE_DATA) {
 +            int ret;
 +            enum AVPixelFormat pix_fmt;
 +
              strf = ff_start_tag(pb, "strf");
              switch (enc->codec_type) {
              case AVMEDIA_TYPE_SUBTITLE:
                  if (enc->codec_id != AV_CODEC_ID_XSUB)
                      break;
              case AVMEDIA_TYPE_VIDEO:
 -                ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 0);
 +                /* WMP expects RGB 5:5:5 rawvideo in avi to have bpp set to 16. */
 +                if (  !enc->codec_tag
 +                    && enc->codec_id == AV_CODEC_ID_RAWVIDEO
 +                    && enc->pix_fmt == AV_PIX_FMT_RGB555LE
 +                    && enc->bits_per_coded_sample == 15)
 +                    enc->bits_per_coded_sample = 16;
 +                ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 0, 0);
 +                pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_avi,
 +                                              enc->bits_per_coded_sample);
 +                if (   !enc->codec_tag
 +                    && enc->codec_id == AV_CODEC_ID_RAWVIDEO
 +                    && enc->pix_fmt != pix_fmt
 +                    && enc->pix_fmt != AV_PIX_FMT_NONE)
 +                    av_log(s, AV_LOG_ERROR, "%s rawvideo cannot be written to avi, output file will be unreadable\n",
 +                          av_get_pix_fmt_name(enc->pix_fmt));
                  break;
              case AVMEDIA_TYPE_AUDIO:
 -                if (ff_put_wav_header(pb, enc) < 0)
 -                    return -1;
 +                if ((ret = ff_put_wav_header(pb, enc, 0)) < 0)
 +                    return ret;
                  break;
              default:
 -                return -1;
 +                av_log(s, AV_LOG_ERROR,
 +                    "Invalid or not supported codec type '%s' found in the input\n",
 +                    (char *)av_x_if_null(av_get_media_type_string(enc->codec_type), "?"));
 +                return AVERROR(EINVAL);
              }
              ff_end_tag(pb, strf);
              if ((t = av_dict_get(st->metadata, "title", NULL, 0))) {
                  ff_riff_write_info_tag(s->pb, "strn", t->value);
                  t = NULL;
              }
 +            if (enc->codec_id == AV_CODEC_ID_XSUB
 +            && (t = av_dict_get(s->streams[i]->metadata, "language", NULL, 0))) {
 +                const char* langstr = av_convert_lang_to(t->value, AV_LANG_ISO639_1);
 +                t = NULL;
 +                if (langstr) {
 +                    char* str = av_asprintf("Subtitle - %s-xx;02", langstr);
 +                    ff_riff_write_info_tag(s->pb, "strn", str);
 +                    av_free(str);
 +                }
 +            }
          }
  
          if (pb->seekable) {
  
      ff_riff_write_info(s);
  
 +
 +    padding = s->metadata_header_padding;
 +    if (padding < 0)
 +        padding = 1016;
 +
      /* some padding for easier tag editing */
 -    list2 = ff_start_tag(pb, "JUNK");
 -    for (i = 0; i < 1016; i += 4)
 -        avio_wl32(pb, 0);
 -    ff_end_tag(pb, list2);
 +    if (padding) {
 +        list2 = ff_start_tag(pb, "JUNK");
 +        for (i = padding; i > 0; i -= 4)
 +            avio_wl32(pb, 0);
 +        ff_end_tag(pb, list2);
 +    }
  
      avi->movi_list = ff_start_tag(pb, "LIST");
      ffio_wfourcc(pb, "movi");
@@@ -456,13 -398,10 +456,13 @@@ static int avi_write_ix(AVFormatContex
      char ix_tag[] = "ix00";
      int i, j;
  
 -    assert(pb->seekable);
 +    av_assert0(pb->seekable);
  
 -    if (avi->riff_id > AVI_MASTER_INDEX_SIZE)
 -        return -1;
 +    if (avi->riff_id > AVI_MASTER_INDEX_SIZE) {
 +        av_log(s, AV_LOG_ERROR, "Invalid riff index %d > %d\n",
 +               avi->riff_id, AVI_MASTER_INDEX_SIZE);
 +        return AVERROR(EINVAL);
 +    }
  
      for (i = 0; i < s->nb_streams; i++) {
          AVIStream *avist = s->streams[i]->priv_data;
@@@ -571,27 -510,15 +571,27 @@@ static int avi_write_packet(AVFormatCon
      AVIStream *avist    = s->streams[stream_index]->priv_data;
      AVCodecContext *enc = s->streams[stream_index]->codec;
  
 +    if (enc->codec_id == AV_CODEC_ID_H264 && enc->codec_tag == MKTAG('H','2','6','4')) {
 +        int ret = ff_check_h264_startcode(s, s->streams[stream_index], pkt);
 +        if (ret < 0)
 +            return ret;
 +    }
 +    av_dlog(s, "dts:%s packet_count:%d stream_index:%d\n", av_ts2str(pkt->dts), avist->packet_count, stream_index);
      while (enc->block_align == 0 && pkt->dts != AV_NOPTS_VALUE &&
 -           pkt->dts > avist->packet_count) {
 +           pkt->dts > avist->packet_count && enc->codec_id != AV_CODEC_ID_XSUB && avist->packet_count) {
          AVPacket empty_packet;
  
 +        if (pkt->dts - avist->packet_count > 60000) {
 +            av_log(s, AV_LOG_ERROR, "Too large number of skipped frames %"PRId64" > 60000\n", pkt->dts - avist->packet_count);
 +            return AVERROR(EINVAL);
 +        }
 +
          av_init_packet(&empty_packet);
          empty_packet.size         = 0;
          empty_packet.data         = NULL;
          empty_packet.stream_index = stream_index;
          avi_write_packet(s, &empty_packet);
 +        av_dlog(s, "dup dts:%s packet_count:%d\n", av_ts2str(pkt->dts), avist->packet_count);
      }
      avist->packet_count++;
  
          avist->audio_strm_length += size;
  
      if (s->pb->seekable) {
 -        int err;
          AVIIndex *idx = &avist->indexes;
          int cl = idx->entry / AVI_INDEX_CLUSTER_SIZE;
          int id = idx->entry % AVI_INDEX_CLUSTER_SIZE;
          if (idx->ents_allocated <= idx->entry) {
 -            if ((err = av_reallocp(&idx->cluster,
 -                                   (cl + 1) * sizeof(*idx->cluster))) < 0) {
 +            idx->cluster = av_realloc_f(idx->cluster, sizeof(void*), cl+1);
 +            if (!idx->cluster) {
                  idx->ents_allocated = 0;
                  idx->entry          = 0;
 -                return err;
 +                return AVERROR(ENOMEM);
              }
              idx->cluster[cl] =
                  av_malloc(AVI_INDEX_CLUSTER_SIZE * sizeof(AVIIentry));
              if (!idx->cluster[cl])
 -                return -1;
 +                return AVERROR(ENOMEM);
              idx->ents_allocated += AVI_INDEX_CLUSTER_SIZE;
          }
  
          idx->cluster[cl][id].flags = flags;
          idx->cluster[cl][id].pos   = avio_tell(pb) - avi->movi_list;
          idx->cluster[cl][id].len   = size;
 +        avist->max_size = FFMAX(avist->max_size, size);
          idx->entry++;
      }
  
@@@ -694,13 -621,9 +694,13 @@@ static int avi_write_trailer(AVFormatCo
      for (i = 0; i < s->nb_streams; i++) {
          AVIStream *avist = s->streams[i]->priv_data;
          for (j = 0; j < avist->indexes.ents_allocated / AVI_INDEX_CLUSTER_SIZE; j++)
 -            av_free(avist->indexes.cluster[j]);
 +            av_freep(&avist->indexes.cluster[j]);
          av_freep(&avist->indexes.cluster);
          avist->indexes.ents_allocated = avist->indexes.entry = 0;
 +        if (pb->seekable) {
 +            avio_seek(pb, avist->frames_hdr_strm + 4, SEEK_SET);
 +            avio_wl32(pb, avist->max_size);
 +        }
      }
  
      return res;
diff --combined libavformat/nutenc.c
@@@ -2,20 -2,20 +2,20 @@@
   * nut muxer
   * Copyright (c) 2004-2007 Michael Niedermayer
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * FFmpeg is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * FFmpeg is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
 - * License along with Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
  #include "libavutil/mathematics.h"
  #include "libavutil/tree.h"
  #include "libavutil/dict.h"
 +#include "libavutil/avassert.h"
  #include "libavutil/time.h"
  #include "libavutil/opt.h"
 +#include "libavcodec/bytestream.h"
  #include "libavcodec/mpegaudiodata.h"
  #include "nut.h"
  #include "internal.h"
@@@ -66,9 -64,12 +66,9 @@@ static int find_expected_header(AVCodec
          lsf           = sample_rate < (24000 + 32000) / 2;
          mpeg25        = sample_rate < (12000 + 16000) / 2;
          sample_rate <<= lsf + mpeg25;
 -        if (sample_rate < (32000 + 44100) / 2)
 -            sample_rate_index = 2;
 -        else if (sample_rate < (44100 + 48000) / 2)
 -            sample_rate_index = 0;
 -        else
 -            sample_rate_index = 1;
 +        if      (sample_rate < (32000 + 44100) / 2) sample_rate_index = 2;
 +        else if (sample_rate < (44100 + 48000) / 2) sample_rate_index = 0;
 +        else                                        sample_rate_index = 1;
  
          sample_rate = avpriv_mpa_freq_tab[sample_rate_index] >> (lsf + mpeg25);
  
      return 0;
  }
  
 -static int find_header_idx(AVFormatContext *s, AVCodecContext *c, int size,
 -                           int frame_type)
 +static int find_header_idx(AVFormatContext *s, AVCodecContext *c, int size, int frame_type)
  {
      NUTContext *nut = s->priv_data;
      uint8_t out[64];
@@@ -171,19 -173,6 +171,19 @@@ static void build_frame_code(AVFormatCo
          int is_audio          = codec->codec_type == AVMEDIA_TYPE_AUDIO;
          int intra_only        = /*codec->intra_only || */ is_audio;
          int pred_count;
 +        int frame_size = 0;
 +
 +        if (codec->codec_type == AVMEDIA_TYPE_AUDIO) {
 +            frame_size = av_get_audio_frame_duration(codec, 0);
 +            if (codec->codec_id == AV_CODEC_ID_VORBIS && !frame_size)
 +                frame_size = 64;
 +        } else {
 +            AVRational f = av_div_q(codec->time_base, *nut->stream[stream_id].time_base);
 +            if (f.den == 1 && f.num>0)
 +                frame_size = f.num;
 +        }
 +        if (!frame_size)
 +            frame_size = 1;
  
          for (key_frame = 0; key_frame < 2; key_frame++) {
              if (!intra_only || !keyframe_0_esc || key_frame != 0) {
          }
  
          key_frame = intra_only;
 +#if 1
          if (is_audio) {
              int frame_bytes = codec->frame_size * (int64_t)codec->bit_rate /
                                (8 * codec->sample_rate);
              int pts;
 -            for (pts = 0; pts < 2; pts++)
 +            for (pts = 0; pts < 2; pts++) {
                  for (pred = 0; pred < 2; pred++) {
 -                    FrameCode *ft = &nut->frame_code[start2];
 +                    FrameCode *ft  = &nut->frame_code[start2];
                      ft->flags      = FLAG_KEY * key_frame;
                      ft->stream_id  = stream_id;
                      ft->size_mul   = frame_bytes + 2;
                      ft->size_lsb   = frame_bytes + pred;
 -                    ft->pts_delta  = pts;
 +                    ft->pts_delta  = pts * frame_size;
                      ft->header_idx = find_header_idx(s, codec, frame_bytes + pred, key_frame);
                      start2++;
                  }
 +            }
          } else {
              FrameCode *ft = &nut->frame_code[start2];
              ft->flags     = FLAG_KEY | FLAG_SIZE_MSB;
              ft->stream_id = stream_id;
              ft->size_mul  = 1;
 -            ft->pts_delta = 1;
 +            ft->pts_delta = frame_size;
              start2++;
          }
 +#endif
  
          if (codec->has_b_frames) {
              pred_count    = 5;
              int start3 = start2 + (end2 - start2) * pred / pred_count;
              int end3   = start2 + (end2 - start2) * (pred + 1) / pred_count;
  
 +            pred_table[pred] *= frame_size;
 +
              for (index = start3; index < end3; index++) {
                  FrameCode *ft = &nut->frame_code[index];
                  ft->flags     = FLAG_KEY * key_frame;
              }
          }
      }
 -    memmove(&nut->frame_code['N' + 1], &nut->frame_code['N'],
 -            sizeof(FrameCode) * (255 - 'N'));
 +    memmove(&nut->frame_code['N' + 1], &nut->frame_code['N'], sizeof(FrameCode) * (255 - 'N'));
      nut->frame_code[0].flags       =
          nut->frame_code[255].flags =
          nut->frame_code['N'].flags = FLAG_INVALID;
  }
  
 -static void put_tt(NUTContext *nut, AVRational *time_base, AVIOContext *bc,
 -                   uint64_t val)
 +static void put_tt(NUTContext *nut, AVRational *time_base, AVIOContext *bc, uint64_t val)
  {
      val *= nut->time_base_count;
      val += time_base - nut->time_base;
@@@ -300,7 -286,8 +300,7 @@@ static inline void ff_put_v_trace(AVIOC
      ff_put_v(bc, v);
  }
  
 -static inline void put_s_trace(AVIOContext *bc, int64_t v, const char *file,
 -                               const char *func, int line)
 +static inline void put_s_trace(AVIOContext *bc, int64_t v, const char *file, const char *func, int line)
  {
      av_log(NULL, AV_LOG_DEBUG, "put_s %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line);
  
@@@ -341,8 -328,6 +341,8 @@@ static void write_mainheader(NUTContex
      int64_t tmp_match;
  
      ff_put_v(bc, nut->version);
 +    if (nut->version > 3)
 +        ff_put_v(bc, nut->minor_version = 1);
      ff_put_v(bc, nut->avf->nb_streams);
      ff_put_v(bc, nut->max_distance);
      ff_put_v(bc, nut->time_base_count);
          tmp_fields = 0;
          tmp_size   = 0;
  //        tmp_res=0;
 -        if (tmp_pts != nut->frame_code[i].pts_delta)
 -            tmp_fields = 1;
 -        if (tmp_mul != nut->frame_code[i].size_mul)
 -            tmp_fields = 2;
 -        if (tmp_stream != nut->frame_code[i].stream_id)
 -            tmp_fields = 3;
 -        if (tmp_size != nut->frame_code[i].size_lsb)
 -            tmp_fields = 4;
 -//        if(tmp_res    != nut->frame_code[i].res            ) tmp_fields=5;
 -        if (tmp_head_idx != nut->frame_code[i].header_idx)
 -            tmp_fields = 8;
 +        if (tmp_pts      != nut->frame_code[i].pts_delta ) tmp_fields = 1;
 +        if (tmp_mul      != nut->frame_code[i].size_mul  ) tmp_fields = 2;
 +        if (tmp_stream   != nut->frame_code[i].stream_id ) tmp_fields = 3;
 +        if (tmp_size     != nut->frame_code[i].size_lsb  ) tmp_fields = 4;
 +//        if (tmp_res    != nut->frame_code[i].res            ) tmp_fields=5;
 +        if (tmp_head_idx != nut->frame_code[i].header_idx) tmp_fields = 8;
  
          tmp_pts    = nut->frame_code[i].pts_delta;
          tmp_flags  = nut->frame_code[i].flags;
  
          ff_put_v(bc, tmp_flags);
          ff_put_v(bc, tmp_fields);
 -        if (tmp_fields > 0)
 -            put_s(bc, tmp_pts);
 -        if (tmp_fields > 1)
 -            ff_put_v(bc, tmp_mul);
 -        if (tmp_fields > 2)
 -            ff_put_v(bc, tmp_stream);
 -        if (tmp_fields > 3)
 -            ff_put_v(bc, tmp_size);
 -        if (tmp_fields > 4)
 -            ff_put_v(bc, 0 /*tmp_res*/);
 -        if (tmp_fields > 5)
 -            ff_put_v(bc, j);
 -        if (tmp_fields > 6)
 -            ff_put_v(bc, tmp_match);
 -        if (tmp_fields > 7)
 -            ff_put_v(bc, tmp_head_idx);
 +        if (tmp_fields > 0) put_s(bc, tmp_pts);
 +        if (tmp_fields > 1) ff_put_v(bc, tmp_mul);
 +        if (tmp_fields > 2) ff_put_v(bc, tmp_stream);
 +        if (tmp_fields > 3) ff_put_v(bc, tmp_size);
 +        if (tmp_fields > 4) ff_put_v(bc, 0 /*tmp_res*/);
 +        if (tmp_fields > 5) ff_put_v(bc, j);
 +        if (tmp_fields > 6) ff_put_v(bc, tmp_match);
 +        if (tmp_fields > 7) ff_put_v(bc, tmp_head_idx);
      }
      ff_put_v(bc, nut->header_count - 1);
      for (i = 1; i < nut->header_count; i++) {
          avio_write(bc, nut->header[i], nut->header_len[i]);
      }
      // flags had been effectively introduced in version 4
 -    if (nut->version > NUT_STABLE_VERSION)
 +    if (nut->version > 3)
          ff_put_v(bc, nut->flags);
  }
  
@@@ -419,17 -417,30 +419,17 @@@ static int write_streamheader(AVFormatC
  {
      NUTContext *nut       = avctx->priv_data;
      AVCodecContext *codec = st->codec;
 -    unsigned codec_tag    = av_codec_get_tag(ff_nut_codec_tags, codec->codec_id);
  
      ff_put_v(bc, i);
      switch (codec->codec_type) {
 -    case AVMEDIA_TYPE_VIDEO:
 -        ff_put_v(bc, 0);
 -        break;
 -    case AVMEDIA_TYPE_AUDIO:
 -        ff_put_v(bc, 1);
 -        break;
 -    case AVMEDIA_TYPE_SUBTITLE:
 -        ff_put_v(bc, 2);
 -        break;
 -    default:
 -        ff_put_v(bc, 3);
 -        break;
 +    case AVMEDIA_TYPE_VIDEO:    ff_put_v(bc, 0); break;
 +    case AVMEDIA_TYPE_AUDIO:    ff_put_v(bc, 1); break;
 +    case AVMEDIA_TYPE_SUBTITLE: ff_put_v(bc, 2); break;
 +    default:                    ff_put_v(bc, 3); break;
      }
      ff_put_v(bc, 4);
 -
 -    if (!codec_tag || codec->codec_id == AV_CODEC_ID_RAWVIDEO)
 -        codec_tag = codec->codec_tag;
 -
 -    if (codec_tag) {
 -        avio_wl32(bc, codec_tag);
 +    if (codec->codec_tag) {
 +        avio_wl32(bc, codec->codec_tag);
      } else {
          av_log(avctx, AV_LOG_ERROR, "No codec tag defined for stream %d\n", i);
          return AVERROR(EINVAL);
@@@ -505,28 -516,20 +505,28 @@@ static int write_globalinfo(NUTContext 
      return 0;
  }
  
 -static int write_streaminfo(NUTContext *nut, AVIOContext *bc, int stream_id){
 +static int write_streaminfo(NUTContext *nut, AVIOContext *bc, int stream_id) {
      AVFormatContext *s= nut->avf;
      AVStream* st = s->streams[stream_id];
 +    AVDictionaryEntry *t = NULL;
      AVIOContext *dyn_bc;
      uint8_t *dyn_buf=NULL;
      int count=0, dyn_size, i;
      int ret = avio_open_dyn_buf(&dyn_bc);
 -    if(ret < 0)
 +    if (ret < 0)
          return ret;
  
 +    while ((t = av_dict_get(st->metadata, "", t, AV_DICT_IGNORE_SUFFIX)))
 +        count += add_info(dyn_bc, t->key, t->value);
      for (i=0; ff_nut_dispositions[i].flag; ++i) {
          if (st->disposition & ff_nut_dispositions[i].flag)
              count += add_info(dyn_bc, "Disposition", ff_nut_dispositions[i].str);
      }
 +    if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
 +        uint8_t buf[256];
 +        snprintf(buf, sizeof(buf), "%d/%d", st->codec->time_base.den, st->codec->time_base.num);
 +        count += add_info(dyn_bc, "r_frame_rate", buf);
 +    }
      dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
  
      if (count) {
@@@ -572,58 -575,6 +572,58 @@@ static int write_chapter(NUTContext *nu
      return 0;
  }
  
 +static int write_index(NUTContext *nut, AVIOContext *bc) {
 +    int i;
 +    Syncpoint dummy= { .pos= 0 };
 +    Syncpoint *next_node[2] = { NULL };
 +    int64_t startpos = avio_tell(bc);
 +    int64_t payload_size;
 +
 +    put_tt(nut, nut->max_pts_tb, bc, nut->max_pts);
 +
 +    ff_put_v(bc, nut->sp_count);
 +
 +    for (i=0; i<nut->sp_count; i++) {
 +        av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp, (void**)next_node);
 +        ff_put_v(bc, (next_node[1]->pos >> 4) - (dummy.pos>>4));
 +        dummy.pos = next_node[1]->pos;
 +    }
 +
 +    for (i=0; i<nut->avf->nb_streams; i++) {
 +        StreamContext *nus= &nut->stream[i];
 +        int64_t last_pts= -1;
 +        int j, k;
 +        for (j=0; j<nut->sp_count; j++) {
 +            int flag;
 +            int n = 0;
 +
 +            if (j && nus->keyframe_pts[j] == nus->keyframe_pts[j-1]) {
 +                av_log(nut->avf, AV_LOG_WARNING, "Multiple keyframes with same PTS\n");
 +                nus->keyframe_pts[j] = AV_NOPTS_VALUE;
 +            }
 +
 +            flag = (nus->keyframe_pts[j] != AV_NOPTS_VALUE) ^ (j+1 == nut->sp_count);
 +            for (; j<nut->sp_count && (nus->keyframe_pts[j] != AV_NOPTS_VALUE) == flag; j++)
 +                n++;
 +
 +            ff_put_v(bc, 1 + 2*flag + 4*n);
 +            for (k= j - n; k<=j && k<nut->sp_count; k++) {
 +                if (nus->keyframe_pts[k] == AV_NOPTS_VALUE)
 +                    continue;
 +                av_assert0(nus->keyframe_pts[k] > last_pts);
 +                ff_put_v(bc, nus->keyframe_pts[k] - last_pts);
 +                last_pts = nus->keyframe_pts[k];
 +            }
 +        }
 +    }
 +
 +    payload_size = avio_tell(bc) - startpos + 8 + 4;
 +
 +    avio_wb64(bc, 8 + payload_size + av_log2(payload_size) / 7 + 1 + 4*(payload_size > 4096));
 +
 +    return 0;
 +}
 +
  static int write_headers(AVFormatContext *avctx, AVIOContext *bc)
  {
      NUTContext *nut = avctx->priv_data;
@@@ -697,8 -648,8 +697,8 @@@ static int nut_write_header(AVFormatCon
  
      nut->avf = s;
  
 -    nut->version = NUT_STABLE_VERSION + !!nut->flags;
 -    if (nut->flags && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
 +    nut->version = FFMAX(NUT_STABLE_VERSION, 3 + !!nut->flags);
 +    if (nut->version > 3 && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
          av_log(s, AV_LOG_ERROR,
                 "The additional syncpoint modes require version %d, "
                 "that is currently not finalized, "
          return AVERROR_EXPERIMENTAL;
      }
  
 -    nut->stream = av_mallocz(sizeof(StreamContext) * s->nb_streams);
 -    if (s->nb_chapters)
 -        nut->chapter = av_mallocz(sizeof(ChapterContext) * s->nb_chapters);
 -    nut->time_base = av_mallocz(sizeof(AVRational) * (s->nb_streams +
 -                                                      s->nb_chapters));
 -    if (!nut->stream || (s->nb_chapters && !nut->chapter) || !nut->time_base) {
 +    nut->stream   = av_calloc(s->nb_streams,  sizeof(*nut->stream ));
 +    nut->chapter  = av_calloc(s->nb_chapters, sizeof(*nut->chapter));
 +    nut->time_base= av_calloc(s->nb_streams +
 +                              s->nb_chapters, sizeof(*nut->time_base));
 +    if (!nut->stream || !nut->chapter || !nut->time_base) {
          av_freep(&nut->stream);
          av_freep(&nut->chapter);
          av_freep(&nut->time_base);
          AVStream *st = s->streams[i];
          int ssize;
          AVRational time_base;
-         ff_parse_specific_params(st->codec, &time_base.den, &ssize, &time_base.num);
 -        ff_parse_specific_params(st, &time_base.den, &ssize,
 -                                 &time_base.num);
++        ff_parse_specific_params(st, &time_base.den, &ssize, &time_base.num);
 +
 +        if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->codec->sample_rate) {
 +            time_base = (AVRational) {1, st->codec->sample_rate};
 +        } else {
 +            time_base = ff_choose_timebase(s, st, 48000);
 +        }
  
          avpriv_set_pts_info(st, 64, time_base.num, time_base.den);
  
      nut->max_distance = MAX_DISTANCE;
      build_elision_headers(s);
      build_frame_code(s);
 -    assert(nut->frame_code['N'].flags == FLAG_INVALID);
 +    av_assert0(nut->frame_code['N'].flags == FLAG_INVALID);
  
      avio_write(bc, ID_STRING, strlen(ID_STRING));
      avio_w8(bc, 0);
      if ((ret = write_headers(s, bc)) < 0)
          return ret;
  
 -    avio_flush(bc);
 +    if (s->avoid_negative_ts < 0)
 +        s->avoid_negative_ts = 1;
  
 -    //FIXME index
 +    avio_flush(bc);
  
      return 0;
  }
@@@ -794,8 -740,6 +794,8 @@@ static int get_needed_flags(NUTContext 
          flags |= FLAG_SIZE_MSB;
      if (pkt->pts - nus->last_pts != fc->pts_delta)
          flags |= FLAG_CODED_PTS;
 +    if (pkt->side_data_elems && nut->version > 3)
 +        flags |= FLAG_SM_DATA;
      if (pkt->size > 2 * nut->max_distance)
          flags |= FLAG_CHECKSUM;
      if (FFABS(pkt->pts - nus->last_pts) > nus->max_pts_distance)
@@@ -828,164 -772,24 +828,164 @@@ static int find_best_header_idx(NUTCont
      return best_i;
  }
  
 +static int write_sm_data(AVFormatContext *s, AVIOContext *bc, AVPacket *pkt, int is_meta)
 +{
 +    int ret, i, dyn_size;
 +    unsigned flags;
 +    AVIOContext *dyn_bc;
 +    int sm_data_count = 0;
 +    uint8_t tmp[256];
 +    uint8_t *dyn_buf;
 +
 +    ret = avio_open_dyn_buf(&dyn_bc);
 +    if (ret < 0)
 +        return ret;
 +
 +    for (i = 0; i<pkt->side_data_elems; i++) {
 +        const uint8_t *data = pkt->side_data[i].data;
 +        int size = pkt->side_data[i].size;
 +        const uint8_t *data_end = data + size;
 +
 +        if (is_meta) {
 +            if (   pkt->side_data[i].type == AV_PKT_DATA_METADATA_UPDATE
 +                || pkt->side_data[i].type == AV_PKT_DATA_STRINGS_METADATA) {
 +                if (!size || data[size-1]) {
 +                    ret = AVERROR(EINVAL);
 +                    goto fail;
 +                }
 +                while (data < data_end) {
 +                    const uint8_t *key = data;
 +                    const uint8_t *val = data + strlen(key) + 1;
 +
 +                    if(val >= data_end) {
 +                        ret = AVERROR(EINVAL);
 +                        goto fail;
 +                    }
 +                    put_str(dyn_bc, key);
 +                    put_s(dyn_bc, -1);
 +                    put_str(dyn_bc, val);
 +                    data = val + strlen(val) + 1;
 +                    sm_data_count++;
 +                }
 +            }
 +        } else {
 +            switch (pkt->side_data[i].type) {
 +            case AV_PKT_DATA_PALETTE:
 +            case AV_PKT_DATA_NEW_EXTRADATA:
 +            case AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL:
 +            default:
 +                if (pkt->side_data[i].type == AV_PKT_DATA_PALETTE) {
 +                    put_str(dyn_bc, "Palette");
 +                } else if(pkt->side_data[i].type == AV_PKT_DATA_NEW_EXTRADATA) {
 +                    put_str(dyn_bc, "Extradata");
 +                } else if(pkt->side_data[i].type == AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL) {
 +                    snprintf(tmp, sizeof(tmp), "CodecSpecificSide%"PRId64"", AV_RB64(data));
 +                    put_str(dyn_bc, tmp);
 +                } else {
 +                    snprintf(tmp, sizeof(tmp), "UserData%s-SD-%d",
 +                            (s->flags & AVFMT_FLAG_BITEXACT) ? "Lavf" : LIBAVFORMAT_IDENT,
 +                            pkt->side_data[i].type);
 +                    put_str(dyn_bc, tmp);
 +                }
 +                put_s(dyn_bc, -2);
 +                put_str(dyn_bc, "bin");
 +                ff_put_v(dyn_bc, pkt->side_data[i].size);
 +                avio_write(dyn_bc, data, pkt->side_data[i].size);
 +                sm_data_count++;
 +                break;
 +            case AV_PKT_DATA_PARAM_CHANGE:
 +                flags = bytestream_get_le32(&data);
 +                if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) {
 +                    put_str(dyn_bc, "Channels");
 +                    put_s(dyn_bc, bytestream_get_le32(&data));
 +                    sm_data_count++;
 +                }
 +                if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) {
 +                    put_str(dyn_bc, "ChannelLayout");
 +                    put_s(dyn_bc, -2);
 +                    put_str(dyn_bc, "u64");
 +                    ff_put_v(bc, 8);
 +                    avio_write(dyn_bc, data, 8); data+=8;
 +                    sm_data_count++;
 +                }
 +                if (flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) {
 +                    put_str(dyn_bc, "SampleRate");
 +                    put_s(dyn_bc, bytestream_get_le32(&data));
 +                    sm_data_count++;
 +                }
 +                if (flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS) {
 +                    put_str(dyn_bc, "Width");
 +                    put_s(dyn_bc, bytestream_get_le32(&data));
 +                    put_str(dyn_bc, "Height");
 +                    put_s(dyn_bc, bytestream_get_le32(&data));
 +                    sm_data_count+=2;
 +                }
 +                break;
 +            case AV_PKT_DATA_SKIP_SAMPLES:
 +                if (AV_RL32(data)) {
 +                    put_str(dyn_bc, "SkipStart");
 +                    put_s(dyn_bc, (unsigned)AV_RL32(data));
 +                    sm_data_count++;
 +                }
 +                if (AV_RL32(data+4)) {
 +                    put_str(dyn_bc, "SkipEnd");
 +                    put_s(dyn_bc, (unsigned)AV_RL32(data+4));
 +                    sm_data_count++;
 +                }
 +                break;
 +            case AV_PKT_DATA_METADATA_UPDATE:
 +            case AV_PKT_DATA_STRINGS_METADATA:
 +                // belongs into meta, not side data
 +                break;
 +            }
 +        }
 +    }
 +
 +fail:
 +    ff_put_v(bc, sm_data_count);
 +    dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
 +    avio_write(bc, dyn_buf, dyn_size);
 +    av_freep(&dyn_buf);
 +
 +    return ret;
 +}
 +
  static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
  {
      NUTContext *nut    = s->priv_data;
      StreamContext *nus = &nut->stream[pkt->stream_index];
 -    AVIOContext *bc    = s->pb, *dyn_bc;
 +    AVIOContext *bc    = s->pb, *dyn_bc, *sm_bc = NULL;
      FrameCode *fc;
      int64_t coded_pts;
 -    int best_length, frame_code, flags, needed_flags, i, header_idx,
 -        best_header_idx;
 +    int best_length, frame_code, flags, needed_flags, i, header_idx;
 +    int best_header_idx;
      int key_frame = !!(pkt->flags & AV_PKT_FLAG_KEY);
      int store_sp  = 0;
 -    int ret;
 +    int ret = 0;
 +    int sm_size = 0;
 +    int data_size = pkt->size;
 +    uint8_t *sm_buf = NULL;
  
      if (pkt->pts < 0) {
          av_log(s, AV_LOG_ERROR,
                 "Negative pts not supported stream %d, pts %"PRId64"\n",
                 pkt->stream_index, pkt->pts);
 -        return AVERROR_INVALIDDATA;
 +        if (pkt->pts == AV_NOPTS_VALUE)
 +            av_log(s, AV_LOG_ERROR, "Try to enable the genpts flag\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    if (pkt->side_data_elems && nut->version > 3) {
 +        ret = avio_open_dyn_buf(&sm_bc);
 +        if (ret < 0)
 +            return ret;
 +        ret = write_sm_data(s, sm_bc, pkt, 0);
 +        if (ret >= 0)
 +            ret = write_sm_data(s, sm_bc, pkt, 1);
 +        sm_size = avio_close_dyn_buf(sm_bc, &sm_buf);
 +        if (ret < 0)
 +            goto fail;
 +        data_size += sm_size;
      }
  
      if (1LL << (20 + 3 * nut->header_count) <= avio_tell(bc))
      if (key_frame && !(nus->last_flags & FLAG_KEY))
          store_sp = 1;
  
 -    if (pkt->size + 30 /*FIXME check*/ + avio_tell(bc) >=
 -        nut->last_syncpoint_pos + nut->max_distance)
 +    if (data_size + 30 /*FIXME check*/ + avio_tell(bc) >= nut->last_syncpoint_pos + nut->max_distance)
          store_sp = 1;
  
  //FIXME: Ensure store_sp is 1 in the first place.
  
      if (store_sp &&
          (!(nut->flags & NUT_PIPE) || nut->last_syncpoint_pos == INT_MIN)) {
 -        Syncpoint *sp, dummy = { .pos = INT64_MAX };
 +        int64_t sp_pos = INT64_MAX;
  
          ff_nut_reset_ts(nut, *nus->time_base, pkt->dts);
          for (i = 0; i < s->nb_streams; i++) {
                  AV_ROUND_DOWN);
              int index = av_index_search_timestamp(st, dts_tb,
                                                    AVSEEK_FLAG_BACKWARD);
 -            if (index >= 0)
 -                dummy.pos = FFMIN(dummy.pos, st->index_entries[index].pos);
 +            if (index >= 0) {
 +                sp_pos = FFMIN(sp_pos, st->index_entries[index].pos);
 +                if (!nut->write_index && 2*index > st->nb_index_entries) {
 +                    memmove(st->index_entries,
 +                            st->index_entries + index,
 +                            sizeof(*st->index_entries) * (st->nb_index_entries - index));
 +                    st->nb_index_entries -=  index;
 +                }
 +            }
          }
 -        if (dummy.pos == INT64_MAX)
 -            dummy.pos = 0;
 -        sp = av_tree_find(nut->syncpoints, &dummy, (void *)ff_nut_sp_pos_cmp,
 -                          NULL);
  
          nut->last_syncpoint_pos = avio_tell(bc);
          ret                     = avio_open_dyn_buf(&dyn_bc);
          if (ret < 0)
 -            return ret;
 +            goto fail;
          put_tt(nut, nus->time_base, dyn_bc, pkt->dts);
 -        ff_put_v(dyn_bc, sp ? (nut->last_syncpoint_pos - sp->pos) >> 4 : 0);
 +        ff_put_v(dyn_bc, sp_pos != INT64_MAX ? (nut->last_syncpoint_pos - sp_pos) >> 4 : 0);
  
          if (nut->flags & NUT_BROADCAST) {
              put_tt(nut, nus->time_base, dyn_bc,
          }
          put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE);
  
 +        if (nut->write_index) {
          if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0)
 -            return ret;
 +            goto fail;
 +
 +        if ((1ll<<60) % nut->sp_count == 0)
 +            for (i=0; i<s->nb_streams; i++) {
 +                int j;
 +                StreamContext *nus = &nut->stream[i];
 +                av_reallocp_array(&nus->keyframe_pts, 2*nut->sp_count, sizeof(*nus->keyframe_pts));
 +                if (!nus->keyframe_pts) {
 +                    ret = AVERROR(ENOMEM);
 +                    goto fail;
 +                }
 +                for (j=nut->sp_count == 1 ? 0 : nut->sp_count; j<2*nut->sp_count; j++)
 +                    nus->keyframe_pts[j] = AV_NOPTS_VALUE;
 +        }
 +        }
      }
 -    assert(nus->last_pts != AV_NOPTS_VALUE);
 +    av_assert0(nus->last_pts != AV_NOPTS_VALUE);
  
      coded_pts = pkt->pts & ((1 << nus->msb_pts_shift) - 1);
      if (ff_lsb2full(nus, coded_pts) != pkt->pts)
          if (flags & FLAG_STREAM_ID)
              length += ff_get_v_length(pkt->stream_index);
  
 -        if (pkt->size % fc->size_mul != fc->size_lsb)
 +        if (data_size % fc->size_mul != fc->size_lsb)
              continue;
          if (flags & FLAG_SIZE_MSB)
 -            length += ff_get_v_length(pkt->size / fc->size_mul);
 +            length += ff_get_v_length(data_size / fc->size_mul);
  
          if (flags & FLAG_CHECKSUM)
              length += 4;
          if (flags & FLAG_CODED_PTS)
              length += ff_get_v_length(coded_pts);
  
 -        if ((flags & FLAG_CODED)
 -            && nut->header_len[best_header_idx] >
 -            nut->header_len[fc->header_idx] + 1) {
 +        if (   (flags & FLAG_CODED)
 +            && nut->header_len[best_header_idx] > nut->header_len[fc->header_idx] + 1) {
              flags |= FLAG_HEADER_IDX;
          }
  
              frame_code  = i;
          }
      }
 -    assert(frame_code != -1);
 +    av_assert0(frame_code != -1);
      fc           = &nut->frame_code[frame_code];
      flags        = fc->flags;
      needed_flags = get_needed_flags(nut, nus, fc, pkt);
          ff_put_v(bc, (flags ^ needed_flags) & ~(FLAG_CODED));
          flags = needed_flags;
      }
 -    if (flags & FLAG_STREAM_ID)
 -        ff_put_v(bc, pkt->stream_index);
 -    if (flags & FLAG_CODED_PTS)
 -        ff_put_v(bc, coded_pts);
 -    if (flags & FLAG_SIZE_MSB)
 -        ff_put_v(bc, pkt->size / fc->size_mul);
 -    if (flags & FLAG_HEADER_IDX)
 -        ff_put_v(bc, header_idx = best_header_idx);
 +    if (flags & FLAG_STREAM_ID)  ff_put_v(bc, pkt->stream_index);
 +    if (flags & FLAG_CODED_PTS)  ff_put_v(bc, coded_pts);
 +    if (flags & FLAG_SIZE_MSB )  ff_put_v(bc, data_size / fc->size_mul);
 +    if (flags & FLAG_HEADER_IDX) ff_put_v(bc, header_idx = best_header_idx);
  
 -    if (flags & FLAG_CHECKSUM)
 -        avio_wl32(bc, ffio_get_checksum(bc));
 -    else
 -        ffio_get_checksum(bc);
 +    if (flags & FLAG_CHECKSUM)   avio_wl32(bc, ffio_get_checksum(bc));
 +    else                         ffio_get_checksum(bc);
 +
 +    if (flags & FLAG_SM_DATA) {
 +        avio_write(bc, sm_buf, sm_size);
 +    }
 +    avio_write(bc, pkt->data + nut->header_len[header_idx], pkt->size - nut->header_len[header_idx]);
  
 -    avio_write(bc, pkt->data + nut->header_len[header_idx],
 -               pkt->size - nut->header_len[header_idx]);
      nus->last_flags = flags;
      nus->last_pts   = pkt->pts;
  
      //FIXME just store one per syncpoint
 -    if (flags & FLAG_KEY && !(nut->flags & NUT_PIPE))
 +    if (flags & FLAG_KEY && !(nut->flags & NUT_PIPE)) {
          av_add_index_entry(
              s->streams[pkt->stream_index],
              nut->last_syncpoint_pos,
              0,
              0,
              AVINDEX_KEYFRAME);
 +        if (nus->keyframe_pts && nus->keyframe_pts[nut->sp_count] == AV_NOPTS_VALUE)
 +            nus->keyframe_pts[nut->sp_count] = pkt->pts;
 +    }
  
 -    return 0;
 +    if (!nut->max_pts_tb || av_compare_ts(nut->max_pts, *nut->max_pts_tb, pkt->pts, *nus->time_base) < 0) {
 +        nut->max_pts = pkt->pts;
 +        nut->max_pts_tb = nus->time_base;
 +    }
 +
 +fail:
 +    av_freep(&sm_buf);
 +
 +    return ret;
  }
  
  static int nut_write_trailer(AVFormatContext *s)
  {
      NUTContext *nut = s->priv_data;
 -    AVIOContext *bc = s->pb;
 +    AVIOContext *bc = s->pb, *dyn_bc;
 +    int i, ret;
  
      while (nut->header_count < 3)
          write_headers(s, bc);
  
 +    ret = avio_open_dyn_buf(&dyn_bc);
 +    if (ret >= 0 && nut->sp_count) {
 +        av_assert1(nut->write_index);
 +        write_index(nut, dyn_bc);
 +        put_packet(nut, bc, dyn_bc, 1, INDEX_STARTCODE);
 +    }
 +
      ff_nut_free_sp(nut);
 +    for (i=0; i<s->nb_streams; i++)
 +        av_freep(&nut->stream[i].keyframe_pts);
 +
      av_freep(&nut->stream);
      av_freep(&nut->chapter);
      av_freep(&nut->time_base);
@@@ -1204,7 -973,6 +1204,7 @@@ static const AVOption options[] = 
      { "default",     "",                                                0,             AV_OPT_TYPE_CONST, {.i64 = 0},             INT_MIN, INT_MAX, E, "syncpoints" },
      { "none",        "Disable syncpoints, low overhead and unseekable", 0,             AV_OPT_TYPE_CONST, {.i64 = NUT_PIPE},      INT_MIN, INT_MAX, E, "syncpoints" },
      { "timestamped", "Extend syncpoints with a wallclock timestamp",    0,             AV_OPT_TYPE_CONST, {.i64 = NUT_BROADCAST}, INT_MIN, INT_MAX, E, "syncpoints" },
 +    { "write_index", "Write index",                               OFFSET(write_index), AV_OPT_TYPE_INT,   {.i64 = 1},                   0,       1, E, },
      { NULL },
  };
  
diff --combined libavformat/riff.h
@@@ -2,20 -2,20 +2,20 @@@
   * RIFF common functions and data
   * copyright (c) 2000 Fabrice Bellard
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * FFmpeg is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * FFmpeg is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
 - * License along with Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
@@@ -43,31 -43,17 +43,31 @@@ void ff_end_tag(AVIOContext *pb, int64_
   * bits_per_encoded_sample fields. Does not read extradata.
   * @return codec tag
   */
 -int ff_get_bmp_header(AVIOContext *pb, AVStream *st);
 +int ff_get_bmp_header(AVIOContext *pb, AVStream *st, unsigned *esize);
 +
 +void ff_put_bmp_header(AVIOContext *pb, AVCodecContext *enc, const AVCodecTag *tags, int for_asf, int ignore_extradata);
 +
 +/**
 + * Tell ff_put_wav_header() to use WAVEFORMATEX even for PCM codecs.
 + */
 +#define FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX    0x00000001
 +
 +/**
 + * Write WAVEFORMAT header structure.
 + *
 + * @param flags a combination of FF_PUT_WAV_HEADER_* constants
 + *
 + * @return the size or -1 on error
 + */
 +int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc, int flags);
  
 -void ff_put_bmp_header(AVIOContext *pb, AVCodecContext *enc, const AVCodecTag *tags, int for_asf);
 -int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc);
  enum AVCodecID ff_wav_codec_get_id(unsigned int tag, int bps);
  int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size);
  
 -extern const AVCodecTag ff_codec_bmp_tags[];
 +extern const AVCodecTag ff_codec_bmp_tags[]; // exposed through avformat_get_riff_video_tags()
  extern const AVCodecTag ff_codec_wav_tags[];
  
- void ff_parse_specific_params(AVCodecContext *stream, int *au_rate, int *au_ssize, int *au_scale);
+ void ff_parse_specific_params(AVStream *st, int *au_rate, int *au_ssize, int *au_scale);
  
  int ff_read_riff_info(AVFormatContext *s, int64_t size);
  
@@@ -105,9 -91,10 +105,9 @@@ static av_always_inline int ff_guidcmp(
      return memcmp(g1, g2, sizeof(ff_asf_guid));
  }
  
 -static av_always_inline int ff_get_guid(AVIOContext *s, ff_asf_guid *g)
 -{
 -    return avio_read(s, *g, sizeof(*g));
 -}
 +int ff_get_guid(AVIOContext *s, ff_asf_guid *g);
 +void ff_put_guid(AVIOContext *s, const ff_asf_guid *g);
 +const ff_asf_guid *get_codec_guid(enum AVCodecID id, const AVCodecGuid *av_guid);
  
  enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid);
  
diff --combined libavformat/riffenc.c
@@@ -2,20 -2,20 +2,20 @@@
   * RIFF muxing functions
   * Copyright (c) 2000 Fabrice Bellard
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * FFmpeg is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * FFmpeg is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
 - * License along with Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
@@@ -31,7 -31,7 +31,7 @@@
  int64_t ff_start_tag(AVIOContext *pb, const char *tag)
  {
      ffio_wfourcc(pb, tag);
 -    avio_wl32(pb, 0);
 +    avio_wl32(pb, -1);
      return avio_tell(pb);
  }
  
@@@ -39,23 -39,18 +39,23 @@@ void ff_end_tag(AVIOContext *pb, int64_
  {
      int64_t pos;
  
 +    av_assert0((start&1) == 0);
 +
      pos = avio_tell(pb);
 +    if (pos & 1)
 +        avio_w8(pb, 0);
      avio_seek(pb, start - 4, SEEK_SET);
      avio_wl32(pb, (uint32_t)(pos - start));
 -    avio_seek(pb, pos, SEEK_SET);
 +    avio_seek(pb, FFALIGN(pos, 2), SEEK_SET);
  }
  
  /* WAVEFORMATEX header */
  /* returns the size or -1 on error */
 -int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc)
 +int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc, int flags)
  {
      int bps, blkalign, bytespersec, frame_size;
 -    int hdrsize = 18;
 +    int hdrsize;
 +    int64_t hdrstart = avio_tell(pb);
      int waveformatextensible;
      uint8_t temp[256];
      uint8_t *riff_extradata       = temp;
@@@ -73,7 -68,6 +73,7 @@@
  
      waveformatextensible = (enc->channels > 2 && enc->channel_layout) ||
                             enc->sample_rate > 48000 ||
 +                           enc->codec_id == AV_CODEC_ID_EAC3 ||
                             av_get_bits_per_sample(enc->codec_id) > 16;
  
      if (waveformatextensible)
  
      avio_wl16(pb, enc->channels);
      avio_wl32(pb, enc->sample_rate);
 -    if (enc->codec_id == AV_CODEC_ID_MP2 ||
 -        enc->codec_id == AV_CODEC_ID_MP3 ||
 +    if (enc->codec_id == AV_CODEC_ID_ATRAC3 ||
 +        enc->codec_id == AV_CODEC_ID_G723_1 ||
 +        enc->codec_id == AV_CODEC_ID_MP2    ||
 +        enc->codec_id == AV_CODEC_ID_MP3    ||
          enc->codec_id == AV_CODEC_ID_GSM_MS) {
          bps = 0;
      } else {
          // blkalign = 144 * enc->bit_rate/enc->sample_rate;
      } else if (enc->codec_id == AV_CODEC_ID_AC3) {
          blkalign = 3840;                /* maximum bytes per frame */
 +    } else if (enc->codec_id == AV_CODEC_ID_AAC) {
 +        blkalign = 768 * enc->channels; /* maximum bytes per frame */
 +    } else if (enc->codec_id == AV_CODEC_ID_G723_1) {
 +        blkalign = 24;
      } else if (enc->block_align != 0) { /* specified by the codec */
          blkalign = enc->block_align;
      } else
          enc->codec_id == AV_CODEC_ID_PCM_F64LE ||
          enc->codec_id == AV_CODEC_ID_PCM_S16LE) {
          bytespersec = enc->sample_rate * blkalign;
 +    } else if (enc->codec_id == AV_CODEC_ID_G723_1) {
 +        bytespersec = 800;
      } else {
          bytespersec = enc->bit_rate / 8;
      }
      avio_wl16(pb, blkalign);    /* block align */
      avio_wl16(pb, bps);         /* bits per sample */
      if (enc->codec_id == AV_CODEC_ID_MP3) {
 -        hdrsize += 12;
          bytestream_put_le16(&riff_extradata, 1);    /* wID */
          bytestream_put_le32(&riff_extradata, 2);    /* fdwFlags */
          bytestream_put_le16(&riff_extradata, 1152); /* nBlockSize */
          bytestream_put_le16(&riff_extradata, 1);    /* nFramesPerBlock */
          bytestream_put_le16(&riff_extradata, 1393); /* nCodecDelay */
      } else if (enc->codec_id == AV_CODEC_ID_MP2) {
 -        hdrsize += 22;
          /* fwHeadLayer */
          bytestream_put_le16(&riff_extradata, 2);
          /* dwHeadBitrate */
          bytestream_put_le32(&riff_extradata, 0);
          /* dwPTSHigh */
          bytestream_put_le32(&riff_extradata, 0);
 +    } else if (enc->codec_id == AV_CODEC_ID_G723_1) {
 +        bytestream_put_le32(&riff_extradata, 0x9ace0002); /* extradata needed for msacm g723.1 codec */
 +        bytestream_put_le32(&riff_extradata, 0xaea2f732);
 +        bytestream_put_le16(&riff_extradata, 0xacde);
      } else if (enc->codec_id == AV_CODEC_ID_GSM_MS ||
                 enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) {
 -        hdrsize += 2;
          /* wSamplesPerBlock */
          bytestream_put_le16(&riff_extradata, frame_size);
      } else if (enc->extradata_size) {
          riff_extradata_start = enc->extradata;
          riff_extradata       = enc->extradata + enc->extradata_size;
 -        hdrsize             += enc->extradata_size;
      }
      /* write WAVEFORMATEXTENSIBLE extensions */
      if (waveformatextensible) {
 -        hdrsize += 22;
 +        int write_channel_mask = enc->strict_std_compliance < FF_COMPLIANCE_NORMAL ||
 +                                 enc->channel_layout < 0x40000;
          /* 22 is WAVEFORMATEXTENSIBLE size */
          avio_wl16(pb, riff_extradata - riff_extradata_start + 22);
          /* ValidBitsPerSample || SamplesPerBlock || Reserved */
          avio_wl16(pb, bps);
          /* dwChannelMask */
 -        avio_wl32(pb, enc->channel_layout);
 +        avio_wl32(pb, write_channel_mask ? enc->channel_layout : 0);
          /* GUID + next 3 */
 +        if (enc->codec_id == AV_CODEC_ID_EAC3) {
 +            ff_put_guid(pb, get_codec_guid(enc->codec_id, ff_codec_wav_guids));
 +        } else {
          avio_wl32(pb, enc->codec_tag);
          avio_wl32(pb, 0x00100000);
          avio_wl32(pb, 0xAA000080);
          avio_wl32(pb, 0x719B3800);
 -    } else {
 +        }
 +    } else if ((flags & FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX) ||
 +               enc->codec_tag != 0x0001 /* PCM */ ||
 +               riff_extradata - riff_extradata_start) {
 +        /* WAVEFORMATEX */
          avio_wl16(pb, riff_extradata - riff_extradata_start); /* cbSize */
 -    }
 +    } /* else PCMWAVEFORMAT */
      avio_write(pb, riff_extradata_start, riff_extradata - riff_extradata_start);
 +    hdrsize = avio_tell(pb) - hdrstart;
      if (hdrsize & 1) {
          hdrsize++;
          avio_w8(pb, 0);
  
  /* BITMAPINFOHEADER header */
  void ff_put_bmp_header(AVIOContext *pb, AVCodecContext *enc,
 -                       const AVCodecTag *tags, int for_asf)
 +                       const AVCodecTag *tags, int for_asf, int ignore_extradata)
  {
      /* size */
 -    avio_wl32(pb, 40 + enc->extradata_size);
 +    avio_wl32(pb, 40 + (ignore_extradata ? 0 : enc->extradata_size));
      avio_wl32(pb, enc->width);
      //We always store RGB TopDown
      avio_wl32(pb, enc->codec_tag ? enc->height : -enc->height);
      avio_wl16(pb, enc->bits_per_coded_sample ? enc->bits_per_coded_sample : 24);
      /* compression type */
      avio_wl32(pb, enc->codec_tag);
 -    avio_wl32(pb, enc->width * enc->height * 3);
 +    avio_wl32(pb, (enc->width * enc->height * (enc->bits_per_coded_sample ? enc->bits_per_coded_sample : 24)+7) / 8);
      avio_wl32(pb, 0);
      avio_wl32(pb, 0);
      avio_wl32(pb, 0);
      avio_wl32(pb, 0);
  
 -    avio_write(pb, enc->extradata, enc->extradata_size);
 +    if (!ignore_extradata) {
 +        avio_write(pb, enc->extradata, enc->extradata_size);
  
 -    if (!for_asf && enc->extradata_size & 1)
 -        avio_w8(pb, 0);
 +        if (!for_asf && enc->extradata_size & 1)
 +            avio_w8(pb, 0);
 +    }
  }
  
- void ff_parse_specific_params(AVCodecContext *stream, int *au_rate,
+ void ff_parse_specific_params(AVStream *st, int *au_rate,
                                int *au_ssize, int *au_scale)
  {
+     AVCodecContext *codec = st->codec;
      int gcd;
      int audio_frame_size;
  
      /* We use the known constant frame size for the codec if known, otherwise
       * fall back on using AVCodecContext.frame_size, which is not as reliable
       * for indicating packet duration. */
-     audio_frame_size = av_get_audio_frame_duration(stream, 0);
+     audio_frame_size = av_get_audio_frame_duration(codec, 0);
      if (!audio_frame_size)
-         audio_frame_size = stream->frame_size;
+         audio_frame_size = codec->frame_size;
  
-     *au_ssize = stream->block_align;
-     if (audio_frame_size && stream->sample_rate) {
+     *au_ssize = codec->block_align;
+     if (audio_frame_size && codec->sample_rate) {
          *au_scale = audio_frame_size;
-         *au_rate  = stream->sample_rate;
-     } else if (stream->codec_type == AVMEDIA_TYPE_VIDEO ||
-                stream->codec_type == AVMEDIA_TYPE_DATA ||
-                stream->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-         *au_scale = stream->time_base.num;
-         *au_rate  = stream->time_base.den;
+         *au_rate  = codec->sample_rate;
+     } else if (codec->codec_type == AVMEDIA_TYPE_VIDEO ||
+                codec->codec_type == AVMEDIA_TYPE_DATA ||
+                codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+         *au_scale = codec->time_base.num;
+         *au_rate  = codec->time_base.den;
      } else {
-         *au_scale = stream->block_align ? stream->block_align * 8 : 8;
-         *au_rate  = stream->bit_rate ? stream->bit_rate :
-                     8 * stream->sample_rate;
+         *au_scale = codec->block_align ? codec->block_align * 8 : 8;
+         *au_rate  = codec->bit_rate ? codec->bit_rate :
+                     8 * codec->sample_rate;
      }
      gcd        = av_gcd(*au_scale, *au_rate);
      *au_scale /= gcd;
@@@ -318,19 -294,3 +319,19 @@@ void ff_riff_write_info(AVFormatContex
              ff_riff_write_info_tag(s->pb, t->key, t->value);
      ff_end_tag(pb, list_pos);
  }
 +
 +void ff_put_guid(AVIOContext *s, const ff_asf_guid *g)
 +{
 +    av_assert0(sizeof(*g) == 16);
 +    avio_write(s, *g, sizeof(*g));
 +}
 +
 +const ff_asf_guid *get_codec_guid(enum AVCodecID id, const AVCodecGuid *av_guid)
 +{
 +    int i;
 +    for (i = 0; av_guid[i].id != AV_CODEC_ID_NONE; i++) {
 +        if (id == av_guid[i].id)
 +            return &(av_guid[i].guid);
 +    }
 +    return NULL;
 +}