Merge commit 'c94e2e85cb6af8a570d8542a830556243bd32873'

* commit 'c94e2e85cb6af8a570d8542a830556243bd32873':
  nut: Support experimental NUT 4 features

Conflicts:
	doc/nut.texi
	libavformat/nut.h
	libavformat/nutdec.c
	libavformat/nutenc.c

Merged-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
Michael Niedermayer 2014-05-29 01:09:51 +02:00
commit a8499cbbe8
5 changed files with 111 additions and 9 deletions

View File

@ -706,6 +706,23 @@ Alternatively you can write the command as:
ffmpeg -benchmark -i INPUT -f null - ffmpeg -benchmark -i INPUT -f null -
@end example @end example
@section nut
@table @option
@item -syncpoints @var{flags}
Change the syncpoint usage in nut:
@table @option
@item @var{default} use the normal low-overhead seeking aids.
@item @var{none} do not use the syncpoints at all, reducing the overhead but making the stream non-seekable;
@item @var{timestamped} extend the syncpoint with a wallclock field.
@end table
The @var{none} and @var{timestamped} flags are experimental.
@end table
@example
ffmpeg -i INPUT -f_strict experimental -syncpoints none - | processor
@end example
@section ogg @section ogg
Ogg container muxer. Ogg container muxer.

View File

@ -21,6 +21,27 @@ The official nut specification is at svn://svn.mplayerhq.hu/nut
In case of any differences between this text and the official specification, In case of any differences between this text and the official specification,
the official specification shall prevail. the official specification shall prevail.
@chapter Modes
NUT has some variants signaled by using the flags field in its main header.
@multitable @columnfractions .4 .4
@item BROADCAST @tab Extend the syncpoint to report the sender wallclock
@item PIPE @tab Omit completely the syncpoint
@end multitable
@section BROADCAST
The BROADCAST variant provides a secondary time reference to facilitate
detecting endpoint latency and network delays.
It assumes all the endpoint clocks are syncronized.
To be used in real-time scenarios.
@section PIPE
The PIPE variant assumes NUT is used as non-seekable intermediate container,
by not using syncpoint removes unneeded overhead and reduces the overall
memory usage.
@chapter Container-specific codec tags @chapter Container-specific codec tags
@section Generic raw YUVA formats @section Generic raw YUVA formats

View File

@ -36,7 +36,9 @@
#define MAX_DISTANCE (1024*32-1) #define MAX_DISTANCE (1024*32-1)
#define NUT_VERSION 3 #define NUT_MAX_VERSION 4
#define NUT_STABLE_VERSION 3
#define NUT_MIN_VERSION 2
typedef enum{ typedef enum{
FLAG_KEY = 1, ///<if set, frame is keyframe FLAG_KEY = 1, ///<if set, frame is keyframe
@ -87,6 +89,7 @@ typedef struct ChapterContext {
} ChapterContext; } ChapterContext;
typedef struct NUTContext { typedef struct NUTContext {
const AVClass *av_class;
AVFormatContext *avf; AVFormatContext *avf;
// int written_packet_size; // int written_packet_size;
// int64_t packet_start; // int64_t packet_start;
@ -105,7 +108,10 @@ typedef struct NUTContext {
int sp_count; int sp_count;
int64_t max_pts; int64_t max_pts;
AVRational *max_pts_tb; AVRational *max_pts_tb;
int version; #define NUT_BROADCAST 1 // use extended syncpoints
#define NUT_PIPE 2 // do not write syncpoints
int flags;
int version; // version currently in use
int minor_version; int minor_version;
} NUTContext; } NUTContext;

View File

@ -229,7 +229,7 @@ static int decode_main_header(NUTContext *nut)
end += avio_tell(bc); end += avio_tell(bc);
tmp = ffio_read_varlen(bc); tmp = ffio_read_varlen(bc);
if (tmp < 2 && tmp > 4) { if (tmp < NUT_MIN_VERSION && tmp > NUT_MAX_VERSION) {
av_log(s, AV_LOG_ERROR, "Version %"PRId64" not supported.\n", av_log(s, AV_LOG_ERROR, "Version %"PRId64" not supported.\n",
tmp); tmp);
return AVERROR(ENOSYS); return AVERROR(ENOSYS);
@ -340,6 +340,11 @@ static int decode_main_header(NUTContext *nut)
av_assert0(nut->header_len[0] == 0); av_assert0(nut->header_len[0] == 0);
} }
// flags had been effectively introduced in version 4
if (nut->version > NUT_STABLE_VERSION) {
nut->flags = ffio_read_varlen(bc);
}
if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { if (skip_reserved(bc, end) || ffio_get_checksum(bc)) {
av_log(s, AV_LOG_ERROR, "main header checksum mismatch\n"); av_log(s, AV_LOG_ERROR, "main header checksum mismatch\n");
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
@ -578,6 +583,14 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr)
ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count], ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count],
tmp / nut->time_base_count); tmp / nut->time_base_count);
if (nut->flags & NUT_BROADCAST) {
tmp = ffio_read_varlen(bc);
av_log(s, AV_LOG_VERBOSE, "Syncpoint wallclock %"PRId64"\n",
av_rescale_q(tmp / nut->time_base_count,
nut->time_base[tmp % nut->time_base_count],
AV_TIME_BASE_Q));
}
if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { if (skip_reserved(bc, end) || ffio_get_checksum(bc)) {
av_log(s, AV_LOG_ERROR, "sync point checksum mismatch\n"); av_log(s, AV_LOG_ERROR, "sync point checksum mismatch\n");
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
@ -898,7 +911,8 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id,
int size, flags, size_mul, pts_delta, i, reserved_count; int size, flags, size_mul, pts_delta, i, reserved_count;
uint64_t tmp; uint64_t tmp;
if (avio_tell(bc) > nut->last_syncpoint_pos + nut->max_distance) { if (!(nut->flags & NUT_PIPE) &&
avio_tell(bc) > nut->last_syncpoint_pos + nut->max_distance) {
av_log(s, AV_LOG_ERROR, av_log(s, AV_LOG_ERROR,
"Last frame must have been damaged %"PRId64" > %"PRId64" + %d\n", "Last frame must have been damaged %"PRId64" > %"PRId64" + %d\n",
avio_tell(bc), nut->last_syncpoint_pos, nut->max_distance); 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,
if (flags & FLAG_CHECKSUM) { if (flags & FLAG_CHECKSUM) {
avio_rb32(bc); // FIXME check this avio_rb32(bc); // FIXME check this
} else if (size > 2 * nut->max_distance || FFABS(stc->last_pts - *pts) > } else if (!(nut->flags & NUT_PIPE) &&
stc->max_pts_distance) { size > 2 * nut->max_distance ||
FFABS(stc->last_pts - *pts) > stc->max_pts_distance) {
av_log(s, AV_LOG_ERROR, "frame size > 2max_distance and no checksum\n"); av_log(s, AV_LOG_ERROR, "frame size > 2max_distance and no checksum\n");
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
} }
@ -1118,6 +1133,10 @@ static int read_seek(AVFormatContext *s, int stream_index,
int64_t pos, pos2, ts; int64_t pos, pos2, ts;
int i; int i;
if (nut->flags & NUT_PIPE) {
return AVERROR(ENOSYS);
}
if (st->index_entries) { if (st->index_entries) {
int index = av_index_search_timestamp(st, pts, flags); int index = av_index_search_timestamp(st, pts, flags);
if (index < 0) if (index < 0)

View File

@ -26,6 +26,8 @@
#include "libavutil/tree.h" #include "libavutil/tree.h"
#include "libavutil/dict.h" #include "libavutil/dict.h"
#include "libavutil/avassert.h" #include "libavutil/avassert.h"
#include "libavutil/time.h"
#include "libavutil/opt.h"
#include "libavcodec/bytestream.h" #include "libavcodec/bytestream.h"
#include "libavcodec/mpegaudiodata.h" #include "libavcodec/mpegaudiodata.h"
#include "nut.h" #include "nut.h"
@ -338,7 +340,7 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc)
tmp_head_idx; tmp_head_idx;
int64_t tmp_match; int64_t tmp_match;
ff_put_v(bc, nut->version = NUT_VERSION); ff_put_v(bc, nut->version);
if (nut->version > 3) if (nut->version > 3)
ff_put_v(bc, nut->minor_version); ff_put_v(bc, nut->minor_version);
ff_put_v(bc, nut->avf->nb_streams); ff_put_v(bc, nut->avf->nb_streams);
@ -407,6 +409,9 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc)
ff_put_v(bc, nut->header_len[i]); ff_put_v(bc, nut->header_len[i]);
avio_write(bc, nut->header[i], nut->header_len[i]); avio_write(bc, nut->header[i], nut->header_len[i]);
} }
// flags had been effectively introduced in version 4
if (nut->version > NUT_STABLE_VERSION)
ff_put_v(bc, nut->flags);
} }
static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc, static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc,
@ -692,6 +697,16 @@ static int nut_write_header(AVFormatContext *s)
nut->avf = s; nut->avf = s;
nut->version = NUT_STABLE_VERSION + !!nut->flags;
if (nut->flags && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
av_log(s, AV_LOG_ERROR,
"The additional syncpoint modes require version %d, "
"that is currently not finalized, "
"please set -f_strict experimental in order to enable it.\n",
nut->version);
return AVERROR_EXPERIMENTAL;
}
nut->stream = av_calloc(s->nb_streams, sizeof(*nut->stream )); nut->stream = av_calloc(s->nb_streams, sizeof(*nut->stream ));
nut->chapter = av_calloc(s->nb_chapters, sizeof(*nut->chapter)); nut->chapter = av_calloc(s->nb_chapters, sizeof(*nut->chapter));
nut->time_base= av_calloc(s->nb_streams + nut->time_base= av_calloc(s->nb_streams +
@ -974,7 +989,8 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
//FIXME: Ensure store_sp is 1 in the first place. //FIXME: Ensure store_sp is 1 in the first place.
if (store_sp) { if (store_sp &&
(!(nut->flags & NUT_PIPE) || nut->last_syncpoint_pos == INT_MIN)) {
Syncpoint *sp, dummy = { .pos = INT64_MAX }; Syncpoint *sp, dummy = { .pos = INT64_MAX };
ff_nut_reset_ts(nut, *nus->time_base, pkt->dts); ff_nut_reset_ts(nut, *nus->time_base, pkt->dts);
@ -1000,6 +1016,11 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
return ret; return ret;
put_tt(nut, nus->time_base, dyn_bc, pkt->dts); put_tt(nut, nus->time_base, dyn_bc, pkt->dts);
ff_put_v(dyn_bc, sp ? (nut->last_syncpoint_pos - sp->pos) >> 4 : 0); ff_put_v(dyn_bc, sp ? (nut->last_syncpoint_pos - sp->pos) >> 4 : 0);
if (nut->flags & NUT_BROADCAST) {
put_tt(nut, nus->time_base, dyn_bc,
av_rescale_q(av_gettime(), AV_TIME_BASE_Q, *nus->time_base));
}
put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE); put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE);
if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0) 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)
nus->last_pts = pkt->pts; nus->last_pts = pkt->pts;
//FIXME just store one per syncpoint //FIXME just store one per syncpoint
if (flags & FLAG_KEY) { if (flags & FLAG_KEY && !(nut->flags & NUT_PIPE)) {
av_add_index_entry( av_add_index_entry(
s->streams[pkt->stream_index], s->streams[pkt->stream_index],
nut->last_syncpoint_pos, nut->last_syncpoint_pos,
@ -1156,6 +1177,23 @@ static int nut_write_trailer(AVFormatContext *s)
return 0; return 0;
} }
#define OFFSET(x) offsetof(NUTContext, x)
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
{ "syncpoints", "NUT syncpoint behaviour", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, E, "syncpoints" },
{ "default", "", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, E, "syncpoints" },
{ "none", "Disable syncpoints, low overhead and unseekable", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_PIPE}, INT_MIN, INT_MAX, E, "syncpoints" },
{ "timestamped", "Extend syncpoints with a wallclock timestamp", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_BROADCAST}, INT_MIN, INT_MAX, E, "syncpoints" },
{ NULL },
};
static const AVClass class = {
.class_name = "nutenc",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
AVOutputFormat ff_nut_muxer = { AVOutputFormat ff_nut_muxer = {
.name = "nut", .name = "nut",
.long_name = NULL_IF_CONFIG_SMALL("NUT"), .long_name = NULL_IF_CONFIG_SMALL("NUT"),
@ -1170,4 +1208,5 @@ AVOutputFormat ff_nut_muxer = {
.write_trailer = nut_write_trailer, .write_trailer = nut_write_trailer,
.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS, .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS,
.codec_tag = ff_nut_codec_tags, .codec_tag = ff_nut_codec_tags,
.priv_class = &class,
}; };