Unified · Split
| @@ -62,6 +62,7 @@ enum { · 61 unmodified lines | ||
| 62 | 62 | FRAG_TYPE_NONE = 0, |
| 63 | 63 | FRAG_TYPE_EVERY_FRAME, |
| 64 | 64 | FRAG_TYPE_DURATION, |
| 65 | + FRAG_TYPE_PFRAMES, | |
| 65 | 66 | FRAG_TYPE_NB |
| 66 | 67 | }; |
| 67 | 68 | |
| @@ -91,6 +92,8 @@ typedef struct OutputStream { · 23 unmodified lines | ||
| 91 | 92 | AVFormatContext *ctx; |
| 92 | 93 | int ctx_inited, as_idx; |
| 93 | 94 | AVIOContext *out; |
| 95 | + AVCodecParserContext *parser; | |
| 96 | + AVCodecContext *parser_avctx; | |
| 94 | 97 | int packets_written; |
| 95 | 98 | char initfile[1024]; |
| 96 | 99 | int64_t init_start_pos, pos; |
| @@ -102,6 +105,7 @@ typedef struct OutputStream { · 5 unmodified lines | ||
| 102 | 105 | Segment **segments; |
| 103 | 106 | int64_t first_pts, start_pts, max_pts; |
| 104 | 107 | int64_t last_dts, last_pts; |
| 108 | + int last_flags; | |
| 105 | 109 | int bit_rate; |
| 106 | 110 | SegmentType segment_type; /* segment type selected for this particular stream */ |
| 107 | 111 | const char *format_name; |
| @@ -117,6 +121,7 @@ typedef struct OutputStream { · 9 unmodified lines | ||
| 117 | 121 | char temp_path[1024]; |
| 118 | 122 | double availability_time_offset; |
| 119 | 123 | int total_pkt_size; |
| 124 | + int64_t total_pkt_duration; | |
| 120 | 125 | int muxer_overhead; |
| 121 | 126 | int frag_type; |
| 122 | 127 | } OutputStream; |
| @@ -606,6 +611,8 @@ static void dash_free(AVFormatContext *s) · 483 unmodified lines | ||
| 606 | 611 | } |
| 607 | 612 | ff_format_io_close(s, &os->out); |
| 608 | 613 | avformat_free_context(os->ctx); |
| 614 | + avcodec_free_context(&os->parser_avctx); | |
| 615 | + av_parser_close(os->parser); | |
| 609 | 616 | for (j = 0; j < os->nb_segments; j++) |
| 610 | 617 | av_free(os->segments[j]); |
| 611 | 618 | av_free(os->segments); |
| @@ -933,6 +940,8 @@ static int parse_adaptation_sets(AVFormatContext *s) · 321 unmodified lines | ||
| 933 | 940 | |
| 934 | 941 | if (!strcmp(type_str, "duration")) |
| 935 | 942 | as->frag_type = FRAG_TYPE_DURATION; |
| 943 | + else if (!strcmp(type_str, "pframes")) | |
| 944 | + as->frag_type = FRAG_TYPE_PFRAMES; | |
| 936 | 945 | else if (!strcmp(type_str, "every_frame")) |
| 937 | 946 | as->frag_type = FRAG_TYPE_EVERY_FRAME; |
| 938 | 947 | else if (!strcmp(type_str, "none")) |
| @@ -1349,6 +1358,18 @@ static int dash_init(AVFormatContext *s) · 410 unmodified lines | ||
| 1349 | 1358 | ctx->avoid_negative_ts = s->avoid_negative_ts; |
| 1350 | 1359 | ctx->flags = s->flags; |
| 1351 | 1360 | |
| 1361 | + os->parser = av_parser_init(st->codecpar->codec_id); | |
| 1362 | + if (os->parser) { | |
| 1363 | + os->parser_avctx = avcodec_alloc_context3(NULL); | |
| 1364 | + if (!os->parser_avctx) | |
| 1365 | + return AVERROR(ENOMEM); | |
| 1366 | + ret = avcodec_parameters_to_context(os->parser_avctx, st->codecpar); | |
| 1367 | + if (ret < 0) | |
| 1368 | + return ret; | |
| 1369 | + // We only want to parse frame headers | |
| 1370 | + os->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; | |
| 1371 | + } | |
| 1372 | + | |
| 1352 | 1373 | if (c->single_file) { |
| 1353 | 1374 | if (os->single_file_name) |
| 1354 | 1375 | 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) · 36 unmodified lines | ||
| 1391 | 1412 | av_log(s, AV_LOG_ERROR, "Fragment duration %"PRId64" is longer than Segment duration %"PRId64"\n", os->frag_duration, os->seg_duration); |
| 1392 | 1413 | return AVERROR(EINVAL); |
| 1393 | 1414 | } |
| 1415 | + if (os->frag_type == FRAG_TYPE_PFRAMES && (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || !os->parser)) { | |
| 1416 | + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && !os->parser) | |
| 1417 | + av_log(s, AV_LOG_WARNING, "frag_type set to P-Frame reordering, but no parser found for stream %d\n", i); | |
| 1418 | + os->frag_type = c->streaming ? FRAG_TYPE_EVERY_FRAME : FRAG_TYPE_NONE; | |
| 1419 | + } | |
| 1394 | 1420 | |
| 1395 | 1421 | if (os->segment_type == SEGMENT_TYPE_MP4) { |
| 1396 | 1422 | if (c->streaming) |
| @@ -1458,6 +1484,8 @@ static int dash_init(AVFormatContext *s) · 61 unmodified lines | ||
| 1458 | 1484 | av_log(s, AV_LOG_WARNING, "no video stream and no seg duration set\n"); |
| 1459 | 1485 | return AVERROR(EINVAL); |
| 1460 | 1486 | } |
| 1487 | + if (!c->has_video && c->frag_type == FRAG_TYPE_PFRAMES) | |
| 1488 | + av_log(s, AV_LOG_WARNING, "no video stream and P-frame fragmentation set\n"); | |
| 1461 | 1489 | |
| 1462 | 1490 | c->nr_of_streams_flushed = 0; |
| 1463 | 1491 | |
| @@ -1730,6 +1758,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream) · 266 unmodified lines | ||
| 1730 | 1758 | av_rescale_q(os->max_pts - os->start_pts, |
| 1731 | 1759 | st->time_base, AV_TIME_BASE_Q); |
| 1732 | 1760 | os->total_pkt_size = 0; |
| 1761 | + os->total_pkt_duration = 0; | |
| 1733 | 1762 | |
| 1734 | 1763 | if (!os->bit_rate) { |
| 1735 | 1764 | // calculate average bitrate of first segment |
| @@ -1831,6 +1860,9 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) · 95 unmodified lines | ||
| 1831 | 1860 | sizeof(c->availability_start_time)); |
| 1832 | 1861 | } |
| 1833 | 1862 | |
| 1863 | + if (!os->packets_written) | |
| 1864 | + os->availability_time_offset = 0; | |
| 1865 | + | |
| 1834 | 1866 | if (!os->availability_time_offset && |
| 1835 | 1867 | ((os->frag_type == FRAG_TYPE_DURATION && os->seg_duration != os->frag_duration) || |
| 1836 | 1868 | (os->frag_type == FRAG_TYPE_EVERY_FRAME && pkt->duration))) { |
| @@ -1895,11 +1927,43 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) · 58 unmodified lines | ||
| 1895 | 1927 | os->max_pts = pkt->pts + pkt->duration; |
| 1896 | 1928 | else |
| 1897 | 1929 | os->max_pts = FFMAX(os->max_pts, pkt->pts + pkt->duration); |
| 1898 | − os->packets_written++; | |
| 1899 | − os->total_pkt_size += pkt->size; | |
| 1930 | + | |
| 1931 | + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && | |
| 1932 | + os->frag_type == FRAG_TYPE_PFRAMES && | |
| 1933 | + os->packets_written) { | |
| 1934 | + uint8_t *data; | |
| 1935 | + int size; | |
| 1936 | + | |
| 1937 | + av_assert0(os->parser); | |
| 1938 | + av_parser_parse2(os->parser, os->parser_avctx, | |
| 1939 | + &data, &size, pkt->data, pkt->size, | |
| 1940 | + pkt->pts, pkt->dts, pkt->pos); | |
| 1941 | + | |
| 1942 | + if ((os->parser->pict_type == AV_PICTURE_TYPE_P && | |
| 1943 | + st->codecpar->video_delay && | |
| 1944 | + !(os->last_flags & AV_PKT_FLAG_KEY)) || | |
| 1945 | + pkt->flags & AV_PKT_FLAG_KEY) { | |
| 1946 | + ret = av_write_frame(os->ctx, NULL); | |
| 1947 | + if (ret < 0) | |
| 1948 | + return ret; | |
| 1949 | + | |
| 1950 | + if (!os->availability_time_offset) { | |
| 1951 | + int64_t frag_duration = av_rescale_q(os->total_pkt_duration, st->time_base, | |
| 1952 | + AV_TIME_BASE_Q); | |
| 1953 | + os->availability_time_offset = ((double) os->seg_duration - | |
| 1954 | + frag_duration) / AV_TIME_BASE; | |
| 1955 | + } | |
| 1956 | + } | |
| 1957 | + } | |
| 1958 | + | |
| 1900 | 1959 | if ((ret = ff_write_chained(os->ctx, 0, pkt, s, 0)) < 0) |
| 1901 | 1960 | return ret; |
| 1902 | 1961 | |
| 1962 | + os->packets_written++; | |
| 1963 | + os->total_pkt_size += pkt->size; | |
| 1964 | + os->total_pkt_duration += pkt->duration; | |
| 1965 | + os->last_flags = pkt->flags; | |
| 1966 | + | |
| 1903 | 1967 | if (!os->init_range_length) |
| 1904 | 1968 | flush_init_segment(s, os); |
| 1905 | 1969 | |
| @@ -2026,6 +2090,7 @@ static const AVOption options[] = { · 120 unmodified lines | ||
| 2026 | 2090 | { "none", "one fragment per segment", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_NONE }, 0, UINT_MAX, E, "frag_type"}, |
| 2027 | 2091 | { "every_frame", "fragment at every frame", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_EVERY_FRAME }, 0, UINT_MAX, E, "frag_type"}, |
| 2028 | 2092 | { "duration", "fragment at specific time intervals", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_DURATION }, 0, UINT_MAX, E, "frag_type"}, |
| 2093 | + { "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"}, | |
| 2029 | 2094 | { "remove_at_exit", "remove all segments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, |
| 2030 | 2095 | { "use_template", "Use SegmentTemplate instead of SegmentList", OFFSET(use_template), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E }, |
| 2031 | 2096 | { "use_timeline", "Use SegmentTimeline in SegmentTemplate", OFFSET(use_timeline), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E }, |