Message ID | 1501569234-29896-2-git-send-email-rodger.combs@gmail.com |
---|---|
State | Superseded |
Headers | show |
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
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>
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 --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, \