Merge commit 'eb447d515956b3ce182d9750083131735f00324c'
authorMichael Niedermayer <michaelni@gmx.at>
Fri, 5 Oct 2012 13:41:45 +0000 (15:41 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Fri, 5 Oct 2012 13:48:23 +0000 (15:48 +0200)
* commit 'eb447d515956b3ce182d9750083131735f00324c':
  segment: Free and reinit the muxer before calling avformat_write_header

Conflicts:
libavformat/segment.c

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

  #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. */
 -    float time;            /**< Set by a private option. */
 -    int  size;             /**< Set by a private option. */
 -    int  wrap;             /**< Set by a private option. */
 -    int64_t offset_time;
 -    int64_t recording_time;
 +    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 has_video;
 -    AVIOContext *pb;
 +    double start_time, end_time;
  } SegmentContext;
  
- static int segment_start(AVFormatContext *s)
 +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)
  {
      SegmentContext *seg = s->priv_data;
-     AVFormatContext *oc = seg->avf;
+     AVFormatContext *oc;
+     int i;
+     seg->avf = oc = avformat_alloc_context();
+     if (!oc)
+         return AVERROR(ENOMEM);
+     oc->oformat            = seg->oformat;
+     oc->interrupt_callback = s->interrupt_callback;
+     for (i = 0; i < s->nb_streams; i++) {
+         AVStream *st;
+         if (!(st = avformat_new_stream(oc, NULL)))
+             return AVERROR(ENOMEM);
+         avcodec_copy_context(st->codec, s->streams[i]->codec);
+         st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
+     }
+     return 0;
+ }
++
+ static int segment_start(AVFormatContext *s)
+ {
+     SegmentContext *c = s->priv_data;
+     AVFormatContext *oc = c->avf;
      int err = 0;
  
-     if (seg->segment_idx_wrap)
-         seg->segment_idx %= seg->segment_idx_wrap;
+     avformat_free_context(oc);
+     c->avf = NULL;
+     if ((err = segment_mux_init(s)) < 0)
+         return err;
+     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, seg->segment_idx++) < 0) {
 -                              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);
-     seg->segment_count++;
 +    }
++    c->segment_count++;
  
      if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
                            &s->interrupt_callback, NULL)) < 0)
@@@ -250,65 -112,17 +282,60 @@@ end
  static int seg_write_header(AVFormatContext *s)
  {
      SegmentContext *seg = s->priv_data;
-     AVFormatContext *oc;
+     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->list)
 -        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;
 +        }
 +    }
 +
-     oc = avformat_alloc_context();
-     if (!oc)
-         return AVERROR(ENOMEM);
 +    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 +=
          goto fail;
      }
  
-     oc->interrupt_callback = s->interrupt_callback;
-     seg->avf = oc;
-     for (i = 0; i < s->nb_streams; i++) {
-         AVStream *st;
-         if (!(st = avformat_new_stream(oc, NULL))) {
-             ret = AVERROR(ENOMEM);
-             goto fail;
-         }
-         avcodec_copy_context(st->codec, s->streams[i]->codec);
-         st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
-     }
+     if ((ret = segment_mux_init(s)) < 0)
+         goto fail;
+     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;
      }
          goto fail;
      }
  
 -    if (seg->list) {
 -        avio_printf(seg->pb, "%s\n", oc->filename);
 -        avio_flush(seg->pb);
 -    }
 -
  fail:
      if (ret) {
-         avformat_free_context(oc);
          if (seg->list)
 -            avio_close(seg->pb);
 +            segment_list_close(s);
+         if (seg->avf)
+             avformat_free_context(seg->avf);
      }
      return ret;
  }
@@@ -375,31 -185,37 +394,34 @@@ 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);
 -
 -        if (!ret)
 -            ret = segment_start(s);
 -
 -        if (ret)
 +        if ((ret = segment_end(s)) < 0 || (ret = segment_start(s)) < 0)
              goto fail;
 -        if (seg->list) {
 -            avio_printf(seg->pb, "%s\n", oc->filename);
 -            avio_flush(seg->pb);
 -            if (seg->size && !(seg->number % seg->size)) {
 -                avio_close(seg->pb);
 -                if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
 -                                      &s->interrupt_callback, NULL)) < 0)
 -                    goto fail;
 -            }
 -        }
+         oc = seg->avf;
 +        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);