Commit 7a8face9 authored by Stefano Sabatini's avatar Stefano Sabatini

lavf/segment: add reference_stream option

parent 9e04e11e
...@@ -493,7 +493,9 @@ streaming output formats, i.e. which do not require global headers, ...@@ -493,7 +493,9 @@ streaming output formats, i.e. which do not require global headers,
and is recommended for outputting e.g. to MPEG transport stream segments. and is recommended for outputting e.g. to MPEG transport stream segments.
@code{ssegment} is a shorter alias for @code{stream_segment}. @code{ssegment} is a shorter alias for @code{stream_segment}.
Every segment starts with a video keyframe, if a video stream is present. Every segment starts with a keyframe of the selected reference stream,
which is set through the @option{reference_stream} option.
Note that if you want accurate splitting for a video file, you need to Note that if you want accurate splitting for a video file, you need to
make the input key frames correspond to the exact splitting times make the input key frames correspond to the exact splitting times
expected by the segmenter, or the segment muxer will start the new expected by the segmenter, or the segment muxer will start the new
...@@ -509,6 +511,13 @@ the option @var{segment_list}. The list type is specified by the ...@@ -509,6 +511,13 @@ the option @var{segment_list}. The list type is specified by the
The segment muxer supports the following options: The segment muxer supports the following options:
@table @option @table @option
@item reference_stream @var{specifier}
Set the reference stream, as specified by the string @var{specifier}.
If @var{specifier} is set to @code{auto}, the reference is choosen
automatically. Otherwise it must a stream specifier (see the ``Stream
specifiers'' chapter in the ffmpeg manual) which specifies the
reference stream. The default value is ``auto''.
@item segment_format @var{format} @item segment_format @var{format}
Override the inner container format, by default it is guessed by the filename Override the inner container format, by default it is guessed by the filename
extension. extension.
......
...@@ -74,7 +74,9 @@ typedef struct { ...@@ -74,7 +74,9 @@ typedef struct {
int write_header_trailer; /**< Set by a private option. */ int write_header_trailer; /**< Set by a private option. */
int reset_timestamps; ///< reset timestamps at the begin of each segment int reset_timestamps; ///< reset timestamps at the begin of each segment
int has_video; char *reference_stream_specifier; ///< reference stream specifier
int reference_stream_index;
double start_time, end_time; double start_time, end_time;
int64_t start_pts, start_dts; int64_t start_pts, start_dts;
int is_first_pkt; ///< tells if it is the first packet in the segment int is_first_pkt; ///< tells if it is the first packet in the segment
...@@ -398,14 +400,57 @@ static int seg_write_header(AVFormatContext *s) ...@@ -398,14 +400,57 @@ static int seg_write_header(AVFormatContext *s)
if (seg->list_type == LIST_TYPE_EXT) if (seg->list_type == LIST_TYPE_EXT)
av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n"); av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
for (i = 0; i < s->nb_streams; i++) seg->reference_stream_index = -1;
seg->has_video += if (!strcmp(seg->reference_stream_specifier, "auto")) {
(s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO); /* select first index of type with highest priority */
int type_index_map[AVMEDIA_TYPE_NB];
static const enum AVMediaType type_priority_list[] = {
AVMEDIA_TYPE_VIDEO,
AVMEDIA_TYPE_AUDIO,
AVMEDIA_TYPE_SUBTITLE,
AVMEDIA_TYPE_DATA,
AVMEDIA_TYPE_ATTACHMENT
};
enum AVMediaType type;
for (i = 0; i < AVMEDIA_TYPE_NB; i++)
type_index_map[i] = -1;
/* select first index for each type */
for (i = 0; i < s->nb_streams; i++) {
type = s->streams[i]->codec->codec_type;
if ((unsigned)type < AVMEDIA_TYPE_NB && type_index_map[type] == -1)
type_index_map[type] = i;
}
for (i = 0; i < FF_ARRAY_ELEMS(type_priority_list); i++) {
type = type_priority_list[i];
if ((seg->reference_stream_index = type_index_map[type]) >= 0)
break;
}
} else {
for (i = 0; i < s->nb_streams; i++) {
ret = avformat_match_stream_specifier(s, s->streams[i],
seg->reference_stream_specifier);
if (ret < 0)
goto fail;
if (ret > 0) {
seg->reference_stream_index = i;
break;
}
}
}
if (seg->reference_stream_index < 0) {
av_log(s, AV_LOG_ERROR, "Could not select stream matching identifier '%s'\n",
seg->reference_stream_specifier);
ret = AVERROR(EINVAL);
goto fail;
}
if (seg->has_video > 1) av_log(s, AV_LOG_VERBOSE, "Selected stream id:%d type:%s\n",
av_log(s, AV_LOG_WARNING, seg->reference_stream_index,
"More than a single video stream present, " av_get_media_type_string(s->streams[seg->reference_stream_index]->codec->codec_type));
"expect issues decoding it.\n");
seg->oformat = av_guess_format(seg->format, s->filename, NULL); seg->oformat = av_guess_format(seg->format, s->filename, NULL);
...@@ -478,8 +523,7 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) ...@@ -478,8 +523,7 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
end_pts = seg->time * seg->segment_count; end_pts = seg->time * seg->segment_count;
} }
/* if the segment has video, start a new segment *only* with a key video frame */ if (pkt->stream_index == seg->reference_stream_index &&
if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO || !seg->has_video) &&
pkt->pts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE &&
av_compare_ts(pkt->pts, st->time_base, av_compare_ts(pkt->pts, st->time_base,
end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0 && end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0 &&
...@@ -565,6 +609,7 @@ fail: ...@@ -565,6 +609,7 @@ fail:
#define OFFSET(x) offsetof(SegmentContext, x) #define OFFSET(x) offsetof(SegmentContext, x)
#define E AV_OPT_FLAG_ENCODING_PARAM #define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = { static const AVOption options[] = {
{ "reference_stream", "set reference stream", OFFSET(reference_stream_specifier), AV_OPT_TYPE_STRING, {.str = "auto"}, CHAR_MIN, CHAR_MAX, E },
{ "segment_format", "set container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, { "segment_format", "set container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
{ "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, { "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#define LIBAVFORMAT_VERSION_MAJOR 54 #define LIBAVFORMAT_VERSION_MAJOR 54
#define LIBAVFORMAT_VERSION_MINOR 50 #define LIBAVFORMAT_VERSION_MINOR 50
#define LIBAVFORMAT_VERSION_MICRO 102 #define LIBAVFORMAT_VERSION_MICRO 103
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
LIBAVFORMAT_VERSION_MINOR, \ LIBAVFORMAT_VERSION_MINOR, \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment