diff mbox

[FFmpeg-devel,2/7] lavf/segment: add option to segment by chapter

Message ID 1501569234-29896-2-git-send-email-rodger.combs@gmail.com
State Superseded
Headers show

Commit Message

Rodger Combs Aug. 1, 2017, 6:33 a.m. UTC
---
 doc/muxers.texi       |  6 +++++
 libavformat/segment.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++----
 libavformat/version.h |  2 +-
 3 files changed, 67 insertions(+), 6 deletions(-)

Comments

Steven Liu Aug. 1, 2017, 6:54 a.m. UTC | #1
2017-08-01 14:33 GMT+08:00 Rodger Combs <rodger.combs@gmail.com>:
> ---
>  doc/muxers.texi       |  6 +++++
>  libavformat/segment.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++----
>  libavformat/version.h |  2 +-
>  3 files changed, 67 insertions(+), 6 deletions(-)
>
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index 94472ce..23ef2e7 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -1538,6 +1538,12 @@ This option specifies to start a new segment whenever a reference
>  stream key frame is found and the sequential number (starting from 0)
>  of the frame is greater or equal to the next value in the list.
>
> +@item segment_chapters @var{1|0}
> +Split each chapter into its own segment. Metadata from the chapters
> +will be written to the corresponding segments. If this option is selected
> +and the filename contains tokens in the format @code{$varname$}, they
> +will be replaced by the corresponding metadata values.
> +
>  @item segment_wrap @var{limit}
>  Wrap around segment index once it reaches @var{limit}.
>
> diff --git a/libavformat/segment.c b/libavformat/segment.c
> index 0e8bcdd..590f62b 100644
> --- a/libavformat/segment.c
> +++ b/libavformat/segment.c
> @@ -106,6 +106,8 @@ typedef struct SegmentContext {
>      int frame_count;       ///< total number of reference frames
>      int segment_frame_count; ///< number of reference frames in the segment
>
> +    int split_chapters;    ///< split on chapter markers
> +
>      int64_t time_delta;
>      int  individual_header_trailer; /**< Set by a private option. */
>      int  write_header_trailer; /**< Set by a private option. */
> @@ -186,6 +188,43 @@ static int segment_mux_init(AVFormatContext *s)
>      return 0;
>  }
>
> +static int replace_variables(AVFormatContext *oc)
> +{
> +    char name[sizeof(oc->filename)];
> +    char *p = name;
> +    char *out = oc->filename;
> +    strncpy(name, oc->filename, sizeof(name));
> +    while (*p) {
> +        char c = *p++;
> +        if (c == '$') {
> +            if (*p == '$') {
> +                p++;
> +                goto append;
> +            } else {
> +                int len;
> +                const char *val;
> +                const AVDictionaryEntry *e;
> +                int end = strcspn(p, "$");
> +                if (p[end] == '\0')
> +                    continue;
> +                p[end] = '\0';
> +                e = av_dict_get(oc->metadata, p, NULL, 0);
> +                val = e ? e->value : "(unknown)";
> +                len = strlen(val);
> +                strncpy(out, val, oc->filename + sizeof(oc->filename) - 1 - out);
why not av_strlcpy?
> +                out = FFMIN(oc->filename + sizeof(oc->filename) - 1, out + len);
> +                p += end + 1;
> +            }
> +        } else {
> +append:
> +            if (out - oc->filename < sizeof(oc->filename) - 1)
> +                *out++ = c;
> +        }
> +    }
> +    *out = '\0';
> +    return 0;
> +}
> +
>  static int set_segment_filename(AVFormatContext *s)
>  {
>      SegmentContext *seg = s->priv_data;
> @@ -210,6 +249,9 @@ static int set_segment_filename(AVFormatContext *s)
>          return AVERROR(EINVAL);
>      }
>
> +    if (seg->split_chapters)
> +        replace_variables(oc);
> +
>      /* copy modified name in list entry */
>      size = strlen(av_basename(oc->filename)) + 1;
>      if (seg->entry_prefix)
> @@ -236,6 +278,8 @@ static int segment_start(AVFormatContext *s, int write_header)
>          if ((err = segment_mux_init(s)) < 0)
>              return err;
>          oc = seg->avf;
> +        if (seg->split_chapters && seg->segment_count < s->nb_chapters && (err = av_dict_copy(&oc->metadata, s->chapters[seg->segment_count]->metadata, 0)) < 0)
> +            return err;
>      }
>
>      seg->segment_idx++;
> @@ -659,10 +703,14 @@ static int seg_init(AVFormatContext *s)
>                 "you can use output_ts_offset instead of it\n");
>      }
>
> -    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) {
> +    if (seg->segment_idx < 0)
> +        seg->segment_idx = seg->split_chapters;
> +
> +    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str + !!seg->split_chapters > 1) {
>          av_log(s, AV_LOG_ERROR,
> -               "segment_time, segment_times, and segment_frames options "
> -               "are mutually exclusive, select just one of them\n");
> +               "segment_time, segment_times, segment_frames, and "
> +               "segment_chapters options are mutually exclusive; "
> +               "select just one of them\n");
>          return AVERROR(EINVAL);
>      }
>
> @@ -672,7 +720,7 @@ static int seg_init(AVFormatContext *s)
>      } else if (seg->frames_str) {
>          if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
>              return ret;
> -    } else {
> +    } else if (!seg->split_chapters) {
>          /* set default value if not specified */
>          if (!seg->time_str)
>              seg->time_str = av_strdup("2");
> @@ -739,6 +787,9 @@ static int seg_init(AVFormatContext *s)
>      if ((ret = segment_mux_init(s)) < 0)
>          return ret;
>
> +    if (seg->split_chapters && s->nb_chapters && (ret = av_dict_copy(&seg->avf->metadata, s->chapters[0]->metadata, 0)) < 0)
> +        return ret;
> +
>      if ((ret = set_segment_filename(s)) < 0)
>          return ret;
>      oc = seg->avf;
> @@ -860,6 +911,9 @@ calc_times:
>      } else if (seg->frames) {
>          start_frame = seg->segment_count < seg->nb_frames ?
>              seg->frames[seg->segment_count] : INT_MAX;
> +    } else if (seg->split_chapters) {
> +        end_pts = seg->segment_count + 1 < s->nb_chapters ?
> +            av_rescale_q(s->chapters[seg->segment_count]->end, s->chapters[seg->segment_count]->time_base, AV_TIME_BASE_Q) : INT64_MAX;
>      } else {
>          if (seg->use_clocktime) {
>              int64_t avgt = av_gettime();
> @@ -1042,9 +1096,10 @@ static const AVOption options[] = {
>      { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0, E },
>      { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
>      { "segment_frames",    "set segment split frame numbers",            OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
> +    { "segment_chapters",  "split segments on chapter markers",          OFFSET(split_chapters), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
>      { "segment_wrap",      "set number after which the index wraps",     OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
>      { "segment_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix), AV_OPT_TYPE_STRING,  {.str = NULL}, 0, 0, E },
> -    { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
> +    { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, E },
>      { "segment_wrap_number", "set the number of wrap before the first segment", OFFSET(segment_idx_wrap_nb), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
>      { "strftime",          "set filename expansion with strftime at segment creation", OFFSET(use_strftime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
>      { "increment_tc", "increment timecode between each segment", OFFSET(increment_tc), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
> diff --git a/libavformat/version.h b/libavformat/version.h
> index a8cf4c1..4d12f5b 100644
> --- a/libavformat/version.h
> +++ b/libavformat/version.h
> @@ -33,7 +33,7 @@
>  // Also please add any ticket numbers that you believe might be affected here
>  #define LIBAVFORMAT_VERSION_MAJOR  57
>  #define LIBAVFORMAT_VERSION_MINOR  77
> -#define LIBAVFORMAT_VERSION_MICRO 100
> +#define LIBAVFORMAT_VERSION_MICRO 101
>
>  #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
>                                                 LIBAVFORMAT_VERSION_MINOR, \
> --
> 2.6.4
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Rodger Combs Aug. 1, 2017, 7:18 a.m. UTC | #2
This was pretty confusing whether it uses strlcpy or strncpy, so I'm switching it to AVBPrintf.

> On Aug 1, 2017, at 01:54, Steven Liu <lingjiujianke@gmail.com> wrote:
> 
> 2017-08-01 14:33 GMT+08:00 Rodger Combs <rodger.combs@gmail.com <mailto:rodger.combs@gmail.com>>:
>> ---
>> doc/muxers.texi       |  6 +++++
>> libavformat/segment.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++----
>> libavformat/version.h |  2 +-
>> 3 files changed, 67 insertions(+), 6 deletions(-)
>> 
>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>> index 94472ce..23ef2e7 100644
>> --- a/doc/muxers.texi
>> +++ b/doc/muxers.texi
>> @@ -1538,6 +1538,12 @@ This option specifies to start a new segment whenever a reference
>> stream key frame is found and the sequential number (starting from 0)
>> of the frame is greater or equal to the next value in the list.
>> 
>> +@item segment_chapters @var{1|0}
>> +Split each chapter into its own segment. Metadata from the chapters
>> +will be written to the corresponding segments. If this option is selected
>> +and the filename contains tokens in the format @code{$varname$}, they
>> +will be replaced by the corresponding metadata values.
>> +
>> @item segment_wrap @var{limit}
>> Wrap around segment index once it reaches @var{limit}.
>> 
>> diff --git a/libavformat/segment.c b/libavformat/segment.c
>> index 0e8bcdd..590f62b 100644
>> --- a/libavformat/segment.c
>> +++ b/libavformat/segment.c
>> @@ -106,6 +106,8 @@ typedef struct SegmentContext {
>>     int frame_count;       ///< total number of reference frames
>>     int segment_frame_count; ///< number of reference frames in the segment
>> 
>> +    int split_chapters;    ///< split on chapter markers
>> +
>>     int64_t time_delta;
>>     int  individual_header_trailer; /**< Set by a private option. */
>>     int  write_header_trailer; /**< Set by a private option. */
>> @@ -186,6 +188,43 @@ static int segment_mux_init(AVFormatContext *s)
>>     return 0;
>> }
>> 
>> +static int replace_variables(AVFormatContext *oc)
>> +{
>> +    char name[sizeof(oc->filename)];
>> +    char *p = name;
>> +    char *out = oc->filename;
>> +    strncpy(name, oc->filename, sizeof(name));
>> +    while (*p) {
>> +        char c = *p++;
>> +        if (c == '$') {
>> +            if (*p == '$') {
>> +                p++;
>> +                goto append;
>> +            } else {
>> +                int len;
>> +                const char *val;
>> +                const AVDictionaryEntry *e;
>> +                int end = strcspn(p, "$");
>> +                if (p[end] == '\0')
>> +                    continue;
>> +                p[end] = '\0';
>> +                e = av_dict_get(oc->metadata, p, NULL, 0);
>> +                val = e ? e->value : "(unknown)";
>> +                len = strlen(val);
>> +                strncpy(out, val, oc->filename + sizeof(oc->filename) - 1 - out);
> why not av_strlcpy?
>> +                out = FFMIN(oc->filename + sizeof(oc->filename) - 1, out + len);
>> +                p += end + 1;
>> +            }
>> +        } else {
>> +append:
>> +            if (out - oc->filename < sizeof(oc->filename) - 1)
>> +                *out++ = c;
>> +        }
>> +    }
>> +    *out = '\0';
>> +    return 0;
>> +}
>> +
>> static int set_segment_filename(AVFormatContext *s)
>> {
>>     SegmentContext *seg = s->priv_data;
>> @@ -210,6 +249,9 @@ static int set_segment_filename(AVFormatContext *s)
>>         return AVERROR(EINVAL);
>>     }
>> 
>> +    if (seg->split_chapters)
>> +        replace_variables(oc);
>> +
>>     /* copy modified name in list entry */
>>     size = strlen(av_basename(oc->filename)) + 1;
>>     if (seg->entry_prefix)
>> @@ -236,6 +278,8 @@ static int segment_start(AVFormatContext *s, int write_header)
>>         if ((err = segment_mux_init(s)) < 0)
>>             return err;
>>         oc = seg->avf;
>> +        if (seg->split_chapters && seg->segment_count < s->nb_chapters && (err = av_dict_copy(&oc->metadata, s->chapters[seg->segment_count]->metadata, 0)) < 0)
>> +            return err;
>>     }
>> 
>>     seg->segment_idx++;
>> @@ -659,10 +703,14 @@ static int seg_init(AVFormatContext *s)
>>                "you can use output_ts_offset instead of it\n");
>>     }
>> 
>> -    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) {
>> +    if (seg->segment_idx < 0)
>> +        seg->segment_idx = seg->split_chapters;
>> +
>> +    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str + !!seg->split_chapters > 1) {
>>         av_log(s, AV_LOG_ERROR,
>> -               "segment_time, segment_times, and segment_frames options "
>> -               "are mutually exclusive, select just one of them\n");
>> +               "segment_time, segment_times, segment_frames, and "
>> +               "segment_chapters options are mutually exclusive; "
>> +               "select just one of them\n");
>>         return AVERROR(EINVAL);
>>     }
>> 
>> @@ -672,7 +720,7 @@ static int seg_init(AVFormatContext *s)
>>     } else if (seg->frames_str) {
>>         if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
>>             return ret;
>> -    } else {
>> +    } else if (!seg->split_chapters) {
>>         /* set default value if not specified */
>>         if (!seg->time_str)
>>             seg->time_str = av_strdup("2");
>> @@ -739,6 +787,9 @@ static int seg_init(AVFormatContext *s)
>>     if ((ret = segment_mux_init(s)) < 0)
>>         return ret;
>> 
>> +    if (seg->split_chapters && s->nb_chapters && (ret = av_dict_copy(&seg->avf->metadata, s->chapters[0]->metadata, 0)) < 0)
>> +        return ret;
>> +
>>     if ((ret = set_segment_filename(s)) < 0)
>>         return ret;
>>     oc = seg->avf;
>> @@ -860,6 +911,9 @@ calc_times:
>>     } else if (seg->frames) {
>>         start_frame = seg->segment_count < seg->nb_frames ?
>>             seg->frames[seg->segment_count] : INT_MAX;
>> +    } else if (seg->split_chapters) {
>> +        end_pts = seg->segment_count + 1 < s->nb_chapters ?
>> +            av_rescale_q(s->chapters[seg->segment_count]->end, s->chapters[seg->segment_count]->time_base, AV_TIME_BASE_Q) : INT64_MAX;
>>     } else {
>>         if (seg->use_clocktime) {
>>             int64_t avgt = av_gettime();
>> @@ -1042,9 +1096,10 @@ static const AVOption options[] = {
>>     { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0, E },
>>     { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
>>     { "segment_frames",    "set segment split frame numbers",            OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
>> +    { "segment_chapters",  "split segments on chapter markers",          OFFSET(split_chapters), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
>>     { "segment_wrap",      "set number after which the index wraps",     OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
>>     { "segment_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix), AV_OPT_TYPE_STRING,  {.str = NULL}, 0, 0, E },
>> -    { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
>> +    { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, E },
>>     { "segment_wrap_number", "set the number of wrap before the first segment", OFFSET(segment_idx_wrap_nb), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
>>     { "strftime",          "set filename expansion with strftime at segment creation", OFFSET(use_strftime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
>>     { "increment_tc", "increment timecode between each segment", OFFSET(increment_tc), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
>> diff --git a/libavformat/version.h b/libavformat/version.h
>> index a8cf4c1..4d12f5b 100644
>> --- a/libavformat/version.h
>> +++ b/libavformat/version.h
>> @@ -33,7 +33,7 @@
>> // Also please add any ticket numbers that you believe might be affected here
>> #define LIBAVFORMAT_VERSION_MAJOR  57
>> #define LIBAVFORMAT_VERSION_MINOR  77
>> -#define LIBAVFORMAT_VERSION_MICRO 100
>> +#define LIBAVFORMAT_VERSION_MICRO 101
>> 
>> #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
>>                                                LIBAVFORMAT_VERSION_MINOR, \
>> --
>> 2.6.4
>> 
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org <mailto:ffmpeg-devel@ffmpeg.org>
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel <http://ffmpeg.org/mailman/listinfo/ffmpeg-devel>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org <mailto:ffmpeg-devel@ffmpeg.org>
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel <http://ffmpeg.org/mailman/listinfo/ffmpeg-devel>
Steven Liu Aug. 1, 2017, 7:24 a.m. UTC | #3
2017-08-01 15:18 GMT+08:00 Rodger Combs <rodger.combs@gmail.com>:
> This was pretty confusing whether it uses strlcpy or strncpy, so I'm switching it to AVBPrintf.
That's a better way :D
>
>> On Aug 1, 2017, at 01:54, Steven Liu <lingjiujianke@gmail.com> wrote:
>>
>> 2017-08-01 14:33 GMT+08:00 Rodger Combs <rodger.combs@gmail.com <mailto:rodger.combs@gmail.com>>:
>>> ---
>>> doc/muxers.texi       |  6 +++++
>>> libavformat/segment.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++----
>>> libavformat/version.h |  2 +-
>>> 3 files changed, 67 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>>> index 94472ce..23ef2e7 100644
>>> --- a/doc/muxers.texi
>>> +++ b/doc/muxers.texi
>>> @@ -1538,6 +1538,12 @@ This option specifies to start a new segment whenever a reference
>>> stream key frame is found and the sequential number (starting from 0)
>>> of the frame is greater or equal to the next value in the list.
>>>
>>> +@item segment_chapters @var{1|0}
>>> +Split each chapter into its own segment. Metadata from the chapters
>>> +will be written to the corresponding segments. If this option is selected
>>> +and the filename contains tokens in the format @code{$varname$}, they
>>> +will be replaced by the corresponding metadata values.
>>> +
>>> @item segment_wrap @var{limit}
>>> Wrap around segment index once it reaches @var{limit}.
>>>
>>> diff --git a/libavformat/segment.c b/libavformat/segment.c
>>> index 0e8bcdd..590f62b 100644
>>> --- a/libavformat/segment.c
>>> +++ b/libavformat/segment.c
>>> @@ -106,6 +106,8 @@ typedef struct SegmentContext {
>>>     int frame_count;       ///< total number of reference frames
>>>     int segment_frame_count; ///< number of reference frames in the segment
>>>
>>> +    int split_chapters;    ///< split on chapter markers
>>> +
>>>     int64_t time_delta;
>>>     int  individual_header_trailer; /**< Set by a private option. */
>>>     int  write_header_trailer; /**< Set by a private option. */
>>> @@ -186,6 +188,43 @@ static int segment_mux_init(AVFormatContext *s)
>>>     return 0;
>>> }
>>>
>>> +static int replace_variables(AVFormatContext *oc)
>>> +{
>>> +    char name[sizeof(oc->filename)];
>>> +    char *p = name;
>>> +    char *out = oc->filename;
>>> +    strncpy(name, oc->filename, sizeof(name));
>>> +    while (*p) {
>>> +        char c = *p++;
>>> +        if (c == '$') {
>>> +            if (*p == '$') {
>>> +                p++;
>>> +                goto append;
>>> +            } else {
>>> +                int len;
>>> +                const char *val;
>>> +                const AVDictionaryEntry *e;
>>> +                int end = strcspn(p, "$");
>>> +                if (p[end] == '\0')
>>> +                    continue;
>>> +                p[end] = '\0';
>>> +                e = av_dict_get(oc->metadata, p, NULL, 0);
>>> +                val = e ? e->value : "(unknown)";
>>> +                len = strlen(val);
>>> +                strncpy(out, val, oc->filename + sizeof(oc->filename) - 1 - out);
>> why not av_strlcpy?
>>> +                out = FFMIN(oc->filename + sizeof(oc->filename) - 1, out + len);
>>> +                p += end + 1;
>>> +            }
>>> +        } else {
>>> +append:
>>> +            if (out - oc->filename < sizeof(oc->filename) - 1)
>>> +                *out++ = c;
>>> +        }
>>> +    }
>>> +    *out = '\0';
>>> +    return 0;
>>> +}
>>> +
>>> static int set_segment_filename(AVFormatContext *s)
>>> {
>>>     SegmentContext *seg = s->priv_data;
>>> @@ -210,6 +249,9 @@ static int set_segment_filename(AVFormatContext *s)
>>>         return AVERROR(EINVAL);
>>>     }
>>>
>>> +    if (seg->split_chapters)
>>> +        replace_variables(oc);
>>> +
>>>     /* copy modified name in list entry */
>>>     size = strlen(av_basename(oc->filename)) + 1;
>>>     if (seg->entry_prefix)
>>> @@ -236,6 +278,8 @@ static int segment_start(AVFormatContext *s, int write_header)
>>>         if ((err = segment_mux_init(s)) < 0)
>>>             return err;
>>>         oc = seg->avf;
>>> +        if (seg->split_chapters && seg->segment_count < s->nb_chapters && (err = av_dict_copy(&oc->metadata, s->chapters[seg->segment_count]->metadata, 0)) < 0)
>>> +            return err;
>>>     }
>>>
>>>     seg->segment_idx++;
>>> @@ -659,10 +703,14 @@ static int seg_init(AVFormatContext *s)
>>>                "you can use output_ts_offset instead of it\n");
>>>     }
>>>
>>> -    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) {
>>> +    if (seg->segment_idx < 0)
>>> +        seg->segment_idx = seg->split_chapters;
>>> +
>>> +    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str + !!seg->split_chapters > 1) {
>>>         av_log(s, AV_LOG_ERROR,
>>> -               "segment_time, segment_times, and segment_frames options "
>>> -               "are mutually exclusive, select just one of them\n");
>>> +               "segment_time, segment_times, segment_frames, and "
>>> +               "segment_chapters options are mutually exclusive; "
>>> +               "select just one of them\n");
>>>         return AVERROR(EINVAL);
>>>     }
>>>
>>> @@ -672,7 +720,7 @@ static int seg_init(AVFormatContext *s)
>>>     } else if (seg->frames_str) {
>>>         if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
>>>             return ret;
>>> -    } else {
>>> +    } else if (!seg->split_chapters) {
>>>         /* set default value if not specified */
>>>         if (!seg->time_str)
>>>             seg->time_str = av_strdup("2");
>>> @@ -739,6 +787,9 @@ static int seg_init(AVFormatContext *s)
>>>     if ((ret = segment_mux_init(s)) < 0)
>>>         return ret;
>>>
>>> +    if (seg->split_chapters && s->nb_chapters && (ret = av_dict_copy(&seg->avf->metadata, s->chapters[0]->metadata, 0)) < 0)
>>> +        return ret;
>>> +
>>>     if ((ret = set_segment_filename(s)) < 0)
>>>         return ret;
>>>     oc = seg->avf;
>>> @@ -860,6 +911,9 @@ calc_times:
>>>     } else if (seg->frames) {
>>>         start_frame = seg->segment_count < seg->nb_frames ?
>>>             seg->frames[seg->segment_count] : INT_MAX;
>>> +    } else if (seg->split_chapters) {
>>> +        end_pts = seg->segment_count + 1 < s->nb_chapters ?
>>> +            av_rescale_q(s->chapters[seg->segment_count]->end, s->chapters[seg->segment_count]->time_base, AV_TIME_BASE_Q) : INT64_MAX;
>>>     } else {
>>>         if (seg->use_clocktime) {
>>>             int64_t avgt = av_gettime();
>>> @@ -1042,9 +1096,10 @@ static const AVOption options[] = {
>>>     { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0, E },
>>>     { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
>>>     { "segment_frames",    "set segment split frame numbers",            OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
>>> +    { "segment_chapters",  "split segments on chapter markers",          OFFSET(split_chapters), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
>>>     { "segment_wrap",      "set number after which the index wraps",     OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
>>>     { "segment_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix), AV_OPT_TYPE_STRING,  {.str = NULL}, 0, 0, E },
>>> -    { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
>>> +    { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, E },
>>>     { "segment_wrap_number", "set the number of wrap before the first segment", OFFSET(segment_idx_wrap_nb), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
>>>     { "strftime",          "set filename expansion with strftime at segment creation", OFFSET(use_strftime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
>>>     { "increment_tc", "increment timecode between each segment", OFFSET(increment_tc), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
>>> diff --git a/libavformat/version.h b/libavformat/version.h
>>> index a8cf4c1..4d12f5b 100644
>>> --- a/libavformat/version.h
>>> +++ b/libavformat/version.h
>>> @@ -33,7 +33,7 @@
>>> // Also please add any ticket numbers that you believe might be affected here
>>> #define LIBAVFORMAT_VERSION_MAJOR  57
>>> #define LIBAVFORMAT_VERSION_MINOR  77
>>> -#define LIBAVFORMAT_VERSION_MICRO 100
>>> +#define LIBAVFORMAT_VERSION_MICRO 101
>>>
>>> #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
>>>                                                LIBAVFORMAT_VERSION_MINOR, \
>>> --
>>> 2.6.4
>>>
>>> _______________________________________________
>>> ffmpeg-devel mailing list
>>> ffmpeg-devel@ffmpeg.org <mailto:ffmpeg-devel@ffmpeg.org>
>>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel <http://ffmpeg.org/mailman/listinfo/ffmpeg-devel>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org <mailto:ffmpeg-devel@ffmpeg.org>
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel <http://ffmpeg.org/mailman/listinfo/ffmpeg-devel>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
diff mbox

Patch

diff --git a/doc/muxers.texi b/doc/muxers.texi
index 94472ce..23ef2e7 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -1538,6 +1538,12 @@  This option specifies to start a new segment whenever a reference
 stream key frame is found and the sequential number (starting from 0)
 of the frame is greater or equal to the next value in the list.
 
+@item segment_chapters @var{1|0}
+Split each chapter into its own segment. Metadata from the chapters
+will be written to the corresponding segments. If this option is selected
+and the filename contains tokens in the format @code{$varname$}, they
+will be replaced by the corresponding metadata values.
+
 @item segment_wrap @var{limit}
 Wrap around segment index once it reaches @var{limit}.
 
diff --git a/libavformat/segment.c b/libavformat/segment.c
index 0e8bcdd..590f62b 100644
--- a/libavformat/segment.c
+++ b/libavformat/segment.c
@@ -106,6 +106,8 @@  typedef struct SegmentContext {
     int frame_count;       ///< total number of reference frames
     int segment_frame_count; ///< number of reference frames in the segment
 
+    int split_chapters;    ///< split on chapter markers
+
     int64_t time_delta;
     int  individual_header_trailer; /**< Set by a private option. */
     int  write_header_trailer; /**< Set by a private option. */
@@ -186,6 +188,43 @@  static int segment_mux_init(AVFormatContext *s)
     return 0;
 }
 
+static int replace_variables(AVFormatContext *oc)
+{
+    char name[sizeof(oc->filename)];
+    char *p = name;
+    char *out = oc->filename;
+    strncpy(name, oc->filename, sizeof(name));
+    while (*p) {
+        char c = *p++;
+        if (c == '$') {
+            if (*p == '$') {
+                p++;
+                goto append;
+            } else {
+                int len;
+                const char *val;
+                const AVDictionaryEntry *e;
+                int end = strcspn(p, "$");
+                if (p[end] == '\0')
+                    continue;
+                p[end] = '\0';
+                e = av_dict_get(oc->metadata, p, NULL, 0);
+                val = e ? e->value : "(unknown)";
+                len = strlen(val);
+                strncpy(out, val, oc->filename + sizeof(oc->filename) - 1 - out);
+                out = FFMIN(oc->filename + sizeof(oc->filename) - 1, out + len);
+                p += end + 1;
+            }
+        } else {
+append:
+            if (out - oc->filename < sizeof(oc->filename) - 1)
+                *out++ = c;
+        }
+    }
+    *out = '\0';
+    return 0;
+}
+
 static int set_segment_filename(AVFormatContext *s)
 {
     SegmentContext *seg = s->priv_data;
@@ -210,6 +249,9 @@  static int set_segment_filename(AVFormatContext *s)
         return AVERROR(EINVAL);
     }
 
+    if (seg->split_chapters)
+        replace_variables(oc);
+
     /* copy modified name in list entry */
     size = strlen(av_basename(oc->filename)) + 1;
     if (seg->entry_prefix)
@@ -236,6 +278,8 @@  static int segment_start(AVFormatContext *s, int write_header)
         if ((err = segment_mux_init(s)) < 0)
             return err;
         oc = seg->avf;
+        if (seg->split_chapters && seg->segment_count < s->nb_chapters && (err = av_dict_copy(&oc->metadata, s->chapters[seg->segment_count]->metadata, 0)) < 0)
+            return err;
     }
 
     seg->segment_idx++;
@@ -659,10 +703,14 @@  static int seg_init(AVFormatContext *s)
                "you can use output_ts_offset instead of it\n");
     }
 
-    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) {
+    if (seg->segment_idx < 0)
+        seg->segment_idx = seg->split_chapters;
+
+    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str + !!seg->split_chapters > 1) {
         av_log(s, AV_LOG_ERROR,
-               "segment_time, segment_times, and segment_frames options "
-               "are mutually exclusive, select just one of them\n");
+               "segment_time, segment_times, segment_frames, and "
+               "segment_chapters options are mutually exclusive; "
+               "select just one of them\n");
         return AVERROR(EINVAL);
     }
 
@@ -672,7 +720,7 @@  static int seg_init(AVFormatContext *s)
     } else if (seg->frames_str) {
         if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
             return ret;
-    } else {
+    } else if (!seg->split_chapters) {
         /* set default value if not specified */
         if (!seg->time_str)
             seg->time_str = av_strdup("2");
@@ -739,6 +787,9 @@  static int seg_init(AVFormatContext *s)
     if ((ret = segment_mux_init(s)) < 0)
         return ret;
 
+    if (seg->split_chapters && s->nb_chapters && (ret = av_dict_copy(&seg->avf->metadata, s->chapters[0]->metadata, 0)) < 0)
+        return ret;
+
     if ((ret = set_segment_filename(s)) < 0)
         return ret;
     oc = seg->avf;
@@ -860,6 +911,9 @@  calc_times:
     } else if (seg->frames) {
         start_frame = seg->segment_count < seg->nb_frames ?
             seg->frames[seg->segment_count] : INT_MAX;
+    } else if (seg->split_chapters) {
+        end_pts = seg->segment_count + 1 < s->nb_chapters ?
+            av_rescale_q(s->chapters[seg->segment_count]->end, s->chapters[seg->segment_count]->time_base, AV_TIME_BASE_Q) : INT64_MAX;
     } else {
         if (seg->use_clocktime) {
             int64_t avgt = av_gettime();
@@ -1042,9 +1096,10 @@  static const AVOption options[] = {
     { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0, E },
     { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
     { "segment_frames",    "set segment split frame numbers",            OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
+    { "segment_chapters",  "split segments on chapter markers",          OFFSET(split_chapters), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
     { "segment_wrap",      "set number after which the index wraps",     OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
     { "segment_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix), AV_OPT_TYPE_STRING,  {.str = NULL}, 0, 0, E },
-    { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
+    { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, E },
     { "segment_wrap_number", "set the number of wrap before the first segment", OFFSET(segment_idx_wrap_nb), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
     { "strftime",          "set filename expansion with strftime at segment creation", OFFSET(use_strftime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
     { "increment_tc", "increment timecode between each segment", OFFSET(increment_tc), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
diff --git a/libavformat/version.h b/libavformat/version.h
index a8cf4c1..4d12f5b 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -33,7 +33,7 @@ 
 // Also please add any ticket numbers that you believe might be affected here
 #define LIBAVFORMAT_VERSION_MAJOR  57
 #define LIBAVFORMAT_VERSION_MINOR  77
-#define LIBAVFORMAT_VERSION_MICRO 100
+#define LIBAVFORMAT_VERSION_MICRO 101
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \