Merge commit '948f3c19a8bd069768ca411212aaf8c1ed96b10d'
authorHendrik Leppkes <h.leppkes@gmail.com>
Tue, 29 Sep 2015 13:14:59 +0000 (15:14 +0200)
committerHendrik Leppkes <h.leppkes@gmail.com>
Tue, 29 Sep 2015 13:22:52 +0000 (15:22 +0200)
* commit '948f3c19a8bd069768ca411212aaf8c1ed96b10d':
  lavc: Make AVPacket.duration int64, and deprecate convergence_duration

Merged-by: Hendrik Leppkes <h.leppkes@gmail.com>
17 files changed:
1  2 
doc/APIchanges
libavcodec/audio_frame_queue.c
libavcodec/audio_frame_queue.h
libavcodec/avcodec.h
libavcodec/avpacket.c
libavcodec/parser.c
libavcodec/version.h
libavformat/framecrcenc.c
libavformat/jacosubdec.c
libavformat/matroskadec.c
libavformat/matroskaenc.c
libavformat/md5enc.c
libavformat/movenc.c
libavformat/mux.c
libavformat/r3d.c
libavformat/srtenc.c
libavformat/utils.c

diff --cc doc/APIchanges
@@@ -15,7 -13,10 +15,10 @@@ libavutil:     2015-08-2
  
  API changes, most recent first:
  
 -2015-xx-xx - xxxxxxx - lavc 57.0.0 - avcodec.h
++2015-09-29 - xxxxxxx - lavc 57.3.100 / lavc 57.2.0 - avcodec.h
+   Change type of AVPacket.duration from int to int64_t.
 -2015-xx-xx - xxxxxxx - lavc 57.2.0 - d3d11va.h
 +2015-09-17 - xxxxxxx - lavc 57.3.100 / lavc 57.2.0 - d3d11va.h
    Add av_d3d11va_alloc_context(). This function must from now on be used for
    allocating AVD3D11VAContext.
  
Simple merge
Simple merge
Simple merge
@@@ -540,31 -349,10 +548,35 @@@ void av_packet_rescale_ts(AVPacket *pkt
          pkt->dts = av_rescale_q(pkt->dts, src_tb, dst_tb);
      if (pkt->duration > 0)
          pkt->duration = av_rescale_q(pkt->duration, src_tb, dst_tb);
+ #if FF_API_CONVERGENCE_DURATION
+ FF_DISABLE_DEPRECATION_WARNINGS
      if (pkt->convergence_duration > 0)
          pkt->convergence_duration = av_rescale_q(pkt->convergence_duration, src_tb, dst_tb);
+ FF_ENABLE_DEPRECATION_WARNINGS
+ #endif
  }
 +
 +int ff_side_data_set_encoder_stats(AVPacket *pkt, int quality, int64_t *error, int error_count, int pict_type)
 +{
 +    uint8_t *side_data;
 +    int side_data_size;
 +    int i;
 +
 +    side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_QUALITY_STATS, &side_data_size);
 +    if (!side_data) {
 +        side_data_size = 4+4+8*error_count;
 +        side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_QUALITY_STATS,
 +                                            side_data_size);
 +    }
 +
 +    if (!side_data || side_data_size < 4+4+8*error_count)
 +        return AVERROR(ENOMEM);
 +
 +    AV_WL32(side_data   , quality  );
 +    side_data[4] = pict_type;
 +    side_data[5] = error_count;
 +    for (i = 0; i<error_count; i++)
 +        AV_WL64(side_data+8 + 8*i , error[i]);
 +
 +    return 0;
 +}
@@@ -23,8 -23,7 +23,9 @@@
  #include <stdint.h>
  #include <string.h>
  
 +#include "libavutil/avassert.h"
 +#include "libavutil/atomic.h"
+ #include "libavutil/internal.h"
  #include "libavutil/mem.h"
  
  #include "internal.h"
@@@ -69,20 -67,31 +70,24 @@@ AVCodecParserContext *av_parser_init(in
  found:
      s = av_mallocz(sizeof(AVCodecParserContext));
      if (!s)
 -        return NULL;
 +        goto err_out;
      s->parser = parser;
 -    if (parser->priv_data_size) {
 -        s->priv_data = av_mallocz(parser->priv_data_size);
 -        if (!s->priv_data) {
 -            av_free(s);
 -            return NULL;
 -        }
 -    }
 +    s->priv_data = av_mallocz(parser->priv_data_size);
 +    if (!s->priv_data)
 +        goto err_out;
 +    s->fetch_timestamp=1;
 +    s->pict_type = AV_PICTURE_TYPE_I;
      if (parser->parser_init) {
          ret = parser->parser_init(s);
 -        if (ret != 0) {
 -            av_free(s->priv_data);
 -            av_free(s);
 -            return NULL;
 -        }
 +        if (ret != 0)
 +            goto err_out;
      }
 -    s->fetch_timestamp      = 1;
 -    s->pict_type            = AV_PICTURE_TYPE_I;
      s->key_frame            = -1;
+ #if FF_API_CONVERGENCE_DURATION
+ FF_DISABLE_DEPRECATION_WARNINGS
      s->convergence_duration = 0;
+ FF_ENABLE_DEPRECATION_WARNINGS
+ #endif
      s->dts_sync_point       = INT_MIN;
      s->dts_ref_dts_delta    = INT_MIN;
      s->pts_dts_delta        = INT_MIN;
Simple merge
@@@ -47,31 -30,8 +47,31 @@@ static int framecrc_write_packet(struc
      uint32_t crc = av_adler32_update(0, pkt->data, pkt->size);
      char buf[256];
  
-     snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8d, %8d, 0x%08"PRIx32,
 -    snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, 0x%08"PRIx32"\n",
++    snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, 0x%08"PRIx32,
               pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size, crc);
 +    if (pkt->flags != AV_PKT_FLAG_KEY)
 +        av_strlcatf(buf, sizeof(buf), ", F=0x%0X", pkt->flags);
 +    if (pkt->side_data_elems) {
 +        int i, j;
 +        av_strlcatf(buf, sizeof(buf), ", S=%d", pkt->side_data_elems);
 +
 +        for (i=0; i<pkt->side_data_elems; i++) {
 +            uint32_t side_data_crc = 0;
 +            if (HAVE_BIGENDIAN && AV_PKT_DATA_PALETTE == pkt->side_data[i].type) {
 +                for (j=0; j<pkt->side_data[i].size; j++) {
 +                    side_data_crc = av_adler32_update(side_data_crc,
 +                                                      pkt->side_data[i].data + (j^3),
 +                                                      1);
 +                }
 +            } else {
 +                side_data_crc = av_adler32_update(0,
 +                                                  pkt->side_data[i].data,
 +                                                  pkt->side_data[i].size);
 +            }
 +            av_strlcatf(buf, sizeof(buf), ", %8d, 0x%08x", pkt->side_data[i].size, side_data_crc);
 +        }
 +    }
 +    av_strlcatf(buf, sizeof(buf), "\n");
      avio_write(s->pb, buf, strlen(buf));
      return 0;
  }
index 5e54363,0000000..0436a93
mode 100644,000000..100644
--- /dev/null
@@@ -1,274 -1,0 +1,274 @@@
-                            int64_t *start, int *duration)
 +/*
 + * Copyright (c) 2012 Clément Bœsch
 + *
 + * This file is part of FFmpeg.
 + *
 + * 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.
 + *
 + * 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 FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * JACOsub subtitle demuxer
 + * @see http://unicorn.us.com/jacosub/jscripts.html
 + * @todo Support P[ALETTE] directive.
 + */
 +
 +#include "avformat.h"
 +#include "internal.h"
 +#include "subtitles.h"
 +#include "libavcodec/internal.h"
 +#include "libavcodec/jacosub.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/bprint.h"
 +#include "libavutil/intreadwrite.h"
 +
 +typedef struct {
 +    int shift;
 +    unsigned timeres;
 +    FFDemuxSubtitlesQueue q;
 +} JACOsubContext;
 +
 +static int timed_line(const char *ptr)
 +{
 +    char c;
 +    int fs, fe;
 +    return (sscanf(ptr, "%*u:%*u:%*u.%*u %*u:%*u:%*u.%*u %c", &c) == 1 ||
 +            (sscanf(ptr, "@%u @%u %c", &fs, &fe, &c) == 3 && fs < fe));
 +}
 +
 +static int jacosub_probe(AVProbeData *p)
 +{
 +    const char *ptr     = p->buf;
 +    const char *ptr_end = p->buf + p->buf_size;
 +
 +    if (AV_RB24(ptr) == 0xEFBBBF)
 +        ptr += 3; /* skip UTF-8 BOM */
 +
 +    while (ptr < ptr_end) {
 +        while (jss_whitespace(*ptr))
 +            ptr++;
 +        if (*ptr != '#' && *ptr != '\n') {
 +            if (timed_line(ptr))
 +                return AVPROBE_SCORE_EXTENSION + 1;
 +            return 0;
 +        }
 +        ptr += ff_subtitles_next_line(ptr);
 +    }
 +    return 0;
 +}
 +
 +static const char * const cmds[] = {
 +    "CLOCKPAUSE",
 +    "DIRECTIVE",
 +    "FONT",
 +    "HRES",
 +    "INCLUDE",
 +    "PALETTE",
 +    "QUANTIZE",
 +    "RAMP",
 +    "SHIFT",
 +    "TIMERES",
 +};
 +
 +static int get_jss_cmd(char k)
 +{
 +    int i;
 +
 +    k = av_toupper(k);
 +    for (i = 0; i < FF_ARRAY_ELEMS(cmds); i++)
 +        if (k == cmds[i][0])
 +            return i;
 +    return -1;
 +}
 +
 +static int jacosub_read_close(AVFormatContext *s)
 +{
 +    JACOsubContext *jacosub = s->priv_data;
 +    ff_subtitles_queue_clean(&jacosub->q);
 +    return 0;
 +}
 +
 +static const char *read_ts(JACOsubContext *jacosub, const char *buf,
++                           int64_t *start, int64_t *duration)
 +{
 +    int len;
 +    unsigned hs, ms, ss, fs; // hours, minutes, seconds, frame start
 +    unsigned he, me, se, fe; // hours, minutes, seconds, frame end
 +    int ts_start, ts_end;
 +
 +    /* timed format */
 +    if (sscanf(buf, "%u:%u:%u.%u %u:%u:%u.%u %n",
 +               &hs, &ms, &ss, &fs,
 +               &he, &me, &se, &fe, &len) == 8) {
 +        ts_start = (hs*3600 + ms*60 + ss) * jacosub->timeres + fs;
 +        ts_end   = (he*3600 + me*60 + se) * jacosub->timeres + fe;
 +        goto shift_and_ret;
 +    }
 +
 +    /* timestamps format */
 +    if (sscanf(buf, "@%u @%u %n", &ts_start, &ts_end, &len) == 2)
 +        goto shift_and_ret;
 +
 +    return NULL;
 +
 +shift_and_ret:
 +    ts_start  = (ts_start + jacosub->shift) * 100 / jacosub->timeres;
 +    ts_end    = (ts_end   + jacosub->shift) * 100 / jacosub->timeres;
 +    *start    = ts_start;
 +    *duration = ts_start + ts_end;
 +    return buf + len;
 +}
 +
 +static int get_shift(int timeres, const char *buf)
 +{
 +    int sign = 1;
 +    int a = 0, b = 0, c = 0, d = 0;
 +#define SSEP "%*1[.:]"
 +    int n = sscanf(buf, "%d"SSEP"%d"SSEP"%d"SSEP"%d", &a, &b, &c, &d);
 +#undef SSEP
 +
 +    if (*buf == '-' || a < 0) {
 +        sign = -1;
 +        a = FFABS(a);
 +    }
 +
 +    switch (n) {
 +    case 4: return sign * ((a*3600 + b*60 + c) * timeres + d);
 +    case 3: return sign * ((         a*60 + b) * timeres + c);
 +    case 2: return sign * ((                a) * timeres + b);
 +    }
 +
 +    return 0;
 +}
 +
 +static int jacosub_read_header(AVFormatContext *s)
 +{
 +    AVBPrint header;
 +    AVIOContext *pb = s->pb;
 +    char line[JSS_MAX_LINESIZE];
 +    JACOsubContext *jacosub = s->priv_data;
 +    int shift_set = 0; // only the first shift matters
 +    int merge_line = 0;
 +    int i, ret;
 +
 +    AVStream *st = avformat_new_stream(s, NULL);
 +    if (!st)
 +        return AVERROR(ENOMEM);
 +    avpriv_set_pts_info(st, 64, 1, 100);
 +    st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
 +    st->codec->codec_id   = AV_CODEC_ID_JACOSUB;
 +
 +    jacosub->timeres = 30;
 +
 +    av_bprint_init(&header, 1024+AV_INPUT_BUFFER_PADDING_SIZE, 4096);
 +
 +    while (!avio_feof(pb)) {
 +        int cmd_len;
 +        const char *p = line;
 +        int64_t pos = avio_tell(pb);
 +        int len = ff_get_line(pb, line, sizeof(line));
 +
 +        p = jss_skip_whitespace(p);
 +
 +        /* queue timed line */
 +        if (merge_line || timed_line(p)) {
 +            AVPacket *sub;
 +
 +            sub = ff_subtitles_queue_insert(&jacosub->q, line, len, merge_line);
 +            if (!sub)
 +                return AVERROR(ENOMEM);
 +            sub->pos = pos;
 +            merge_line = len > 1 && !strcmp(&line[len - 2], "\\\n");
 +            continue;
 +        }
 +
 +        /* skip all non-compiler commands and focus on the command */
 +        if (*p != '#')
 +            continue;
 +        p++;
 +        i = get_jss_cmd(p[0]);
 +        if (i == -1)
 +            continue;
 +
 +        /* trim command + spaces */
 +        cmd_len = strlen(cmds[i]);
 +        if (av_strncasecmp(p, cmds[i], cmd_len) == 0)
 +            p += cmd_len;
 +        else
 +            p++;
 +        p = jss_skip_whitespace(p);
 +
 +        /* handle commands which affect the whole script */
 +        switch (cmds[i][0]) {
 +        case 'S': // SHIFT command affect the whole script...
 +            if (!shift_set) {
 +                jacosub->shift = get_shift(jacosub->timeres, p);
 +                shift_set = 1;
 +            }
 +            av_bprintf(&header, "#S %s", p);
 +            break;
 +        case 'T': // ...but must be placed after TIMERES
 +            jacosub->timeres = strtol(p, NULL, 10);
 +            if (!jacosub->timeres)
 +                jacosub->timeres = 30;
 +            else
 +                av_bprintf(&header, "#T %s", p);
 +            break;
 +        }
 +    }
 +
 +    /* general/essential directives in the extradata */
 +    ret = avpriv_bprint_to_extradata(st->codec, &header);
 +    if (ret < 0)
 +        goto fail;
 +
 +    /* SHIFT and TIMERES affect the whole script so packet timing can only be
 +     * done in a second pass */
 +    for (i = 0; i < jacosub->q.nb_subs; i++) {
 +        AVPacket *sub = &jacosub->q.subs[i];
 +        read_ts(jacosub, sub->data, &sub->pts, &sub->duration);
 +    }
 +    ff_subtitles_queue_finalize(s, &jacosub->q);
 +
 +    return 0;
 +fail:
 +    jacosub_read_close(s);
 +    return ret;
 +}
 +
 +static int jacosub_read_packet(AVFormatContext *s, AVPacket *pkt)
 +{
 +    JACOsubContext *jacosub = s->priv_data;
 +    return ff_subtitles_queue_read_packet(&jacosub->q, pkt);
 +}
 +
 +static int jacosub_read_seek(AVFormatContext *s, int stream_index,
 +                             int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
 +{
 +    JACOsubContext *jacosub = s->priv_data;
 +    return ff_subtitles_queue_seek(&jacosub->q, s, stream_index,
 +                                   min_ts, ts, max_ts, flags);
 +}
 +
 +AVInputFormat ff_jacosub_demuxer = {
 +    .name           = "jacosub",
 +    .long_name      = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"),
 +    .priv_data_size = sizeof(JACOsubContext),
 +    .read_probe     = jacosub_probe,
 +    .read_header    = jacosub_read_header,
 +    .read_packet    = jacosub_read_packet,
 +    .read_seek2     = jacosub_read_seek,
 +    .read_close     = jacosub_read_close,
 +};
@@@ -2777,32 -2335,28 +2777,18 @@@ static int matroska_parse_frame(Matrosk
      else
          pkt->pts = timecode;
      pkt->pos = pos;
 -    if (track->type != MATROSKA_TRACK_TYPE_SUBTITLE || st->codec->codec_id == AV_CODEC_ID_TEXT)
 -        pkt->duration = duration;
++    pkt->duration = lace_duration;
++
+ #if FF_API_CONVERGENCE_DURATION
+ FF_DISABLE_DEPRECATION_WARNINGS
 -    if (st->codec->codec_id == AV_CODEC_ID_TEXT)
 -        pkt->convergence_duration = duration;
 +    if (st->codec->codec_id == AV_CODEC_ID_SUBRIP) {
-         /*
-          * For backward compatibility.
-          * Historically, we have put subtitle duration
-          * in convergence_duration, on the off chance
-          * that the time_scale is less than 1us, which
-          * could result in a 32bit overflow on the
-          * normal duration field.
-          */
 +        pkt->convergence_duration = lace_duration;
 +    }
-     if (track->type != MATROSKA_TRACK_TYPE_SUBTITLE ||
-         lace_duration <= INT_MAX) {
-         /*
-          * For non subtitle tracks, just store the duration
-          * as normal.
-          *
-          * If it's a subtitle track and duration value does
-          * not overflow a uint32, then also store it normally.
-          */
-         pkt->duration = lace_duration;
-     }
+ FF_ENABLE_DEPRECATION_WARNINGS
+ #endif
  
 -    if (st->codec->codec_id == AV_CODEC_ID_SSA)
 -        matroska_fix_ass_packet(matroska, pkt, duration);
 -
 -    if (matroska->prev_pkt                                 &&
 -        timecode                         != AV_NOPTS_VALUE &&
 -        matroska->prev_pkt->pts          == timecode       &&
 -        matroska->prev_pkt->stream_index == st->index      &&
 -        st->codec->codec_id == AV_CODEC_ID_SSA)
 -        matroska_merge_packets(matroska->prev_pkt, pkt);
 -    else {
 -        dynarray_add(&matroska->packets, &matroska->num_packets, pkt);
 -        matroska->prev_pkt = pkt;
 -    }
 +    dynarray_add(&matroska->packets, &matroska->num_packets, pkt);
 +    matroska->prev_pkt = pkt;
  
      return 0;
  
@@@ -1620,18 -1389,13 +1620,18 @@@ static void mkv_write_block(AVFormatCon
  {
      MatroskaMuxContext *mkv = s->priv_data;
      AVCodecContext *codec = s->streams[pkt->stream_index]->codec;
 -    uint8_t *data = NULL;
 -    int offset = 0, size = pkt->size;
 +    uint8_t *data = NULL, *side_data = NULL;
 +    int offset = 0, size = pkt->size, side_data_size = 0;
      int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts;
 +    uint64_t additional_id = 0;
 +    int64_t discard_padding = 0;
 +    uint8_t track_number = (mkv->is_dash ? mkv->dash_track_number : (pkt->stream_index + 1));
 +    ebml_master block_group, block_additions, block_more;
  
      av_log(s, AV_LOG_DEBUG, "Writing block at offset %" PRIu64 ", size %d, "
-            "pts %" PRId64 ", dts %" PRId64 ", duration %d, keyframe %d\n",
 -           "pts %" PRId64 ", dts %" PRId64 ", duration %" PRId64 ", flags %d\n",
 -           avio_tell(pb), pkt->size, pkt->pts, pkt->dts, pkt->duration, flags);
++           "pts %" PRId64 ", dts %" PRId64 ", duration %" PRId64 ", keyframe %d\n",
 +           avio_tell(pb), pkt->size, pkt->pts, pkt->dts, pkt->duration,
 +           keyframe != 0);
      if (codec->codec_id == AV_CODEC_ID_H264 && codec->extradata_size > 0 &&
          (AV_RB24(codec->extradata) == 1 || AV_RB32(codec->extradata) == 1))
          ff_avc_parse_nal_units_buf(pkt->data, &data, &size);
      avio_write(pb, data + offset, size);
      if (data != pkt->data)
          av_free(data);
 -}
  
 -static int srt_get_duration(uint8_t **buf)
 -{
 -    int i, duration = 0;
 -
 -    for (i = 0; i < 2 && !duration; i++) {
 -        int s_hour, s_min, s_sec, s_hsec, e_hour, e_min, e_sec, e_hsec;
 -        if (sscanf(*buf, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d",
 -                   &s_hour, &s_min, &s_sec, &s_hsec,
 -                   &e_hour, &e_min, &e_sec, &e_hsec) == 8) {
 -            s_min += 60 * s_hour;
 -            e_min += 60 * e_hour;
 -            s_sec += 60 * s_min;
 -
 -            e_sec  += 60 * e_min;
 -            s_hsec += 1000 * s_sec;
 -            e_hsec += 1000 * e_sec;
 -
 -            duration = e_hsec - s_hsec;
 -        }
 -        *buf += strcspn(*buf, "\n") + 1;
 +    if (blockid == MATROSKA_ID_BLOCK && !keyframe) {
 +        put_ebml_sint(pb, MATROSKA_ID_BLOCKREFERENCE,
 +                      mkv->last_track_timestamp[track_number - 1]);
 +    }
 +    mkv->last_track_timestamp[track_number - 1] = ts - mkv->cluster_pts;
 +
 +    if (discard_padding) {
 +        put_ebml_sint(pb, MATROSKA_ID_DISCARDPADDING, discard_padding);
 +    }
 +
 +    if (side_data_size && additional_id == 1) {
 +        block_additions = start_ebml_master(pb, MATROSKA_ID_BLOCKADDITIONS, 0);
 +        block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE, 0);
 +        put_ebml_uint(pb, MATROSKA_ID_BLOCKADDID, 1);
 +        put_ebml_id(pb, MATROSKA_ID_BLOCKADDITIONAL);
 +        put_ebml_num(pb, side_data_size, 0);
 +        avio_write(pb, side_data, side_data_size);
 +        end_ebml_master(pb, block_more);
 +        end_ebml_master(pb, block_additions);
 +    }
 +    if ((side_data_size && additional_id == 1) || discard_padding) {
 +        end_ebml_master(pb, block_group);
      }
 -    return duration;
  }
  
 -static int mkv_write_srt_blocks(AVFormatContext *s, AVIOContext *pb,
 -                                AVPacket *pkt)
 +static int mkv_write_vtt_blocks(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt)
  {
 +    MatroskaMuxContext *mkv = s->priv_data;
      ebml_master blockgroup;
 -    AVPacket pkt2 = *pkt;
 -    int64_t duration = srt_get_duration(&pkt2.data);
 -    pkt2.size -= pkt2.data - pkt->data;
 -
 -    blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP,
 -                                   mkv_blockgroup_size(pkt2.size));
 -    mkv_write_block(s, pb, MATROSKA_ID_BLOCK, &pkt2, 0);
 -    put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration);
 +    int id_size, settings_size, size;
 +    uint8_t *id, *settings;
 +    int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts;
 +    const int flags = 0;
 +
 +    id_size = 0;
 +    id = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_IDENTIFIER,
 +                                 &id_size);
 +
 +    settings_size = 0;
 +    settings = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_SETTINGS,
 +                                       &settings_size);
 +
 +    size = id_size + 1 + settings_size + 1 + pkt->size;
 +
 +    av_log(s, AV_LOG_DEBUG, "Writing block at offset %" PRIu64 ", size %d, "
-            "pts %" PRId64 ", dts %" PRId64 ", duration %d, flags %d\n",
++           "pts %" PRId64 ", dts %" PRId64 ", duration %" PRId64 ", flags %d\n",
 +           avio_tell(pb), size, pkt->pts, pkt->dts, pkt->duration, flags);
 +
 +    blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, mkv_blockgroup_size(size));
 +
 +    put_ebml_id(pb, MATROSKA_ID_BLOCK);
 +    put_ebml_num(pb, size + 4, 0);
 +    avio_w8(pb, 0x80 | (pkt->stream_index + 1));     // this assumes stream_index is less than 126
 +    avio_wb16(pb, ts - mkv->cluster_pts);
 +    avio_w8(pb, flags);
 +    avio_printf(pb, "%.*s\n%.*s\n%.*s", id_size, id, settings_size, settings, pkt->size, pkt->data);
 +
 +    put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, pkt->duration);
      end_ebml_master(pb, blockgroup);
  
 -    return duration;
 +    return pkt->duration;
  }
  
  static void mkv_flush_dynbuf(AVFormatContext *s)
@@@ -1833,36 -1518,32 +1833,41 @@@ static int mkv_write_packet_internal(AV
          mkv->cluster_pts = FFMAX(0, ts);
      }
  
 +    relative_packet_pos = avio_tell(s->pb) - mkv->cluster.pos;
 +
      if (codec->codec_type != AVMEDIA_TYPE_SUBTITLE) {
 -        mkv_write_block(s, pb, MATROSKA_ID_SIMPLEBLOCK, pkt, keyframe << 7);
 -    } else if (codec->codec_id == AV_CODEC_ID_SSA) {
 -        duration = mkv_write_ass_blocks(s, pb, pkt);
 -    } else if (codec->codec_id == AV_CODEC_ID_SRT) {
 -        duration = mkv_write_srt_blocks(s, pb, pkt);
 +        mkv_write_block(s, pb, MATROSKA_ID_SIMPLEBLOCK, pkt, keyframe);
 +        if (s->pb->seekable && (codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe || add_cue)) {
 +            ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, dash_tracknum, ts, mkv->cluster_pos, relative_packet_pos, -1);
 +            if (ret < 0) return ret;
 +        }
      } else {
 -        ebml_master blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP,
 -                                                   mkv_blockgroup_size(pkt->size));
 -        duration = pkt->duration;
 +        if (codec->codec_id == AV_CODEC_ID_WEBVTT) {
 +            duration = mkv_write_vtt_blocks(s, pb, pkt);
 +        } else {
 +            ebml_master blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP,
 +                                                       mkv_blockgroup_size(pkt->size));
++
+ #if FF_API_CONVERGENCE_DURATION
+ FF_DISABLE_DEPRECATION_WARNINGS
 -        if (pkt->convergence_duration)
 -            duration = pkt->convergence_duration;
 +            /* For backward compatibility, prefer convergence_duration. */
 +            if (pkt->convergence_duration > 0) {
 +                duration = pkt->convergence_duration;
 +            }
+ FF_ENABLE_DEPRECATION_WARNINGS
+ #endif
 -        mkv_write_block(s, pb, MATROSKA_ID_BLOCK, pkt, 0);
 -        put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration);
 -        end_ebml_master(pb, blockgroup);
 -    }
 +            /* All subtitle blocks are considered to be keyframes. */
 +            mkv_write_block(s, pb, MATROSKA_ID_BLOCK, pkt, 1);
 +            put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration);
 +            end_ebml_master(pb, blockgroup);
 +        }
  
 -    if (codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe) {
 -        ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, ts,
 -                               mkv->cluster_pos);
 -        if (ret < 0)
 -            return ret;
 +        if (s->pb->seekable) {
 +            ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, dash_tracknum, ts,
 +                                   mkv->cluster_pos, relative_packet_pos, duration);
 +            if (ret < 0)
 +                return ret;
 +        }
      }
  
      mkv->duration = FFMAX(mkv->duration, ts + duration);
@@@ -132,10 -101,10 +132,10 @@@ static int framemd5_write_packet(struc
  {
      struct MD5Context *c = s->priv_data;
      char buf[256];
 -    av_md5_init(c->md5);
 -    av_md5_update(c->md5, pkt->data, pkt->size);
 +    av_hash_init(c->hash);
 +    av_hash_update(c->hash, pkt->data, pkt->size);
  
-     snprintf(buf, sizeof(buf) - 64, "%d, %10"PRId64", %10"PRId64", %8d, %8d, ",
+     snprintf(buf, sizeof(buf) - 64, "%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, ",
               pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size);
      md5_finish(s, buf);
      return 0;
@@@ -4306,21 -3367,6 +4306,21 @@@ int ff_mov_write_packet(AVFormatContex
      int size = pkt->size, ret = 0;
      uint8_t *reformatted_data = NULL;
  
-             av_log(s, AV_LOG_ERROR, "Application provided duration: %d is invalid\n", pkt->duration);
 +    if (trk->entry) {
 +        int64_t duration = pkt->dts - trk->cluster[trk->entry - 1].dts;
 +        if (duration < 0 || duration > INT_MAX) {
 +            av_log(s, AV_LOG_ERROR, "Application provided duration: %"PRId64" / timestamp: %"PRId64" is out of range for mov/mp4 format\n",
 +                duration, pkt->dts
 +            );
 +
 +            pkt->dts = trk->cluster[trk->entry - 1].dts + 1;
 +            pkt->pts = AV_NOPTS_VALUE;
 +        }
 +        if (pkt->duration < 0) {
++            av_log(s, AV_LOG_ERROR, "Application provided duration: %"PRId64" is invalid\n", pkt->duration);
 +            return AVERROR(EINVAL);
 +        }
 +    }
      if (mov->flags & FF_MOV_FLAG_FRAGMENT) {
          int ret;
          if (mov->moov_written || mov->flags & FF_MOV_FLAG_EMPTY_MOOV) {
@@@ -487,19 -261,14 +487,19 @@@ int avformat_write_header(AVFormatConte
  //FIXME merge with compute_pkt_fields
  static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt)
  {
 -    int delay = FFMAX(st->codec->has_b_frames, !!st->codec->max_b_frames);
 +    int delay = FFMAX(st->codec->has_b_frames, st->codec->max_b_frames > 0);
      int num, den, i;
 +    int frame_size;
  
 -    av_log(s, AV_LOG_TRACE, "compute_pkt_fields2: pts:%" PRId64 " dts:%" PRId64 " cur_dts:%" PRId64 " b:%d size:%d st:%d\n",
 -            pkt->pts, pkt->dts, st->cur_dts, delay, pkt->size, pkt->stream_index);
 +    if (s->debug & FF_FDEBUG_TS)
 +        av_log(s, AV_LOG_TRACE, "compute_pkt_fields2: pts:%s dts:%s cur_dts:%s b:%d size:%d st:%d\n",
 +            av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), delay, pkt->size, pkt->stream_index);
  
 -/*    if(pkt->pts == AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE)
 - *      return AVERROR(EINVAL);*/
 +    if (pkt->duration < 0 && st->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) {
-         av_log(s, AV_LOG_WARNING, "Packet with invalid duration %d in stream %d\n",
++        av_log(s, AV_LOG_WARNING, "Packet with invalid duration %"PRId64" in stream %d\n",
 +               pkt->duration, pkt->stream_index);
 +        pkt->duration = 0;
 +    }
  
      /* duration field */
      if (pkt->duration == 0) {
@@@ -314,9 -312,8 +314,9 @@@ static int r3d_read_reda(AVFormatContex
  
      pkt->stream_index = 1;
      pkt->dts = dts;
 -    pkt->duration = av_rescale(samples, st->time_base.den, st->codec->sample_rate);
 +    if (st->codec->sample_rate)
 +        pkt->duration = av_rescale(samples, st->time_base.den, st->codec->sample_rate);
-     av_log(s, AV_LOG_TRACE, "pkt dts %"PRId64" duration %d samples %d sample rate %d\n",
+     av_log(s, AV_LOG_TRACE, "pkt dts %"PRId64" duration %"PRId64" samples %d sample rate %d\n",
              pkt->dts, pkt->duration, samples, st->codec->sample_rate);
  
      return 0;
index 9bb83d6,0000000..0b2a16b
mode 100644,000000..100644
--- /dev/null
@@@ -1,112 -1,0 +1,116 @@@
 +/*
 + * SubRip subtitle muxer
 + * Copyright (c) 2012  Nicolas George <nicolas.george@normalesup.org>
 + *
 + * This file is part of FFmpeg.
 + *
 + * 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.
 + *
 + * 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 FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +#include "avformat.h"
 +#include "internal.h"
 +#include "libavutil/log.h"
 +#include "libavutil/intreadwrite.h"
 +
 +/* TODO: add options for:
 +   - character encoding;
 +   - LF / CRLF;
 +   - byte order mark.
 + */
 +
 +typedef struct SRTContext{
 +    unsigned index;
 +} SRTContext;
 +
 +static int srt_write_header(AVFormatContext *avf)
 +{
 +    SRTContext *srt = avf->priv_data;
 +
 +    if (avf->nb_streams != 1 ||
 +        avf->streams[0]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) {
 +        av_log(avf, AV_LOG_ERROR,
 +               "SRT supports only a single subtitles stream.\n");
 +        return AVERROR(EINVAL);
 +    }
 +    if (avf->streams[0]->codec->codec_id != AV_CODEC_ID_TEXT &&
 +        avf->streams[0]->codec->codec_id != AV_CODEC_ID_SUBRIP) {
 +        av_log(avf, AV_LOG_ERROR,
 +               "Unsupported subtitles codec: %s\n",
 +               avcodec_get_name(avf->streams[0]->codec->codec_id));
 +        return AVERROR(EINVAL);
 +    }
 +    avpriv_set_pts_info(avf->streams[0], 64, 1, 1000);
 +    srt->index = 1;
 +    return 0;
 +}
 +
 +static int srt_write_packet(AVFormatContext *avf, AVPacket *pkt)
 +{
 +    SRTContext *srt = avf->priv_data;
 +
 +    // TODO: reindent
 +        int64_t s = pkt->pts, e, d = pkt->duration;
 +        int size, x1 = -1, y1 = -1, x2 = -1, y2 = -1;
 +        const uint8_t *p;
 +
 +        p = av_packet_get_side_data(pkt, AV_PKT_DATA_SUBTITLE_POSITION, &size);
 +        if (p && size == 16) {
 +            x1 = AV_RL32(p     );
 +            y1 = AV_RL32(p +  4);
 +            x2 = AV_RL32(p +  8);
 +            y2 = AV_RL32(p + 12);
 +        }
 +
++#if FF_API_CONVERGENCE_DURATION
++FF_DISABLE_DEPRECATION_WARNINGS
 +        if (d <= 0)
 +            /* For backward compatibility, fallback to convergence_duration. */
 +            d = pkt->convergence_duration;
++FF_ENABLE_DEPRECATION_WARNINGS
++#endif
 +        if (s == AV_NOPTS_VALUE || d < 0) {
 +            av_log(avf, AV_LOG_WARNING,
 +                   "Insufficient timestamps in event number %d.\n", srt->index);
 +            return 0;
 +        }
 +        e = s + d;
 +        avio_printf(avf->pb, "%d\n%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d",
 +                       srt->index,
 +                       (int)(s / 3600000),      (int)(s / 60000) % 60,
 +                       (int)(s /    1000) % 60, (int)(s %  1000),
 +                       (int)(e / 3600000),      (int)(e / 60000) % 60,
 +                       (int)(e /    1000) % 60, (int)(e %  1000));
 +        if (p)
 +            avio_printf(avf->pb, "  X1:%03d X2:%03d Y1:%03d Y2:%03d",
 +                        x1, x2, y1, y2);
 +        avio_printf(avf->pb, "\n");
 +
 +    avio_write(avf->pb, pkt->data, pkt->size);
 +    avio_write(avf->pb, "\n\n", 2);
 +    srt->index++;
 +    return 0;
 +}
 +
 +AVOutputFormat ff_srt_muxer = {
 +    .name           = "srt",
 +    .long_name      = NULL_IF_CONFIG_SMALL("SubRip subtitle"),
 +    .mime_type      = "application/x-subrip",
 +    .extensions     = "srt",
 +    .priv_data_size = sizeof(SRTContext),
 +    .write_header   = srt_write_header,
 +    .write_packet   = srt_write_packet,
 +    .flags          = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT,
 +    .subtitle_codec = AV_CODEC_ID_SUBRIP,
 +};
@@@ -953,21 -560,12 +953,21 @@@ static void update_initial_durations(AV
                  cur_dts -= duration;
              }
          }
 -        pktl          = s->internal->packet_buffer;
 +        if (pktl && pktl->pkt.dts != st->first_dts) {
-             av_log(s, AV_LOG_DEBUG, "first_dts %s not matching first dts %s (pts %s, duration %d) in the queue\n",
++            av_log(s, AV_LOG_DEBUG, "first_dts %s not matching first dts %s (pts %s, duration %"PRId64") in the queue\n",
 +                   av_ts2str(st->first_dts), av_ts2str(pktl->pkt.dts), av_ts2str(pktl->pkt.pts), pktl->pkt.duration);
 +            return;
 +        }
 +        if (!pktl) {
 +            av_log(s, AV_LOG_DEBUG, "first_dts %s but no packet with dts in the queue\n", av_ts2str(st->first_dts));
 +            return;
 +        }
 +        pktl          = s->internal->packet_buffer ? s->internal->packet_buffer : s->internal->parse_queue;
          st->first_dts = cur_dts;
 -    } else if (st->cur_dts)
 +    } else if (st->cur_dts != RELATIVE_TS_BASE)
          return;
  
 -    for (; pktl; pktl = pktl->next) {
 +    for (; pktl; pktl = get_next_pkt(s, st, pktl)) {
          if (pktl->pkt.stream_index != stream_index)
              continue;
          if (pktl->pkt.pts == pktl->pkt.dts  &&
@@@ -1092,16 -652,15 +1092,16 @@@ static void compute_pkt_fields(AVFormat
          pkt->pts > pkt->dts)
          presentation_delayed = 1;
  
 -    av_log(NULL, AV_LOG_TRACE,
 -            "IN delayed:%d pts:%"PRId64", dts:%"PRId64" "
 -            "cur_dts:%"PRId64" st:%d pc:%p\n",
 -            presentation_delayed, pkt->pts, pkt->dts, st->cur_dts,
 -            pkt->stream_index, pc);
 -    /* Interpolate PTS and DTS if they are not present. We skip H.264
 +    if (s->debug & FF_FDEBUG_TS)
 +        av_log(s, AV_LOG_TRACE,
-             "IN delayed:%d pts:%s, dts:%s cur_dts:%s st:%d pc:%p duration:%d delay:%d onein_oneout:%d\n",
++            "IN delayed:%d pts:%s, dts:%s cur_dts:%s st:%d pc:%p duration:%"PRId64" delay:%d onein_oneout:%d\n",
 +            presentation_delayed, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts),
 +            pkt->stream_index, pc, pkt->duration, delay, onein_oneout);
 +
 +    /* Interpolate PTS and DTS if they are not present. We skip H264
       * currently because delay and has_b_frames are not reliably set. */
      if ((delay == 0 || (delay == 1 && pc)) &&
 -        st->codec->codec_id != AV_CODEC_ID_H264) {
 +        onein_oneout) {
          if (presentation_delayed) {
              /* DTS = decompression timestamp */
              /* PTS = presentation timestamp */
          st->pts_buffer[0] = pkt->pts;
          for (i = 0; i<delay && st->pts_buffer[i] > st->pts_buffer[i + 1]; i++)
              FFSWAP(int64_t, st->pts_buffer[i], st->pts_buffer[i + 1]);
 -        if (pkt->dts == AV_NOPTS_VALUE)
 -            pkt->dts = st->pts_buffer[0];
 -        // We skipped it above so we try here.
 -        if (st->codec->codec_id == AV_CODEC_ID_H264)
 -            // This should happen on the first packet
 -            update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts);
 -        if (pkt->dts > st->cur_dts)
 -            st->cur_dts = pkt->dts;
 +
 +        pkt->dts = select_from_pts_buffer(st, st->pts_buffer, pkt->dts);
      }
 +    // We skipped it above so we try here.
 +    if (!onein_oneout)
 +        // This should happen on the first packet
 +        update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt);
 +    if (pkt->dts > st->cur_dts)
 +        st->cur_dts = pkt->dts;
  
 -    av_log(NULL, AV_LOG_TRACE,
 -            "OUTdelayed:%d/%d pts:%"PRId64", dts:%"PRId64" cur_dts:%"PRId64"\n",
 -            presentation_delayed, delay, pkt->pts, pkt->dts, st->cur_dts);
 +    if (s->debug & FF_FDEBUG_TS)
 +        av_log(s, AV_LOG_TRACE, "OUTdelayed:%d/%d pts:%s, dts:%s cur_dts:%s\n",
 +            presentation_delayed, delay, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts));
  
      /* update flags */
 -    if (is_intra_only(st->codec->codec_id))
 +    if (is_intra_only(st->codec))
          pkt->flags |= AV_PKT_FLAG_KEY;
+ #if FF_API_CONVERGENCE_DURATION
+ FF_DISABLE_DEPRECATION_WARNINGS
      if (pc)
          pkt->convergence_duration = pc->convergence_duration;
+ FF_ENABLE_DEPRECATION_WARNINGS
+ #endif
  }
  
  static void free_packet_buffer(AVPacketList **pkt_buf, AVPacketList **pkt_buf_end)
@@@ -1348,10 -909,9 +1352,10 @@@ static int read_frame_internal(AVFormat
          }
          if (s->debug & FF_FDEBUG_TS)
              av_log(s, AV_LOG_DEBUG,
-                    "ff_read_packet stream=%d, pts=%s, dts=%s, size=%d, duration=%d, flags=%d\n",
 -                   "ff_read_packet stream=%d, pts=%"PRId64", dts=%"PRId64", "
 -                   "size=%d, duration=%"PRId64", flags=%d\n",
 -                   cur_pkt.stream_index, cur_pkt.pts, cur_pkt.dts,
++                   "ff_read_packet stream=%d, pts=%s, dts=%s, size=%d, duration=%"PRId64", flags=%d\n",
 +                   cur_pkt.stream_index,
 +                   av_ts2str(cur_pkt.pts),
 +                   av_ts2str(cur_pkt.dts),
                     cur_pkt.size, cur_pkt.duration, cur_pkt.flags);
  
          if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) {
  
      if (s->debug & FF_FDEBUG_TS)
          av_log(s, AV_LOG_DEBUG,
 -               "read_frame_internal stream=%d, pts=%"PRId64", dts=%"PRId64", "
 +               "read_frame_internal stream=%d, pts=%s, dts=%s, "
-                "size=%d, duration=%d, flags=%d\n",
+                "size=%d, duration=%"PRId64", flags=%d\n",
 -               pkt->stream_index, pkt->pts, pkt->dts,
 +               pkt->stream_index,
 +               av_ts2str(pkt->pts),
 +               av_ts2str(pkt->dts),
                 pkt->size, pkt->duration, pkt->flags);
  
      return ret;