Merge commit '88058d9a994f42e4e9ed4e67baf696bbfe53128c'
authorMichael Niedermayer <michaelni@gmx.at>
Thu, 11 Oct 2012 12:28:18 +0000 (14:28 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Thu, 11 Oct 2012 12:28:18 +0000 (14:28 +0200)
* commit '88058d9a994f42e4e9ed4e67baf696bbfe53128c':
  vc1dec: Set chroma reference field from REFFIELD for 1REF field pictures
  segment: support applehttp style list

Conflicts:
libavformat/segment.c

Merged-by: Michael Niedermayer <michaelni@gmx.at>
1  2 
libavcodec/vc1dec.c
libavformat/segment.c

diff --combined libavcodec/vc1dec.c
@@@ -4,20 -4,20 +4,20 @@@
   * Copyright (c) 2006-2007 Konstantin Shishkov
   * Partly based on vc9.c (c) 2005 Anonymous, Alex Beregszaszi, 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
   */
  
@@@ -38,7 -38,6 +38,7 @@@
  #include "unary.h"
  #include "mathops.h"
  #include "vdpau_internal.h"
 +#include "libavutil/avassert.h"
  
  #undef NDEBUG
  #include <assert.h>
@@@ -369,7 -368,7 +369,7 @@@ static void vc1_mc_1mv(VC1Context *v, i
      }
      if (v->field_mode) { // interlaced field picture
          if (!dir) {
 -            if ((v->cur_field_type != v->ref_field_type[dir]) && v->cur_field_type) {
 +            if ((v->cur_field_type != v->ref_field_type[dir]) && v->second_field) {
                  srcY = s->current_picture.f.data[0];
                  srcU = s->current_picture.f.data[1];
                  srcV = s->current_picture.f.data[2];
          srcY += s->mspel * (1 + s->linesize);
      }
  
 -    if (v->field_mode && v->cur_field_type) {
 +    if (v->field_mode && v->second_field) {
          off    = s->current_picture_ptr->f.linesize[0];
          off_uv = s->current_picture_ptr->f.linesize[1];
      } else {
@@@ -561,7 -560,7 +561,7 @@@ static void vc1_mc_4mv_luma(VC1Context 
  
      if (!dir) {
          if (v->field_mode) {
 -            if ((v->cur_field_type != v->ref_field_type[dir]) && v->cur_field_type)
 +            if ((v->cur_field_type != v->ref_field_type[dir]) && v->second_field)
                  srcY = s->current_picture.f.data[0];
              else
                  srcY = s->last_picture.f.data[0];
          off = ((n > 1) ? s->linesize : 0) + (n & 1) * 8;
      else
          off = s->linesize * 4 * (n & 2) + (n & 1) * 8;
 -    if (v->field_mode && v->cur_field_type)
 +    if (v->field_mode && v->second_field)
          off += s->current_picture_ptr->f.linesize[0];
  
      src_x = s->mb_x * 16 + (n & 1) * 8 + (mx >> 2);
@@@ -795,6 -794,7 +795,7 @@@ static void vc1_mc_4mv_chroma(VC1Contex
      /* calculate chroma MV vector from four luma MVs */
      if (!v->field_mode || (v->field_mode && !v->numref)) {
          valid_count = get_chroma_mv(mvx, mvy, intra, 0, &tx, &ty);
+         chroma_ref_type = v->reffield;
          if (!valid_count) {
              s->current_picture.f.motion_val[1][s->block_index[0] + v->blocks_off][0] = 0;
              s->current_picture.f.motion_val[1][s->block_index[0] + v->blocks_off][1] = 0;
              srcU += s->current_picture_ptr->f.linesize[1];
              srcV += s->current_picture_ptr->f.linesize[2];
          }
 -        off = v->cur_field_type ? s->current_picture_ptr->f.linesize[1] : 0;
 +        off = v->second_field ? s->current_picture_ptr->f.linesize[1] : 0;
      }
  
      if (v->rangeredfrm || (v->mv_mode == MV_PMODE_INTENSITY_COMP)
@@@ -1138,7 -1138,6 +1139,7 @@@ static av_always_inline void get_mvdata
          }
      }
      else {
 +        av_assert0(index < esc);
          if (extend_x)
              offs_tab = offset_table2;
          else
@@@ -1790,7 -1789,7 +1791,7 @@@ static inline void vc1_pred_mv_intfr(VC
                  } else if (c_valid) {
                      px = C[0];
                      py = C[1];
 -                }
 +                } else px = py = 0;
              }
          } else if (total_valid == 1) {
              px = (a_valid) ? A[0] : ((b_valid) ? B[0] : C[0]);
@@@ -1923,7 -1922,7 +1924,7 @@@ static void vc1_interp_mc(VC1Context *v
          srcY += s->mspel * (1 + s->linesize);
      }
  
 -    if (v->field_mode && v->cur_field_type) {
 +    if (v->field_mode && v->second_field) {
          off    = s->current_picture_ptr->f.linesize[0];
          off_uv = s->current_picture_ptr->f.linesize[1];
      } else {
@@@ -3690,7 -3689,7 +3691,7 @@@ static int vc1_decode_p_mb_intfr(VC1Con
      int idx_mbmode = 0, mvbp;
      int stride_y, fieldtx;
  
 -    mquant = v->pq; /* Loosy initialization */
 +    mquant = v->pq; /* Lossy initialization */
  
      if (v->skip_is_raw)
          skipped = get_bits1(gb);
@@@ -3894,11 -3893,11 +3895,11 @@@ static int vc1_decode_p_mb_intfi(VC1Con
      int val; /* temp values */
      int first_block = 1;
      int dst_idx, off;
 -    int pred_flag;
 +    int pred_flag = 0;
      int block_cbp = 0, pat, block_tt = 0;
      int idx_mbmode = 0;
  
 -    mquant = v->pq; /* Loosy initialization */
 +    mquant = v->pq; /* Lossy initialization */
  
      idx_mbmode = get_vlc2(gb, v->mbmode_vlc->table, VC1_IF_MBMODE_VLC_BITS, 2);
      if (idx_mbmode <= 1) { // intra MB
                  continue;
              v->vc1dsp.vc1_inv_trans_8x8(s->block[i]);
              off  = (i & 4) ? 0 : ((i & 1) * 8 + (i & 2) * 4 * s->linesize);
 -            off += v->cur_field_type ? ((i & 4) ? s->current_picture_ptr->f.linesize[1] : s->current_picture_ptr->f.linesize[0]) : 0;
 +            off += v->second_field ? ((i & 4) ? s->current_picture_ptr->f.linesize[1] : s->current_picture_ptr->f.linesize[0]) : 0;
              s->dsp.put_signed_pixels_clamped(s->block[i], s->dest[dst_idx] + off, (i & 4) ? s->uvlinesize : s->linesize);
              // TODO: loop filter
          }
              dst_idx += i >> 2;
              val = ((cbp >> (5 - i)) & 1);
              off = (i & 4) ? 0 : (i & 1) * 8 + (i & 2) * 4 * s->linesize;
 -            if (v->cur_field_type)
 +            if (v->second_field)
                  off += (i & 4) ? s->current_picture_ptr->f.linesize[1] : s->current_picture_ptr->f.linesize[0];
              if (val) {
                  pat = vc1_decode_p_block(v, s->block[i], i, mquant, ttmb,
@@@ -4171,7 -4170,7 +4172,7 @@@ static void vc1_decode_b_mb_intfi(VC1Co
      int bmvtype = BMV_TYPE_BACKWARD;
      int idx_mbmode, interpmvp;
  
 -    mquant      = v->pq; /* Loosy initialization */
 +    mquant      = v->pq; /* Lossy initialization */
      s->mb_intra = 0;
  
      idx_mbmode = get_vlc2(gb, v->mbmode_vlc->table, VC1_IF_MBMODE_VLC_BITS, 2);
                  for (j = 0; j < 64; j++)
                      s->block[i][j] <<= 1;
              off  = (i & 4) ? 0 : ((i & 1) * 8 + (i & 2) * 4 * s->linesize);
 -            off += v->cur_field_type ? ((i & 4) ? s->current_picture_ptr->f.linesize[1] : s->current_picture_ptr->f.linesize[0]) : 0;
 +            off += v->second_field ? ((i & 4) ? s->current_picture_ptr->f.linesize[1] : s->current_picture_ptr->f.linesize[0]) : 0;
              s->dsp.put_signed_pixels_clamped(s->block[i], s->dest[dst_idx] + off, (i & 4) ? s->uvlinesize : s->linesize);
              // TODO: yet to perform loop filter
          }
              dst_idx += i >> 2;
              val = ((cbp >> (5 - i)) & 1);
              off = (i & 4) ? 0 : (i & 1) * 8 + (i & 2) * 4 * s->linesize;
 -            if (v->cur_field_type)
 +            if (v->second_field)
                  off += (i & 4) ? s->current_picture_ptr->f.linesize[1] : s->current_picture_ptr->f.linesize[0];
              if (val) {
                  vc1_decode_p_block(v, s->block[i], i, mquant, ttmb,
@@@ -4643,7 -4642,7 +4644,7 @@@ static void vc1_decode_p_blocks(VC1Cont
          if (s->mb_y != s->start_mb_y) ff_draw_horiz_band(s, (s->mb_y - 1) * 16, 16);
          s->first_slice_line = 0;
      }
 -    if (apply_loop_filter) {
 +    if (apply_loop_filter && v->fcm == PROGRESSIVE) {
          s->mb_x = 0;
          ff_init_block_index(s);
          for (; s->mb_x < s->mb_width; s->mb_x++) {
@@@ -5316,16 -5315,13 +5317,16 @@@ static int vc1_decode_frame(AVCodecCont
      AVFrame *pict = data;
      uint8_t *buf2 = NULL;
      const uint8_t *buf_start = buf;
 -    int mb_height, n_slices1;
 +    int mb_height, n_slices1=-1;
      struct {
          uint8_t *buf;
          GetBitContext gb;
          int mby_start;
      } *slices = NULL, *tmp;
  
 +    if(s->flags & CODEC_FLAG_LOW_DELAY)
 +        s->low_delay = 1;
 +
      /* no supplementary picture */
      if (buf_size == 0 || (buf_size == 4 && AV_RB32(buf) == VC1_CODE_ENDOFSEQ)) {
          /* special case for last picture */
              *data_size = sizeof(AVFrame);
          }
  
 -        return 0;
 +        return buf_size;
      }
  
      if (s->avctx->codec->capabilities&CODEC_CAP_HWACCEL_VDPAU) {
      // do parse frame header
      v->pic_header_flag = 0;
      if (v->profile < PROFILE_ADVANCED) {
 -        if (ff_vc1_parse_frame_header(v, &s->gb) == -1) {
 +        if (ff_vc1_parse_frame_header(v, &s->gb) < 0) {
              goto err;
          }
      } else {
 -        if (ff_vc1_parse_frame_header_adv(v, &s->gb) == -1) {
 +        if (ff_vc1_parse_frame_header_adv(v, &s->gb) < 0) {
              goto err;
          }
      }
  
 +    if (avctx->debug & FF_DEBUG_PICT_INFO)
 +        av_log(v->s.avctx, AV_LOG_DEBUG, "pict_type: %c\n", av_get_picture_type_char(s->pict_type));
 +
      if ((avctx->codec_id == AV_CODEC_ID_WMV3IMAGE || avctx->codec_id == AV_CODEC_ID_VC1IMAGE)
          && s->pict_type != AV_PICTURE_TYPE_I) {
          av_log(v->s.avctx, AV_LOG_ERROR, "Sprite decoder: expected I-frame\n");
          goto err;
      }
  
 +    v->s.current_picture_ptr->f.interlaced_frame = (v->fcm != PROGRESSIVE);
 +    v->s.current_picture_ptr->f.top_field_first  = v->tff;
 +
      s->me.qpel_put = s->dsp.put_qpel_pixels_tab;
      s->me.qpel_avg = s->dsp.avg_qpel_pixels_tab;
  
                  s->end_mb_y = (i == n_slices     ) ? mb_height : FFMIN(mb_height, slices[i].mby_start % mb_height);
              else
                  s->end_mb_y = (i <= n_slices1 + 1) ? mb_height : FFMIN(mb_height, slices[i].mby_start % mb_height);
 +            if (s->end_mb_y <= s->start_mb_y) {
 +                av_log(v->s.avctx, AV_LOG_ERROR, "end mb y %d %d invalid\n", s->end_mb_y, s->start_mb_y);
 +                continue;
 +            }
              ff_vc1_decode_blocks(v);
              if (i != n_slices)
                  s->gb = slices[i].gb;
                  get_bits_count(&s->gb), s->gb.size_in_bits);
  //  if (get_bits_count(&s->gb) > buf_size * 8)
  //      return -1;
 -        ff_er_frame_end(s);
 +        if(s->error_occurred && s->pict_type == AV_PICTURE_TYPE_B)
 +            goto err;
 +        if(!v->field_mode)
 +            ff_er_frame_end(s);
      }
  
      ff_MPV_frame_end(s);
diff --combined libavformat/segment.c
@@@ -1,4 -1,5 +1,4 @@@
  /*
 - * Generic segmenter
   * Copyright (c) 2011, Luca Barbato
   *
   * This file is part of Libav.
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
 +/**
 + * @file generic segmenter
 + * M3U8 specification can be find here:
 + * @url{http://tools.ietf.org/id/draft-pantos-http-live-streaming-08.txt}
 + */
 +
  #include <float.h>
  
  #include "avformat.h"
  #include "internal.h"
  
 +#include "libavutil/avassert.h"
  #include "libavutil/log.h"
  #include "libavutil/opt.h"
  #include "libavutil/avstring.h"
  #include "libavutil/parseutils.h"
  #include "libavutil/mathematics.h"
  
 +typedef enum {
 +    LIST_TYPE_UNDEFINED = -1,
 +    LIST_TYPE_FLAT = 0,
 +    LIST_TYPE_CSV,
 +    LIST_TYPE_M3U8,
 +    LIST_TYPE_EXT, ///< deprecated
 +    LIST_TYPE_NB,
 +} ListType;
 +
 +
 +#define SEGMENT_LIST_FLAG_CACHE 1
 +#define SEGMENT_LIST_FLAG_LIVE  2
 +
  typedef struct {
      const AVClass *class;  /**< Class for private options. */
 -    int number;
 +    int segment_idx;       ///< index of the segment file to write, starting from 0
 +    int segment_idx_wrap;  ///< number after which the index wraps
 +    int segment_count;     ///< number of segment files already written
      AVOutputFormat *oformat;
      AVFormatContext *avf;
 -    char *format;          /**< Set by a private option. */
 -    char *list;            /**< Set by a private option. */
 -    int  list_type;        /**< Set by a private option. */
 -    float time;            /**< Set by a private option. */
 -    int  size;             /**< Set by a private option. */
 -    int  wrap;             /**< Set by a private option. */
 +    char *format;          ///< format to use for output segment files
 +    char *list;            ///< filename for the segment list file
 +    int   list_count;      ///< list counter
 +    int   list_flags;      ///< flags affecting list generation
 +    int   list_size;       ///< number of entries for the segment list file
 +    double list_max_segment_time; ///< max segment time in the current list
 +    ListType list_type;    ///< set the list type
 +    AVIOContext *list_pb;  ///< list file put-byte context
 +    char *time_str;        ///< segment duration specification string
 +    int64_t time;          ///< segment duration
 +    char *times_str;       ///< segment times specification string
 +    int64_t *times;        ///< list of segment interval specification
 +    int nb_times;          ///< number of elments in the times array
 +    char *time_delta_str;  ///< approximation value duration used for the segment times
 +    int64_t time_delta;
      int  individual_header_trailer; /**< Set by a private option. */
      int  write_header_trailer; /**< Set by a private option. */
 -    int64_t offset_time;
 -    int64_t recording_time;
      int has_video;
 -    AVIOContext *pb;
 +    double start_time, end_time;
  } SegmentContext;
  
 -enum {
 -    LIST_FLAT,
 -    LIST_HLS
 -};
 +static void print_csv_escaped_str(AVIOContext *ctx, const char *str)
 +{
 +    int needs_quoting = !!str[strcspn(str, "\",\n\r")];
 +
 +    if (needs_quoting)
 +        avio_w8(ctx, '"');
 +
 +    for (; *str; str++) {
 +        if (*str == '"')
 +            avio_w8(ctx, '"');
 +        avio_w8(ctx, *str);
 +    }
 +    if (needs_quoting)
 +        avio_w8(ctx, '"');
 +}
  
  static int segment_mux_init(AVFormatContext *s)
  {
      return 0;
  }
  
 -static int segment_hls_window(AVFormatContext *s, int last)
 -{
 -    SegmentContext *seg = s->priv_data;
 -    int i, ret = 0;
 -    char buf[1024];
 -
 -    if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
 -                              &s->interrupt_callback, NULL)) < 0)
 -        goto fail;
 -
 -    avio_printf(seg->pb, "#EXTM3U\n");
 -    avio_printf(seg->pb, "#EXT-X-VERSION:3\n");
 -    avio_printf(seg->pb, "#EXT-X-TARGETDURATION:%d\n", (int)seg->time);
 -    avio_printf(seg->pb, "#EXT-X-MEDIA-SEQUENCE:%d\n",
 -                FFMAX(0, seg->number - seg->size));
 -
 -    for (i = FFMAX(0, seg->number - seg->size);
 -         i < seg->number; i++) {
 -        avio_printf(seg->pb, "#EXTINF:%d,\n", (int)seg->time);
 -        av_get_frame_filename(buf, sizeof(buf), s->filename, i);
 -        avio_printf(seg->pb, "%s\n", buf);
 -    }
 -
 -    if (last)
 -        avio_printf(seg->pb, "#EXT-X-ENDLIST\n");
 -fail:
 -    avio_closep(&seg->pb);
 -    return ret;
 -}
 -
  static int segment_start(AVFormatContext *s, int write_header)
  {
      SegmentContext *c = s->priv_data;
          oc = c->avf;
      }
  
 -    if (c->wrap)
 -        c->number %= c->wrap;
 +    if (c->segment_idx_wrap)
 +        c->segment_idx %= c->segment_idx_wrap;
  
      if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
 -                              s->filename, c->number++) < 0)
 +                              s->filename, c->segment_idx++) < 0) {
 +        av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename);
          return AVERROR(EINVAL);
 +    }
 +    c->segment_count++;
  
      if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
                            &s->interrupt_callback, NULL)) < 0)
      return 0;
  }
  
 -static int segment_end(AVFormatContext *oc, int write_trailer)
 +static int segment_list_open(AVFormatContext *s)
 +{
 +    SegmentContext *seg = s->priv_data;
 +    int ret;
 +
 +    ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
 +                     &s->interrupt_callback, NULL);
 +    if (ret < 0)
 +        return ret;
 +    seg->list_max_segment_time = 0;
 +
 +    if (seg->list_type == LIST_TYPE_M3U8) {
 +        avio_printf(seg->list_pb, "#EXTM3U\n");
 +        avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n");
 +        avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->list_count);
 +        avio_printf(seg->list_pb, "#EXT-X-ALLOWCACHE:%d\n",
 +                    !!(seg->list_flags & SEGMENT_LIST_FLAG_CACHE));
 +        if (seg->list_flags & SEGMENT_LIST_FLAG_LIVE)
 +            avio_printf(seg->list_pb,
 +                        "#EXT-X-TARGETDURATION:%"PRId64"\n", seg->time / 1000000);
 +    }
 +
 +    return ret;
 +}
 +
 +static void segment_list_close(AVFormatContext *s)
  {
 +    SegmentContext *seg = s->priv_data;
 +
 +    if (seg->list_type == LIST_TYPE_M3U8) {
 +        if (!(seg->list_flags & SEGMENT_LIST_FLAG_LIVE))
 +            avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n",
 +                        (int)ceil(seg->list_max_segment_time));
 +        avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n");
 +    }
 +    seg->list_count++;
 +
 +    avio_close(seg->list_pb);
 +}
 +
 +static int segment_end(AVFormatContext *s, int write_trailer)
 +{
 +    SegmentContext *seg = s->priv_data;
 +    AVFormatContext *oc = seg->avf;
      int ret = 0;
  
      av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */
      if (write_trailer)
 -        av_write_trailer(oc);
 +        ret = av_write_trailer(oc);
 +
 +    if (ret < 0)
 +        av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
 +               oc->filename);
 +
 +    if (seg->list) {
 +        if (seg->list_size && !(seg->segment_count % seg->list_size)) {
 +            segment_list_close(s);
 +            if ((ret = segment_list_open(s)) < 0)
 +                goto end;
 +        }
 +
 +        if (seg->list_type == LIST_TYPE_FLAT) {
 +            avio_printf(seg->list_pb, "%s\n", oc->filename);
 +        } else if (seg->list_type == LIST_TYPE_CSV || seg->list_type == LIST_TYPE_EXT) {
 +            print_csv_escaped_str(seg->list_pb, oc->filename);
 +            avio_printf(seg->list_pb, ",%f,%f\n", seg->start_time, seg->end_time);
 +        } else if (seg->list_type == LIST_TYPE_M3U8) {
 +            avio_printf(seg->list_pb, "#EXTINF:%f,\n%s\n",
 +                        seg->end_time - seg->start_time, oc->filename);
 +        }
 +        seg->list_max_segment_time = FFMAX(seg->end_time - seg->start_time, seg->list_max_segment_time);
 +        avio_flush(seg->list_pb);
 +    }
 +
 +end:
      avio_close(oc->pb);
  
      return ret;
  }
  
 +static int parse_times(void *log_ctx, int64_t **times, int *nb_times,
 +                       const char *times_str)
 +{
 +    char *p;
 +    int i, ret = 0;
 +    char *times_str1 = av_strdup(times_str);
 +    char *saveptr = NULL;
 +
 +    if (!times_str1)
 +        return AVERROR(ENOMEM);
 +
 +#define FAIL(err) ret = err; goto end
 +
 +    *nb_times = 1;
 +    for (p = times_str1; *p; p++)
 +        if (*p == ',')
 +            (*nb_times)++;
 +
 +    *times = av_malloc(sizeof(**times) * *nb_times);
 +    if (!*times) {
 +        av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n");
 +        FAIL(AVERROR(ENOMEM));
 +    }
 +
 +    p = times_str1;
 +    for (i = 0; i < *nb_times; i++) {
 +        int64_t t;
 +        char *tstr = av_strtok(p, ",", &saveptr);
 +        av_assert0(tstr);
 +        p = NULL;
 +
 +        ret = av_parse_time(&t, tstr, 1);
 +        if (ret < 0) {
 +            av_log(log_ctx, AV_LOG_ERROR,
 +                   "Invalid time duration specification in %s\n", p);
 +            FAIL(AVERROR(EINVAL));
 +        }
 +        (*times)[i] = t;
 +
 +        /* check on monotonicity */
 +        if (i && (*times)[i-1] > (*times)[i]) {
 +            av_log(log_ctx, AV_LOG_ERROR,
 +                   "Specified time %f is greater than the following time %f\n",
 +                   (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
 +            FAIL(AVERROR(EINVAL));
 +        }
 +    }
 +
 +end:
 +    av_free(times_str1);
 +    return ret;
 +}
 +
  static int open_null_ctx(AVIOContext **ctx)
  {
      int buf_size = 32768;
@@@ -315,59 -182,16 +315,59 @@@ static int seg_write_header(AVFormatCon
      AVFormatContext *oc = NULL;
      int ret, i;
  
 -    seg->number = 0;
 -    seg->offset_time = 0;
 -    seg->recording_time = seg->time * 1000000;
 +    seg->segment_count = 0;
      if (!seg->write_header_trailer)
          seg->individual_header_trailer = 0;
  
 -    if (seg->list && seg->list_type != LIST_HLS)
 -        if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
 -                              &s->interrupt_callback, NULL)) < 0)
 +    if (seg->time_str && seg->times_str) {
 +        av_log(s, AV_LOG_ERROR,
 +               "segment_time and segment_times options are mutually exclusive, select just one of them\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    if ((seg->list_flags & SEGMENT_LIST_FLAG_LIVE) && seg->times_str) {
 +        av_log(s, AV_LOG_ERROR,
 +               "segment_flags +live and segment_times options are mutually exclusive:"
 +               "specify -segment_time if you want a live-friendly list\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    if (seg->times_str) {
 +        if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
 +            return ret;
 +    } else {
 +        /* set default value if not specified */
 +        if (!seg->time_str)
 +            seg->time_str = av_strdup("2");
 +        if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) {
 +            av_log(s, AV_LOG_ERROR,
 +                   "Invalid time duration specification '%s' for segment_time option\n",
 +                   seg->time_str);
 +            return ret;
 +        }
 +    }
 +
 +    if (seg->time_delta_str) {
 +        if ((ret = av_parse_time(&seg->time_delta, seg->time_delta_str, 1)) < 0) {
 +            av_log(s, AV_LOG_ERROR,
 +                   "Invalid time duration specification '%s' for delta option\n",
 +                   seg->time_delta_str);
 +            return ret;
 +        }
 +    }
 +
 +    if (seg->list) {
 +        if (seg->list_type == LIST_TYPE_UNDEFINED) {
 +            if      (av_match_ext(seg->list, "csv" )) seg->list_type = LIST_TYPE_CSV;
 +            else if (av_match_ext(seg->list, "ext" )) seg->list_type = LIST_TYPE_EXT;
 +            else if (av_match_ext(seg->list, "m3u8")) seg->list_type = LIST_TYPE_M3U8;
 +            else                                      seg->list_type = LIST_TYPE_FLAT;
 +        }
 +        if ((ret = segment_list_open(s)) < 0)
              goto fail;
 +    }
 +    if (seg->list_type == LIST_TYPE_EXT)
 +        av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
  
      for (i = 0; i < s->nb_streams; i++)
          seg->has_video +=
      oc = seg->avf;
  
      if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
 -                              s->filename, seg->number++) < 0) {
 +                              s->filename, seg->segment_idx++) < 0) {
          ret = AVERROR(EINVAL);
          goto fail;
      }
 +    seg->segment_count++;
  
      if (seg->write_header_trailer) {
          if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
          goto fail;
      }
  
 +    if (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0)
 +        s->avoid_negative_ts = 1;
 +
      if (!seg->write_header_trailer) {
          close_null_ctx(oc->pb);
          if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
              goto fail;
      }
  
 -    if (seg->list) {
 -        if (seg->list_type == LIST_HLS) {
 -            if ((ret = segment_hls_window(s, 0)) < 0)
 -                goto fail;
 -        } else {
 -            avio_printf(seg->pb, "%s\n", oc->filename);
 -            avio_flush(seg->pb);
 -        }
 -    }
 -
  fail:
      if (ret) {
          if (seg->list)
 -            avio_close(seg->pb);
 +            segment_list_close(s);
          if (seg->avf)
              avformat_free_context(seg->avf);
      }
@@@ -441,26 -271,18 +441,26 @@@ static int seg_write_packet(AVFormatCon
      SegmentContext *seg = s->priv_data;
      AVFormatContext *oc = seg->avf;
      AVStream *st = s->streams[pkt->stream_index];
 -    int64_t end_pts = seg->recording_time * seg->number;
 +    int64_t end_pts;
      int ret;
  
 -    if ((seg->has_video && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
 +    if (seg->times) {
 +        end_pts = seg->segment_count <= seg->nb_times ?
 +            seg->times[seg->segment_count-1] : INT64_MAX;
 +    } else {
 +        end_pts = seg->time * seg->segment_count;
 +    }
 +
 +    /* if the segment has video, start a new segment *only* with a key video frame */
 +    if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO || !seg->has_video) &&
          av_compare_ts(pkt->pts, st->time_base,
 -                      end_pts, AV_TIME_BASE_Q) >= 0 &&
 +                      end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0 &&
          pkt->flags & AV_PKT_FLAG_KEY) {
  
 -        av_log(s, AV_LOG_DEBUG, "Next segment starts at %d %"PRId64"\n",
 -               pkt->stream_index, pkt->pts);
 +        av_log(s, AV_LOG_DEBUG, "Next segment starts with packet stream:%d pts:%"PRId64" pts_time:%f\n",
 +               pkt->stream_index, pkt->pts, pkt->pts * av_q2d(st->time_base));
  
 -        ret = segment_end(oc, seg->individual_header_trailer);
 +        ret = segment_end(s, seg->individual_header_trailer);
  
          if (!ret)
              ret = segment_start(s, seg->individual_header_trailer);
  
          oc = seg->avf;
  
 -        if (seg->list) {
 -            if (seg->list_type == LIST_HLS) {
 -                if ((ret = segment_hls_window(s, 0)) < 0)
 -                    goto fail;
 -            } else {
 -                avio_printf(seg->pb, "%s\n", oc->filename);
 -                avio_flush(seg->pb);
 -                if (seg->size && !(seg->number % seg->size)) {
 -                    avio_closep(&seg->pb);
 -                    if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
 -                                          &s->interrupt_callback, NULL)) < 0)
 -                        goto fail;
 -                }
 -            }
 -        }
 +        seg->start_time = (double)pkt->pts * av_q2d(st->time_base);
 +    } else if (pkt->pts != AV_NOPTS_VALUE) {
 +        seg->end_time = FFMAX(seg->end_time,
 +                              (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
      }
  
      ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
  fail:
      if (ret < 0) {
          if (seg->list)
 -            avio_close(seg->pb);
 +            avio_close(seg->list_pb);
          avformat_free_context(oc);
      }
  
@@@ -494,19 -327,25 +494,21 @@@ static int seg_write_trailer(struct AVF
      AVFormatContext *oc = seg->avf;
      int ret;
      if (!seg->write_header_trailer) {
-         ret = segment_end(s, 0);
 -        if ((ret = segment_end(oc, 0)) < 0)
++        if ((ret = segment_end(s, 0)) < 0)
+             goto fail;
          open_null_ctx(&oc->pb);
-         av_write_trailer(oc);
+         ret = av_write_trailer(oc);
          close_null_ctx(oc->pb);
      } else {
 -        ret = segment_end(oc, 1);
 +        ret = segment_end(s, 1);
      }
++fail:
 +    if (seg->list)
 +        segment_list_close(s);
  
 -    if (ret < 0)
 -        goto fail;
 -
 -    if (seg->list && seg->list_type == LIST_HLS) {
 -        if ((ret = segment_hls_window(s, 1) < 0))
 -            goto fail;
 -    }
 +    av_opt_free(seg);
 +    av_freep(&seg->times);
  
 -fail:
 -    avio_close(seg->pb);
      avformat_free_context(oc);
      return ret;
  }
  #define OFFSET(x) offsetof(SegmentContext, x)
  #define E AV_OPT_FLAG_ENCODING_PARAM
  static const AVOption options[] = {
 -    { "segment_format",    "container format used for the segments",  OFFSET(format),  AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
 -    { "segment_time",      "segment length in seconds",               OFFSET(time),    AV_OPT_TYPE_FLOAT,  {.dbl = 2},     0, FLT_MAX, E },
 -    { "segment_list",      "output the segment list",                 OFFSET(list),    AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
 -    { "segment_list_size", "maximum number of playlist entries",      OFFSET(size),    AV_OPT_TYPE_INT,    {.i64 = 5},     0, INT_MAX, E },
 -    { "segment_list_type", "segment list format",                     OFFSET(list_type),    AV_OPT_TYPE_INT,    {.i64 = LIST_FLAT},     0, 2, E, "list_type" },
 -    {   "flat",            "plain list (default)",                    0,               AV_OPT_TYPE_CONST,  {.i64 = LIST_FLAT}, 0, 0, E, "list_type" },
 -    {   "hls",             "Apple HTTP Live Streaming compatible",    0,               AV_OPT_TYPE_CONST,  {.i64 = LIST_HLS},  0, 0, E, "list_type" },
 -    { "segment_wrap",      "number after which the index wraps",      OFFSET(wrap),    AV_OPT_TYPE_INT,    {.i64 = 0},     0, INT_MAX, E },
 +    { "segment_format",    "set container format used for the segments", OFFSET(format),  AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
 +    { "segment_list",      "set the segment list filename",              OFFSET(list),    AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
 +
 +    { "segment_list_flags","set flags affecting segment list generation", OFFSET(list_flags), AV_OPT_TYPE_FLAGS, {.i64 = SEGMENT_LIST_FLAG_CACHE }, 0, UINT_MAX, E, "list_flags"},
 +    { "cache",             "allow list caching",                                    0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX,   E, "list_flags"},
 +    { "live",              "enable live-friendly list generation (useful for HLS)", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_LIVE }, INT_MIN, INT_MAX,    E, "list_flags"},
 +
 +    { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT,  {.i64 = 0},     0, INT_MAX, E },
 +    { "segment_list_type", "set the segment list type",                  OFFSET(list_type), AV_OPT_TYPE_INT,  {.i64 = LIST_TYPE_UNDEFINED}, -1, LIST_TYPE_NB-1, E, "list_type" },
 +    { "flat", "flat format",     0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, 0, "list_type" },
 +    { "csv",  "csv format",      0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_CSV  }, INT_MIN, INT_MAX, 0, "list_type" },
 +    { "ext",  "extended format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_EXT  }, INT_MIN, INT_MAX, 0, "list_type" },
 +    { "m3u8", "M3U8 format",     0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, 0, "list_type" },
++    { "hls", "Apple HTTP Live Streaming compatible",     0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, 0, "list_type" },
 +    { "segment_time",      "set segment duration",                       OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
 +    { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E },
 +    { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
 +    { "segment_wrap",      "set number after which the index wraps",     OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
      { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
      { "write_header_trailer", "write a header to the first segment and a trailer to the last one", OFFSET(write_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
      { NULL },
@@@ -543,6 -373,7 +546,6 @@@ static const AVClass seg_class = 
      .version    = LIBAVUTIL_VERSION_INT,
  };
  
 -
  AVOutputFormat ff_segment_muxer = {
      .name           = "segment",
      .long_name      = NULL_IF_CONFIG_SMALL("segment"),
      .write_trailer  = seg_write_trailer,
      .priv_class     = &seg_class,
  };
 +
 +static const AVClass sseg_class = {
 +    .class_name = "stream_segment muxer",
 +    .item_name  = av_default_item_name,
 +    .option     = options,
 +    .version    = LIBAVUTIL_VERSION_INT,
 +};
 +
 +AVOutputFormat ff_stream_segment_muxer = {
 +    .name           = "stream_segment,ssegment",
 +    .long_name      = NULL_IF_CONFIG_SMALL("streaming segment muxer"),
 +    .priv_data_size = sizeof(SegmentContext),
 +    .flags          = AVFMT_NOFILE,
 +    .write_header   = seg_write_header,
 +    .write_packet   = seg_write_packet,
 +    .write_trailer  = seg_write_trailer,
 +    .priv_class     = &sseg_class,
 +};