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

2017-11-21 Thread Rostislav Pehlivanov
On 2 August 2017 at 07:30, Rodger Combs  wrote:

> ---
>  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);
> +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(>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, >frames, >nb_frames,
> seg->frames_str)) < 0)
>  return ret;
> -} else {
> +} else if (!seg->split_chapters) {
>  /* set default value if not specified */
>  if (!seg->time_str)
>  

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

2017-08-01 Thread Steven Liu
2017-08-01 15:18 GMT+08:00 Rodger Combs :
> 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  wrote:
>>
>> 2017-08-01 14:33 GMT+08:00 Rodger Combs > >:
>>> ---
>>> 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(>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, 

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

2017-08-01 Thread Rodger Combs
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  wrote:
> 
> 2017-08-01 14:33 GMT+08:00 Rodger Combs  >:
>> ---
>> 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(>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 

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

2017-08-01 Thread Steven Liu
2017-08-01 14:33 GMT+08:00 Rodger Combs :
> ---
>  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(>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, >frames, >nb_frames, 
> seg->frames_str)) < 0)
>  return ret;
> -} else {
> +} else if (!seg->split_chapters) {
>  /* set default value if not specified */
>  if 

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

2017-08-01 Thread Rodger Combs
---
 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);
+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(>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, >frames, >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(>avf->metadata, s->chapters[0]->metadata, 0)) < 0)
+return ret;
+
 if ((ret = 

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

2017-05-08 Thread Steven Liu
2017-05-08 12:36 GMT+08:00 Rodger Combs :

> ---
>  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 0866142..b80bc68 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -1521,6 +1521,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 8ec3653..8575d32 100644
> --- a/libavformat/segment.c
> +++ b/libavformat/segment.c
> @@ -108,6 +108,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. */
> @@ -188,6 +190,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 == '$') {
>
double check?

> +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;
> @@ -212,6 +251,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)
> @@ -238,6 +280,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(>metadata, s->chapters[seg->segment_count]->metadata,
> 0)) < 0)
> +return err;
>  }
>
>  seg->segment_idx++;
> @@ -661,10 +705,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);
>  }
>
> @@ -674,7 +722,7 @@ static int seg_init(AVFormatContext *s)
>  } else if (seg->frames_str) {
>  if ((ret = parse_frames(s, >frames, >nb_frames,
> seg->frames_str)) < 0)
>  return ret;
> -} else {
> +} else if (!seg->split_chapters) {
>  /* set default value if not specified */
>  if 

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

2017-05-07 Thread Rodger Combs
---
 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 0866142..b80bc68 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -1521,6 +1521,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 8ec3653..8575d32 100644
--- a/libavformat/segment.c
+++ b/libavformat/segment.c
@@ -108,6 +108,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. */
@@ -188,6 +190,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;
@@ -212,6 +251,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)
@@ -238,6 +280,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(>metadata, s->chapters[seg->segment_count]->metadata, 0)) < 
0)
+return err;
 }
 
 seg->segment_idx++;
@@ -661,10 +705,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);
 }
 
@@ -674,7 +722,7 @@ static int seg_init(AVFormatContext *s)
 } else if (seg->frames_str) {
 if ((ret = parse_frames(s, >frames, >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");
@@ -741,6 +789,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(>avf->metadata, s->chapters[0]->metadata, 0)) < 0)
+return ret;
+
 if ((ret =