Merge commit '378a6315b7c48195ffd94e6aa9aa6d663d42b35e'
authorMichael Niedermayer <michaelni@gmx.at>
Fri, 5 Oct 2012 14:14:06 +0000 (16:14 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Fri, 5 Oct 2012 14:21:16 +0000 (16:21 +0200)
* commit '378a6315b7c48195ffd94e6aa9aa6d663d42b35e':
  segment: Add an option for disabling writing of a header/trailer to each segment

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. */
 +    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. */
 -    int64_t offset_time;
 -    int64_t recording_time;
      int has_video;
 -    AVIOContext *pb;
 +    double start_time, end_time;
  } SegmentContext;
  
 +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;
@@@ -122,21 -77,20 +122,23 @@@ static int segment_start(AVFormatContex
      AVFormatContext *oc = c->avf;
      int err = 0;
  
-     avformat_free_context(oc);
-     c->avf = NULL;
-     if ((err = segment_mux_init(s)) < 0)
-         return err;
-     oc = c->avf;
+     if (write_header) {
+         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, 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)
  {
- static int segment_end(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;
  
-     ret = av_write_trailer(oc);
+     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;
@@@ -394,26 -191,23 +399,31 @@@ 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));
  
-         if ((ret = segment_end(s)) < 0 || (ret = segment_start(s)) < 0)
 -        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);
+         if (ret)
              goto fail;
  
          oc = seg->avf;
@@@ -440,13 -240,9 +450,13 @@@ static int seg_write_trailer(struct AVF
  {
      SegmentContext *seg = s->priv_data;
      AVFormatContext *oc = seg->avf;
-     int ret = segment_end(s);
 -    int ret = segment_end(oc);
++    int ret = segment_end(s, 1);
      if (seg->list)
 -        avio_close(seg->pb);
 +        segment_list_close(s);
 +
 +    av_opt_free(seg);
 +    av_freep(&seg->times);
 +
      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_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" },
 +    { "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 },
      { NULL },
  };