* commit 'c94e2e85cb6af8a570d8542a830556243bd32873': nut: Support experimental NUT 4 features Conflicts: doc/nut.texi libavformat/nut.h libavformat/nutdec.c libavformat/nutenc.c
Unified · Split
| @@ -706,6 +706,23 @@ Alternatively you can write the command as: · 705 unmodified lines | |||
| 706 | ffmpeg -benchmark -i INPUT -f null - | 706 | ffmpeg -benchmark -i INPUT -f null - |
| 707 | @end example | 707 | @end example |
| 708 | 708 | ||
| 709 | @section nut | ||
| 710 | |||
| 711 | @table @option | ||
| 712 | @item -syncpoints @var{flags} | ||
| 713 | Change the syncpoint usage in nut: | ||
| 714 | @table @option | ||
| 715 | @item @var{default} use the normal low-overhead seeking aids. | ||
| 716 | @item @var{none} do not use the syncpoints at all, reducing the overhead but making the stream non-seekable; | ||
| 717 | @item @var{timestamped} extend the syncpoint with a wallclock field. | ||
| 718 | @end table | ||
| 719 | The @var{none} and @var{timestamped} flags are experimental. | ||
| 720 | @end table | ||
| 721 | |||
| 722 | @example | ||
| 723 | ffmpeg -i INPUT -f_strict experimental -syncpoints none - | processor | ||
| 724 | @end example | ||
| 725 | |||
| 709 | @section ogg | 726 | @section ogg |
| 710 | 727 | ||
| 711 | Ogg container muxer. | 728 | Ogg container muxer. |
| @@ -21,6 +21,27 @@ The official nut specification is at svn://svn.mplayerhq.hu/nut · 20 unmodified lines | |||
| 21 | In case of any differences between this text and the official specification, | 21 | In case of any differences between this text and the official specification, |
| 22 | the official specification shall prevail. | 22 | the official specification shall prevail. |
| 23 | 23 | ||
| 24 | @chapter Modes | ||
| 25 | NUT has some variants signaled by using the flags field in its main header. | ||
| 26 | |||
| 27 | @multitable @columnfractions .4 .4 | ||
| 28 | @item BROADCAST @tab Extend the syncpoint to report the sender wallclock | ||
| 29 | @item PIPE @tab Omit completely the syncpoint | ||
| 30 | @end multitable | ||
| 31 | |||
| 32 | @section BROADCAST | ||
| 33 | |||
| 34 | The BROADCAST variant provides a secondary time reference to facilitate | ||
| 35 | detecting endpoint latency and network delays. | ||
| 36 | It assumes all the endpoint clocks are syncronized. | ||
| 37 | To be used in real-time scenarios. | ||
| 38 | |||
| 39 | @section PIPE | ||
| 40 | |||
| 41 | The PIPE variant assumes NUT is used as non-seekable intermediate container, | ||
| 42 | by not using syncpoint removes unneeded overhead and reduces the overall | ||
| 43 | memory usage. | ||
| 44 | |||
| 24 | @chapter Container-specific codec tags | 45 | @chapter Container-specific codec tags |
| 25 | 46 | ||
| 26 | @section Generic raw YUVA formats | 47 | @section Generic raw YUVA formats |
| @@ -36,7 +36,9 @@ · 35 unmodified lines | |||
| 36 | 36 | ||
| 37 | #define MAX_DISTANCE (1024*32-1) | 37 | #define MAX_DISTANCE (1024*32-1) |
| 38 | 38 | ||
| 39 | #define NUT_VERSION 3 | 39 | #define NUT_MAX_VERSION 4 |
| 40 | #define NUT_STABLE_VERSION 3 | ||
| 41 | #define NUT_MIN_VERSION 2 | ||
| 40 | 42 | ||
| 41 | typedef enum{ | 43 | typedef enum{ |
| 42 | FLAG_KEY = 1, ///<if set, frame is keyframe | 44 | FLAG_KEY = 1, ///<if set, frame is keyframe |
| @@ -87,6 +89,7 @@ typedef struct ChapterContext { · 44 unmodified lines | |||
| 87 | } ChapterContext; | 89 | } ChapterContext; |
| 88 | 90 | ||
| 89 | typedef struct NUTContext { | 91 | typedef struct NUTContext { |
| 92 | const AVClass *av_class; | ||
| 90 | AVFormatContext *avf; | 93 | AVFormatContext *avf; |
| 91 | // int written_packet_size; | 94 | // int written_packet_size; |
| 92 | // int64_t packet_start; | 95 | // int64_t packet_start; |
| @@ -105,7 +108,10 @@ typedef struct NUTContext { · 12 unmodified lines | |||
| 105 | int sp_count; | 108 | int sp_count; |
| 106 | int64_t max_pts; | 109 | int64_t max_pts; |
| 107 | AVRational *max_pts_tb; | 110 | AVRational *max_pts_tb; |
| 108 | int version; | 111 | #define NUT_BROADCAST 1 // use extended syncpoints |
| 112 | #define NUT_PIPE 2 // do not write syncpoints | ||
| 113 | int flags; | ||
| 114 | int version; // version currently in use | ||
| 109 | int minor_version; | 115 | int minor_version; |
| 110 | } NUTContext; | 116 | } NUTContext; |
| 111 | 117 | ||
| @@ -229,7 +229,7 @@ static int decode_main_header(NUTContext *nut) · 228 unmodified lines | |||
| 229 | end += avio_tell(bc); | 229 | end += avio_tell(bc); |
| 230 | 230 | ||
| 231 | tmp = ffio_read_varlen(bc); | 231 | tmp = ffio_read_varlen(bc); |
| 232 | if (tmp < 2 && tmp > 4) { | 232 | if (tmp < NUT_MIN_VERSION && tmp > NUT_MAX_VERSION) { |
| 233 | av_log(s, AV_LOG_ERROR, "Version %"PRId64" not supported.\n", | 233 | av_log(s, AV_LOG_ERROR, "Version %"PRId64" not supported.\n", |
| 234 | tmp); | 234 | tmp); |
| 235 | return AVERROR(ENOSYS); | 235 | return AVERROR(ENOSYS); |
| @@ -340,6 +340,11 @@ static int decode_main_header(NUTContext *nut) · 104 unmodified lines | |||
| 340 | av_assert0(nut->header_len[0] == 0); | 340 | av_assert0(nut->header_len[0] == 0); |
| 341 | } | 341 | } |
| 342 | 342 | ||
| 343 | // flags had been effectively introduced in version 4 | ||
| 344 | if (nut->version > NUT_STABLE_VERSION) { | ||
| 345 | nut->flags = ffio_read_varlen(bc); | ||
| 346 | } | ||
| 347 | |||
| 343 | if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { | 348 | if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { |
| 344 | av_log(s, AV_LOG_ERROR, "main header checksum mismatch\n"); | 349 | av_log(s, AV_LOG_ERROR, "main header checksum mismatch\n"); |
| 345 | return AVERROR_INVALIDDATA; | 350 | return AVERROR_INVALIDDATA; |
| @@ -578,6 +583,14 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) · 232 unmodified lines | |||
| 578 | ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count], | 583 | ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count], |
| 579 | tmp / nut->time_base_count); | 584 | tmp / nut->time_base_count); |
| 580 | 585 | ||
| 586 | if (nut->flags & NUT_BROADCAST) { | ||
| 587 | tmp = ffio_read_varlen(bc); | ||
| 588 | av_log(s, AV_LOG_VERBOSE, "Syncpoint wallclock %"PRId64"\n", | ||
| 589 | av_rescale_q(tmp / nut->time_base_count, | ||
| 590 | nut->time_base[tmp % nut->time_base_count], | ||
| 591 | AV_TIME_BASE_Q)); | ||
| 592 | } | ||
| 593 | |||
| 581 | if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { | 594 | if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { |
| 582 | av_log(s, AV_LOG_ERROR, "sync point checksum mismatch\n"); | 595 | av_log(s, AV_LOG_ERROR, "sync point checksum mismatch\n"); |
| 583 | return AVERROR_INVALIDDATA; | 596 | return AVERROR_INVALIDDATA; |
| @@ -898,7 +911,8 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, · 314 unmodified lines | |||
| 898 | int size, flags, size_mul, pts_delta, i, reserved_count; | 911 | int size, flags, size_mul, pts_delta, i, reserved_count; |
| 899 | uint64_t tmp; | 912 | uint64_t tmp; |
| 900 | 913 | ||
| 901 | if (avio_tell(bc) > nut->last_syncpoint_pos + nut->max_distance) { | 914 | if (!(nut->flags & NUT_PIPE) && |
| 915 | avio_tell(bc) > nut->last_syncpoint_pos + nut->max_distance) { | ||
| 902 | av_log(s, AV_LOG_ERROR, | 916 | av_log(s, AV_LOG_ERROR, |
| 903 | "Last frame must have been damaged %"PRId64" > %"PRId64" + %d\n", | 917 | "Last frame must have been damaged %"PRId64" > %"PRId64" + %d\n", |
| 904 | avio_tell(bc), nut->last_syncpoint_pos, nut->max_distance); | 918 | avio_tell(bc), nut->last_syncpoint_pos, nut->max_distance); |
| @@ -951,8 +965,9 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, · 46 unmodified lines | |||
| 951 | 965 | ||
| 952 | if (flags & FLAG_CHECKSUM) { | 966 | if (flags & FLAG_CHECKSUM) { |
| 953 | avio_rb32(bc); // FIXME check this | 967 | avio_rb32(bc); // FIXME check this |
| 954 | } else if (size > 2 * nut->max_distance || FFABS(stc->last_pts - *pts) > | 968 | } else if (!(nut->flags & NUT_PIPE) && |
| 955 | stc->max_pts_distance) { | 969 | size > 2 * nut->max_distance || |
| 970 | FFABS(stc->last_pts - *pts) > stc->max_pts_distance) { | ||
| 956 | av_log(s, AV_LOG_ERROR, "frame size > 2max_distance and no checksum\n"); | 971 | av_log(s, AV_LOG_ERROR, "frame size > 2max_distance and no checksum\n"); |
| 957 | return AVERROR_INVALIDDATA; | 972 | return AVERROR_INVALIDDATA; |
| 958 | } | 973 | } |
| @@ -1118,6 +1133,10 @@ static int read_seek(AVFormatContext *s, int stream_index, · 159 unmodified lines | |||
| 1118 | int64_t pos, pos2, ts; | 1133 | int64_t pos, pos2, ts; |
| 1119 | int i; | 1134 | int i; |
| 1120 | 1135 | ||
| 1136 | if (nut->flags & NUT_PIPE) { | ||
| 1137 | return AVERROR(ENOSYS); | ||
| 1138 | } | ||
| 1139 | |||
| 1121 | if (st->index_entries) { | 1140 | if (st->index_entries) { |
| 1122 | int index = av_index_search_timestamp(st, pts, flags); | 1141 | int index = av_index_search_timestamp(st, pts, flags); |
| 1123 | if (index < 0) | 1142 | if (index < 0) |
| @@ -26,6 +26,8 @@ · 25 unmodified lines | |||
| 26 | #include "libavutil/tree.h" | 26 | #include "libavutil/tree.h" |
| 27 | #include "libavutil/dict.h" | 27 | #include "libavutil/dict.h" |
| 28 | #include "libavutil/avassert.h" | 28 | #include "libavutil/avassert.h" |
| 29 | #include "libavutil/time.h" | ||
| 30 | #include "libavutil/opt.h" | ||
| 29 | #include "libavcodec/bytestream.h" | 31 | #include "libavcodec/bytestream.h" |
| 30 | #include "libavcodec/mpegaudiodata.h" | 32 | #include "libavcodec/mpegaudiodata.h" |
| 31 | #include "nut.h" | 33 | #include "nut.h" |
| @@ -338,7 +340,7 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc) · 306 unmodified lines | |||
| 338 | tmp_head_idx; | 340 | tmp_head_idx; |
| 339 | int64_t tmp_match; | 341 | int64_t tmp_match; |
| 340 | 342 | ||
| 341 | ff_put_v(bc, nut->version = NUT_VERSION); | 343 | ff_put_v(bc, nut->version); |
| 342 | if (nut->version > 3) | 344 | if (nut->version > 3) |
| 343 | ff_put_v(bc, nut->minor_version); | 345 | ff_put_v(bc, nut->minor_version); |
| 344 | ff_put_v(bc, nut->avf->nb_streams); | 346 | ff_put_v(bc, nut->avf->nb_streams); |
| @@ -407,6 +409,9 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc) · 62 unmodified lines | |||
| 407 | ff_put_v(bc, nut->header_len[i]); | 409 | ff_put_v(bc, nut->header_len[i]); |
| 408 | avio_write(bc, nut->header[i], nut->header_len[i]); | 410 | avio_write(bc, nut->header[i], nut->header_len[i]); |
| 409 | } | 411 | } |
| 412 | // flags had been effectively introduced in version 4 | ||
| 413 | if (nut->version > NUT_STABLE_VERSION) | ||
| 414 | ff_put_v(bc, nut->flags); | ||
| 410 | } | 415 | } |
| 411 | 416 | ||
| 412 | static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc, | 417 | static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc, |
| @@ -692,6 +697,16 @@ static int nut_write_header(AVFormatContext *s) · 279 unmodified lines | |||
| 692 | 697 | ||
| 693 | nut->avf = s; | 698 | nut->avf = s; |
| 694 | 699 | ||
| 700 | nut->version = NUT_STABLE_VERSION + !!nut->flags; | ||
| 701 | if (nut->flags && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { | ||
| 702 | av_log(s, AV_LOG_ERROR, | ||
| 703 | "The additional syncpoint modes require version %d, " | ||
| 704 | "that is currently not finalized, " | ||
| 705 | "please set -f_strict experimental in order to enable it.\n", | ||
| 706 | nut->version); | ||
| 707 | return AVERROR_EXPERIMENTAL; | ||
| 708 | } | ||
| 709 | |||
| 695 | nut->stream = av_calloc(s->nb_streams, sizeof(*nut->stream )); | 710 | nut->stream = av_calloc(s->nb_streams, sizeof(*nut->stream )); |
| 696 | nut->chapter = av_calloc(s->nb_chapters, sizeof(*nut->chapter)); | 711 | nut->chapter = av_calloc(s->nb_chapters, sizeof(*nut->chapter)); |
| 697 | nut->time_base= av_calloc(s->nb_streams + | 712 | nut->time_base= av_calloc(s->nb_streams + |
| @@ -974,7 +989,8 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) · 276 unmodified lines | |||
| 974 | 989 | ||
| 975 | //FIXME: Ensure store_sp is 1 in the first place. | 990 | //FIXME: Ensure store_sp is 1 in the first place. |
| 976 | 991 | ||
| 977 | if (store_sp) { | 992 | if (store_sp && |
| 993 | (!(nut->flags & NUT_PIPE) || nut->last_syncpoint_pos == INT_MIN)) { | ||
| 978 | Syncpoint *sp, dummy = { .pos = INT64_MAX }; | 994 | Syncpoint *sp, dummy = { .pos = INT64_MAX }; |
| 979 | 995 | ||
| 980 | ff_nut_reset_ts(nut, *nus->time_base, pkt->dts); | 996 | ff_nut_reset_ts(nut, *nus->time_base, pkt->dts); |
| @@ -1000,6 +1016,11 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) · 19 unmodified lines | |||
| 1000 | return ret; | 1016 | return ret; |
| 1001 | put_tt(nut, nus->time_base, dyn_bc, pkt->dts); | 1017 | put_tt(nut, nus->time_base, dyn_bc, pkt->dts); |
| 1002 | ff_put_v(dyn_bc, sp ? (nut->last_syncpoint_pos - sp->pos) >> 4 : 0); | 1018 | ff_put_v(dyn_bc, sp ? (nut->last_syncpoint_pos - sp->pos) >> 4 : 0); |
| 1019 | |||
| 1020 | if (nut->flags & NUT_BROADCAST) { | ||
| 1021 | put_tt(nut, nus->time_base, dyn_bc, | ||
| 1022 | av_rescale_q(av_gettime(), AV_TIME_BASE_Q, *nus->time_base)); | ||
| 1023 | } | ||
| 1003 | put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE); | 1024 | put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE); |
| 1004 | 1025 | ||
| 1005 | if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0) | 1026 | if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0) |
| @@ -1110,7 +1131,7 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) · 104 unmodified lines | |||
| 1110 | nus->last_pts = pkt->pts; | 1131 | nus->last_pts = pkt->pts; |
| 1111 | 1132 | ||
| 1112 | //FIXME just store one per syncpoint | 1133 | //FIXME just store one per syncpoint |
| 1113 | if (flags & FLAG_KEY) { | 1134 | if (flags & FLAG_KEY && !(nut->flags & NUT_PIPE)) { |
| 1114 | av_add_index_entry( | 1135 | av_add_index_entry( |
| 1115 | s->streams[pkt->stream_index], | 1136 | s->streams[pkt->stream_index], |
| 1116 | nut->last_syncpoint_pos, | 1137 | nut->last_syncpoint_pos, |
| @@ -1156,6 +1177,23 @@ static int nut_write_trailer(AVFormatContext *s) · 39 unmodified lines | |||
| 1156 | return 0; | 1177 | return 0; |
| 1157 | } | 1178 | } |
| 1158 | 1179 | ||
| 1180 | #define OFFSET(x) offsetof(NUTContext, x) | ||
| 1181 | #define E AV_OPT_FLAG_ENCODING_PARAM | ||
| 1182 | static const AVOption options[] = { | ||
| 1183 | { "syncpoints", "NUT syncpoint behaviour", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, E, "syncpoints" }, | ||
| 1184 | { "default", "", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, E, "syncpoints" }, | ||
| 1185 | { "none", "Disable syncpoints, low overhead and unseekable", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_PIPE}, INT_MIN, INT_MAX, E, "syncpoints" }, | ||
| 1186 | { "timestamped", "Extend syncpoints with a wallclock timestamp", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_BROADCAST}, INT_MIN, INT_MAX, E, "syncpoints" }, | ||
| 1187 | { NULL }, | ||
| 1188 | }; | ||
| 1189 | |||
| 1190 | static const AVClass class = { | ||
| 1191 | .class_name = "nutenc", | ||
| 1192 | .item_name = av_default_item_name, | ||
| 1193 | .option = options, | ||
| 1194 | .version = LIBAVUTIL_VERSION_INT, | ||
| 1195 | }; | ||
| 1196 | |||
| 1159 | AVOutputFormat ff_nut_muxer = { | 1197 | AVOutputFormat ff_nut_muxer = { |
| 1160 | .name = "nut", | 1198 | .name = "nut", |
| 1161 | .long_name = NULL_IF_CONFIG_SMALL("NUT"), | 1199 | .long_name = NULL_IF_CONFIG_SMALL("NUT"), |
| @@ -1170,4 +1208,5 @@ AVOutputFormat ff_nut_muxer = { · 8 unmodified lines | |||
| 1170 | .write_trailer = nut_write_trailer, | 1208 | .write_trailer = nut_write_trailer, |
| 1171 | .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS, | 1209 | .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS, |
| 1172 | .codec_tag = ff_nut_codec_tags, | 1210 | .codec_tag = ff_nut_codec_tags, |
| 1211 | .priv_class = &class, | ||
| 1173 | }; | 1212 | }; |