* 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 | 706 | ffmpeg -benchmark -i INPUT -f null - |
| 707 | 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 | 726 | @section ogg |
| 710 | 727 | |
| 711 | 728 | Ogg container muxer. |
| @@ -21,6 +21,27 @@ The official nut specification is at svn://svn.mplayerhq.hu/nut · 20 unmodified lines | ||
| 21 | 21 | In case of any differences between this text and the official specification, |
| 22 | 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 | 45 | @chapter Container-specific codec tags |
| 25 | 46 | |
| 26 | 47 | @section Generic raw YUVA formats |
| @@ -36,7 +36,9 @@ · 35 unmodified lines | ||
| 36 | 36 | |
| 37 | 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 | 43 | typedef enum{ |
| 42 | 44 | FLAG_KEY = 1, ///<if set, frame is keyframe |
| @@ -87,6 +89,7 @@ typedef struct ChapterContext { · 44 unmodified lines | ||
| 87 | 89 | } ChapterContext; |
| 88 | 90 | |
| 89 | 91 | typedef struct NUTContext { |
| 92 | + const AVClass *av_class; | |
| 90 | 93 | AVFormatContext *avf; |
| 91 | 94 | // int written_packet_size; |
| 92 | 95 | // int64_t packet_start; |
| @@ -105,7 +108,10 @@ typedef struct NUTContext { · 12 unmodified lines | ||
| 105 | 108 | int sp_count; |
| 106 | 109 | int64_t max_pts; |
| 107 | 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 | 115 | int minor_version; |
| 110 | 116 | } NUTContext; |
| 111 | 117 | |
| @@ -229,7 +229,7 @@ static int decode_main_header(NUTContext *nut) · 228 unmodified lines | ||
| 229 | 229 | end += avio_tell(bc); |
| 230 | 230 | |
| 231 | 231 | tmp = ffio_read_varlen(bc); |
| 232 | − if (tmp < 2 && tmp > 4) { | |
| 232 | + if (tmp < NUT_MIN_VERSION && tmp > NUT_MAX_VERSION) { | |
| 233 | 233 | av_log(s, AV_LOG_ERROR, "Version %"PRId64" not supported.\n", |
| 234 | 234 | tmp); |
| 235 | 235 | return AVERROR(ENOSYS); |
| @@ -340,6 +340,11 @@ static int decode_main_header(NUTContext *nut) · 104 unmodified lines | ||
| 340 | 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 | 348 | if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { |
| 344 | 349 | av_log(s, AV_LOG_ERROR, "main header checksum mismatch\n"); |
| 345 | 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 | 583 | ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count], |
| 579 | 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 | 594 | if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { |
| 582 | 595 | av_log(s, AV_LOG_ERROR, "sync point checksum mismatch\n"); |
| 583 | 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 | 911 | int size, flags, size_mul, pts_delta, i, reserved_count; |
| 899 | 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 | 916 | av_log(s, AV_LOG_ERROR, |
| 903 | 917 | "Last frame must have been damaged %"PRId64" > %"PRId64" + %d\n", |
| 904 | 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 | 966 | if (flags & FLAG_CHECKSUM) { |
| 953 | 967 | avio_rb32(bc); // FIXME check this |
| 954 | − } else if (size > 2 * nut->max_distance || FFABS(stc->last_pts - *pts) > | |
| 955 | − stc->max_pts_distance) { | |
| 968 | + } else if (!(nut->flags & NUT_PIPE) && | |
| 969 | + size > 2 * nut->max_distance || | |
| 970 | + FFABS(stc->last_pts - *pts) > stc->max_pts_distance) { | |
| 956 | 971 | av_log(s, AV_LOG_ERROR, "frame size > 2max_distance and no checksum\n"); |
| 957 | 972 | return AVERROR_INVALIDDATA; |
| 958 | 973 | } |
| @@ -1118,6 +1133,10 @@ static int read_seek(AVFormatContext *s, int stream_index, · 159 unmodified lines | ||
| 1118 | 1133 | int64_t pos, pos2, ts; |
| 1119 | 1134 | int i; |
| 1120 | 1135 | |
| 1136 | + if (nut->flags & NUT_PIPE) { | |
| 1137 | + return AVERROR(ENOSYS); | |
| 1138 | + } | |
| 1139 | + | |
| 1121 | 1140 | if (st->index_entries) { |
| 1122 | 1141 | int index = av_index_search_timestamp(st, pts, flags); |
| 1123 | 1142 | if (index < 0) |
| @@ -26,6 +26,8 @@ · 25 unmodified lines | ||
| 26 | 26 | #include "libavutil/tree.h" |
| 27 | 27 | #include "libavutil/dict.h" |
| 28 | 28 | #include "libavutil/avassert.h" |
| 29 | +#include "libavutil/time.h" | |
| 30 | +#include "libavutil/opt.h" | |
| 29 | 31 | #include "libavcodec/bytestream.h" |
| 30 | 32 | #include "libavcodec/mpegaudiodata.h" |
| 31 | 33 | #include "nut.h" |
| @@ -338,7 +340,7 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc) · 306 unmodified lines | ||
| 338 | 340 | tmp_head_idx; |
| 339 | 341 | int64_t tmp_match; |
| 340 | 342 | |
| 341 | − ff_put_v(bc, nut->version = NUT_VERSION); | |
| 343 | + ff_put_v(bc, nut->version); | |
| 342 | 344 | if (nut->version > 3) |
| 343 | 345 | ff_put_v(bc, nut->minor_version); |
| 344 | 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 | 409 | ff_put_v(bc, nut->header_len[i]); |
| 408 | 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 | 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 | 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 | 710 | nut->stream = av_calloc(s->nb_streams, sizeof(*nut->stream )); |
| 696 | 711 | nut->chapter = av_calloc(s->nb_chapters, sizeof(*nut->chapter)); |
| 697 | 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 | 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 | 994 | Syncpoint *sp, dummy = { .pos = INT64_MAX }; |
| 979 | 995 | |
| 980 | 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 | 1016 | return ret; |
| 1001 | 1017 | put_tt(nut, nus->time_base, dyn_bc, pkt->dts); |
| 1002 | 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 | 1024 | put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE); |
| 1004 | 1025 | |
| 1005 | 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 | 1131 | nus->last_pts = pkt->pts; |
| 1111 | 1132 | |
| 1112 | 1133 | //FIXME just store one per syncpoint |
| 1113 | − if (flags & FLAG_KEY) { | |
| 1134 | + if (flags & FLAG_KEY && !(nut->flags & NUT_PIPE)) { | |
| 1114 | 1135 | av_add_index_entry( |
| 1115 | 1136 | s->streams[pkt->stream_index], |
| 1116 | 1137 | nut->last_syncpoint_pos, |
| @@ -1156,6 +1177,23 @@ static int nut_write_trailer(AVFormatContext *s) · 39 unmodified lines | ||
| 1156 | 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 | 1197 | AVOutputFormat ff_nut_muxer = { |
| 1160 | 1198 | .name = "nut", |
| 1161 | 1199 | .long_name = NULL_IF_CONFIG_SMALL("NUT"), |
| @@ -1170,4 +1208,5 @@ AVOutputFormat ff_nut_muxer = { · 8 unmodified lines | ||
| 1170 | 1208 | .write_trailer = nut_write_trailer, |
| 1171 | 1209 | .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS, |
| 1172 | 1210 | .codec_tag = ff_nut_codec_tags, |
| 1211 | + .priv_class = &class, | |
| 1173 | 1212 | }; |