From ea5a9106287b85ea702a13e83778f80dab51143b Mon Sep 17 00:00:00 2001 From: James Almer Date: Mon, 5 Aug 2019 21:35:42 -0300 Subject: [PATCH] avformat/dashenc: allow splitting fragments following P-Frame reordering Signed-off-by: James Almer --- libavformat/dashenc.c | 69 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index cf73d82d8b..f54107f1dd 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -62,6 +62,7 @@ enum { FRAG_TYPE_NONE = 0, FRAG_TYPE_EVERY_FRAME, FRAG_TYPE_DURATION, + FRAG_TYPE_PFRAMES, FRAG_TYPE_NB }; @@ -91,6 +92,8 @@ typedef struct OutputStream { AVFormatContext *ctx; int ctx_inited, as_idx; AVIOContext *out; + AVCodecParserContext *parser; + AVCodecContext *parser_avctx; int packets_written; char initfile[1024]; int64_t init_start_pos, pos; @@ -102,6 +105,7 @@ typedef struct OutputStream { Segment **segments; int64_t first_pts, start_pts, max_pts; int64_t last_dts, last_pts; + int last_flags; int bit_rate; SegmentType segment_type; /* segment type selected for this particular stream */ const char *format_name; @@ -117,6 +121,7 @@ typedef struct OutputStream { char temp_path[1024]; double availability_time_offset; int total_pkt_size; + int64_t total_pkt_duration; int muxer_overhead; int frag_type; } OutputStream; @@ -606,6 +611,8 @@ static void dash_free(AVFormatContext *s) } ff_format_io_close(s, &os->out); avformat_free_context(os->ctx); + avcodec_free_context(&os->parser_avctx); + av_parser_close(os->parser); for (j = 0; j < os->nb_segments; j++) av_free(os->segments[j]); av_free(os->segments); @@ -933,6 +940,8 @@ static int parse_adaptation_sets(AVFormatContext *s) if (!strcmp(type_str, "duration")) as->frag_type = FRAG_TYPE_DURATION; + else if (!strcmp(type_str, "pframes")) + as->frag_type = FRAG_TYPE_PFRAMES; else if (!strcmp(type_str, "every_frame")) as->frag_type = FRAG_TYPE_EVERY_FRAME; else if (!strcmp(type_str, "none")) @@ -1349,6 +1358,18 @@ static int dash_init(AVFormatContext *s) ctx->avoid_negative_ts = s->avoid_negative_ts; ctx->flags = s->flags; + os->parser = av_parser_init(st->codecpar->codec_id); + if (os->parser) { + os->parser_avctx = avcodec_alloc_context3(NULL); + if (!os->parser_avctx) + return AVERROR(ENOMEM); + ret = avcodec_parameters_to_context(os->parser_avctx, st->codecpar); + if (ret < 0) + return ret; + // We only want to parse frame headers + os->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + } + if (c->single_file) { if (os->single_file_name) ff_dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), os->single_file_name, i, 0, os->bit_rate, 0); @@ -1391,6 +1412,11 @@ static int dash_init(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Fragment duration %"PRId64" is longer than Segment duration %"PRId64"\n", os->frag_duration, os->seg_duration); return AVERROR(EINVAL); } + if (os->frag_type == FRAG_TYPE_PFRAMES && (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || !os->parser)) { + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && !os->parser) + av_log(s, AV_LOG_WARNING, "frag_type set to P-Frame reordering, but no parser found for stream %d\n", i); + os->frag_type = c->streaming ? FRAG_TYPE_EVERY_FRAME : FRAG_TYPE_NONE; + } if (os->segment_type == SEGMENT_TYPE_MP4) { if (c->streaming) @@ -1458,6 +1484,8 @@ static int dash_init(AVFormatContext *s) av_log(s, AV_LOG_WARNING, "no video stream and no seg duration set\n"); return AVERROR(EINVAL); } + if (!c->has_video && c->frag_type == FRAG_TYPE_PFRAMES) + av_log(s, AV_LOG_WARNING, "no video stream and P-frame fragmentation set\n"); c->nr_of_streams_flushed = 0; @@ -1730,6 +1758,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream) av_rescale_q(os->max_pts - os->start_pts, st->time_base, AV_TIME_BASE_Q); os->total_pkt_size = 0; + os->total_pkt_duration = 0; if (!os->bit_rate) { // calculate average bitrate of first segment @@ -1831,6 +1860,9 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) sizeof(c->availability_start_time)); } + if (!os->packets_written) + os->availability_time_offset = 0; + if (!os->availability_time_offset && ((os->frag_type == FRAG_TYPE_DURATION && os->seg_duration != os->frag_duration) || (os->frag_type == FRAG_TYPE_EVERY_FRAME && pkt->duration))) { @@ -1895,11 +1927,43 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) os->max_pts = pkt->pts + pkt->duration; else os->max_pts = FFMAX(os->max_pts, pkt->pts + pkt->duration); - os->packets_written++; - os->total_pkt_size += pkt->size; + + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && + os->frag_type == FRAG_TYPE_PFRAMES && + os->packets_written) { + uint8_t *data; + int size; + + av_assert0(os->parser); + av_parser_parse2(os->parser, os->parser_avctx, + &data, &size, pkt->data, pkt->size, + pkt->pts, pkt->dts, pkt->pos); + + if ((os->parser->pict_type == AV_PICTURE_TYPE_P && + st->codecpar->video_delay && + !(os->last_flags & AV_PKT_FLAG_KEY)) || + pkt->flags & AV_PKT_FLAG_KEY) { + ret = av_write_frame(os->ctx, NULL); + if (ret < 0) + return ret; + + if (!os->availability_time_offset) { + int64_t frag_duration = av_rescale_q(os->total_pkt_duration, st->time_base, + AV_TIME_BASE_Q); + os->availability_time_offset = ((double) os->seg_duration - + frag_duration) / AV_TIME_BASE; + } + } + } + if ((ret = ff_write_chained(os->ctx, 0, pkt, s, 0)) < 0) return ret; + os->packets_written++; + os->total_pkt_size += pkt->size; + os->total_pkt_duration += pkt->duration; + os->last_flags = pkt->flags; + if (!os->init_range_length) flush_init_segment(s, os); @@ -2026,6 +2090,7 @@ static const AVOption options[] = { { "none", "one fragment per segment", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_NONE }, 0, UINT_MAX, E, "frag_type"}, { "every_frame", "fragment at every frame", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_EVERY_FRAME }, 0, UINT_MAX, E, "frag_type"}, { "duration", "fragment at specific time intervals", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_DURATION }, 0, UINT_MAX, E, "frag_type"}, + { "pframes", "fragment at keyframes and following P-Frame reordering (Video only, experimental)", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_PFRAMES }, 0, UINT_MAX, E, "frag_type"}, { "remove_at_exit", "remove all segments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { "use_template", "Use SegmentTemplate instead of SegmentList", OFFSET(use_template), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E }, { "use_timeline", "Use SegmentTimeline in SegmentTemplate", OFFSET(use_timeline), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E },