* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <stdint.h>
+
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/tree.h"
#include "libavutil/dict.h"
+#include "libavutil/time.h"
+#include "libavutil/opt.h"
#include "libavcodec/mpegaudiodata.h"
#include "nut.h"
#include "internal.h"
#include "avio_internal.h"
+#include "riff.h"
-static int find_expected_header(AVCodecContext *c, int size, int key_frame,
+static int find_expected_header(AVCodecParameters *p, int size, int key_frame,
uint8_t out[64])
{
- int sample_rate = c->sample_rate;
+ int sample_rate = p->sample_rate;
if (size > 4096)
return 0;
AV_WB24(out, 1);
- if (c->codec_id == AV_CODEC_ID_MPEG4) {
+ if (p->codec_id == AV_CODEC_ID_MPEG4) {
if (key_frame) {
return 3;
} else {
out[3] = 0xB6;
return 4;
}
- } else if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO ||
- c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
+ } else if (p->codec_id == AV_CODEC_ID_MPEG1VIDEO ||
+ p->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
return 3;
- } else if (c->codec_id == AV_CODEC_ID_H264) {
+ } else if (p->codec_id == AV_CODEC_ID_H264) {
return 3;
- } else if (c->codec_id == AV_CODEC_ID_MP3 ||
- c->codec_id == AV_CODEC_ID_MP2) {
+ } else if (p->codec_id == AV_CODEC_ID_MP3 ||
+ p->codec_id == AV_CODEC_ID_MP2) {
int lsf, mpeg25, sample_rate_index, bitrate_index, frame_size;
- int layer = c->codec_id == AV_CODEC_ID_MP3 ? 3 : 2;
+ int layer = p->codec_id == AV_CODEC_ID_MP3 ? 3 : 2;
unsigned int header = 0xFFF00000;
lsf = sample_rate < (24000 + 32000) / 2;
header |= (bitrate_index & 1) << 9;
return 2; //FIXME actually put the needed ones in build_elision_headers()
- return 3; //we guess that the private bit is not set
+ //return 3; //we guess that the private bit is not set
//FIXME the above assumptions should be checked, if these turn out false too often something should be done
}
return 0;
}
-static int find_header_idx(AVFormatContext *s, AVCodecContext *c, int size,
+static int find_header_idx(AVFormatContext *s, AVCodecParameters *p, int size,
int frame_type)
{
NUTContext *nut = s->priv_data;
uint8_t out[64];
int i;
- int len = find_expected_header(c, size, frame_type, out);
+ int len = find_expected_header(p, size, frame_type, out);
for (i = 1; i < nut->header_count; i++) {
if (len == nut->header_len[i] && !memcmp(out, nut->header[i], len)) {
for (stream_id = 0; stream_id < s->nb_streams; stream_id++) {
int start2 = start + (end - start) * stream_id / s->nb_streams;
int end2 = start + (end - start) * (stream_id + 1) / s->nb_streams;
- AVCodecContext *codec = s->streams[stream_id]->codec;
- int is_audio = codec->codec_type == AVMEDIA_TYPE_AUDIO;
+ AVCodecParameters *par = s->streams[stream_id]->codecpar;
+ const AVCodecDescriptor *desc = avcodec_descriptor_get(par->codec_id);
+ int is_audio = par->codec_type == AVMEDIA_TYPE_AUDIO;
int intra_only = /*codec->intra_only || */ is_audio;
int pred_count;
ft->stream_id = stream_id;
ft->size_mul = 1;
if (is_audio)
- ft->header_idx = find_header_idx(s, codec, -1, key_frame);
+ ft->header_idx = find_header_idx(s, par, -1, key_frame);
start2++;
}
}
key_frame = intra_only;
if (is_audio) {
- int frame_bytes = codec->frame_size * (int64_t)codec->bit_rate /
- (8 * codec->sample_rate);
+ int frame_bytes;
int pts;
+
+ if (par->block_align > 0) {
+ frame_bytes = par->block_align;
+ } else {
+ int frame_size = av_get_audio_frame_duration2(par, 0);
+ frame_bytes = frame_size * (int64_t)par->bit_rate / (8 * par->sample_rate);
+ }
+
for (pts = 0; pts < 2; pts++)
for (pred = 0; pred < 2; pred++) {
FrameCode *ft = &nut->frame_code[start2];
ft->size_mul = frame_bytes + 2;
ft->size_lsb = frame_bytes + pred;
ft->pts_delta = pts;
- ft->header_idx = find_header_idx(s, codec, frame_bytes + pred, key_frame);
+ ft->header_idx = find_header_idx(s, par, frame_bytes + pred, key_frame);
start2++;
}
} else {
start2++;
}
- if (codec->has_b_frames) {
+ if (desc && desc->props & AV_CODEC_PROP_REORDER) {
pred_count = 5;
pred_table[0] = -2;
pred_table[1] = -1;
pred_table[2] = 1;
pred_table[3] = 3;
pred_table[4] = 4;
- } else if (codec->codec_id == AV_CODEC_ID_VORBIS) {
+ } else if (par->codec_id == AV_CODEC_ID_VORBIS) {
pred_count = 3;
pred_table[0] = 2;
pred_table[1] = 9;
ft->size_lsb = index - start3;
ft->pts_delta = pred_table[pred];
if (is_audio)
- ft->header_idx = find_header_idx(s, codec, -1, key_frame);
+ ft->header_idx = find_header_idx(s, par, -1, key_frame);
}
}
}
ff_put_v(bc, 2 * FFABS(val) - (val > 0));
}
-#ifdef TRACE
-static inline void ff_put_v_trace(AVIOContext *bc, uint64_t v, const char *file,
- const char *func, int line)
-{
- av_log(NULL, AV_LOG_DEBUG, "ff_put_v %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line);
-
- ff_put_v(bc, v);
-}
-
-static inline void put_s_trace(AVIOContext *bc, int64_t v, const char *file,
- const char *func, int line)
-{
- av_log(NULL, AV_LOG_DEBUG, "put_s %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line);
-
- put_s(bc, v);
-}
-#define ff_put_v(bc, v) ff_put_v_trace(bc, v, __FILE__, __PRETTY_FUNCTION__, __LINE__)
-#define put_s(bc, v) put_s_trace(bc, v, __FILE__, __PRETTY_FUNCTION__, __LINE__)
-#endif
-
//FIXME remove calculate_checksum
static void put_packet(NUTContext *nut, AVIOContext *bc, AVIOContext *dyn_bc,
int calculate_checksum, uint64_t startcode)
tmp_head_idx;
int64_t tmp_match;
- ff_put_v(bc, 3); /* version */
+ ff_put_v(bc, nut->version);
ff_put_v(bc, nut->avf->nb_streams);
ff_put_v(bc, nut->max_distance);
ff_put_v(bc, nut->time_base_count);
ff_put_v(bc, 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,
AVStream *st, int i)
{
NUTContext *nut = avctx->priv_data;
- AVCodecContext *codec = st->codec;
- unsigned codec_tag = av_codec_get_tag(ff_nut_codec_tags, codec->codec_id);
+ AVCodecParameters *par = st->codecpar;
+ const AVCodecDescriptor *desc = avcodec_descriptor_get(par->codec_id);
+ unsigned codec_tag = av_codec_get_tag(ff_nut_codec_tags, par->codec_id);
ff_put_v(bc, i);
- switch (codec->codec_type) {
+ switch (par->codec_type) {
case AVMEDIA_TYPE_VIDEO:
ff_put_v(bc, 0);
break;
}
ff_put_v(bc, 4);
- if (!codec_tag || codec->codec_id == AV_CODEC_ID_RAWVIDEO)
- codec_tag = codec->codec_tag;
+ if (av_codec_get_id(ff_nut_codec_tags, par->codec_tag) == par->codec_id ||
+ !codec_tag || par->codec_id == AV_CODEC_ID_RAWVIDEO)
+ codec_tag = par->codec_tag;
if (codec_tag) {
avio_wl32(bc, codec_tag);
ff_put_v(bc, nut->stream[i].time_base - nut->time_base);
ff_put_v(bc, nut->stream[i].msb_pts_shift);
ff_put_v(bc, nut->stream[i].max_pts_distance);
- ff_put_v(bc, codec->has_b_frames);
+ ff_put_v(bc, (desc && desc->props & AV_CODEC_PROP_REORDER) ? 16 : 0);
avio_w8(bc, 0); /* flags: 0x1 - fixed_fps, 0x2 - index_present */
- ff_put_v(bc, codec->extradata_size);
- avio_write(bc, codec->extradata, codec->extradata_size);
+ ff_put_v(bc, par->extradata_size);
+ avio_write(bc, par->extradata, par->extradata_size);
- switch (codec->codec_type) {
+ switch (par->codec_type) {
case AVMEDIA_TYPE_AUDIO:
- ff_put_v(bc, codec->sample_rate);
+ ff_put_v(bc, par->sample_rate);
ff_put_v(bc, 1);
- ff_put_v(bc, codec->channels);
+ ff_put_v(bc, par->channels);
break;
case AVMEDIA_TYPE_VIDEO:
- ff_put_v(bc, codec->width);
- ff_put_v(bc, codec->height);
+ ff_put_v(bc, par->width);
+ ff_put_v(bc, par->height);
if (st->sample_aspect_ratio.num <= 0 ||
st->sample_aspect_ratio.den <= 0) {
return ret;
if (ret > 0)
put_packet(nut, bc, dyn_bc, 1, INFO_STARTCODE);
- else {
- uint8_t *buf;
- avio_close_dyn_buf(dyn_bc, &buf);
- av_free(buf);
- }
+ else
+ ffio_free_dyn_buf(&dyn_bc);
}
for (i = 0; i < nut->avf->nb_chapters; i++) {
return ret;
ret = write_chapter(nut, dyn_bc, i);
if (ret < 0) {
- uint8_t *buf;
- avio_close_dyn_buf(dyn_bc, &buf);
- av_freep(&buf);
+ ffio_free_dyn_buf(&dyn_bc);
return ret;
}
put_packet(nut, bc, dyn_bc, 1, INFO_STARTCODE);
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_mallocz(sizeof(StreamContext) * s->nb_streams);
if (s->nb_chapters)
nut->chapter = av_mallocz(sizeof(ChapterContext) * s->nb_chapters);
AVStream *st = s->streams[i];
int ssize;
AVRational time_base;
- ff_parse_specific_params(st->codec, &time_base.den, &ssize,
+ ff_parse_specific_params(st, &time_base.den, &ssize,
&time_base.num);
avpriv_set_pts_info(st, 64, time_base.num, time_base.den);
//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 };
ff_nut_reset_ts(nut, *nus->time_base, pkt->dts);
}
if (dummy.pos == INT64_MAX)
dummy.pos = 0;
- sp = av_tree_find(nut->syncpoints, &dummy, (void *)ff_nut_sp_pos_cmp,
+ sp = av_tree_find(nut->syncpoints, &dummy,
+ (int (*)(void *, const void *)) ff_nut_sp_pos_cmp,
NULL);
nut->last_syncpoint_pos = avio_tell(bc);
return ret;
put_tt(nut, nus->time_base, dyn_bc, pkt->dts);
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);
- ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts);
+ if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0)
+ return ret;
}
assert(nus->last_pts != AV_NOPTS_VALUE);
frame_code = i;
}
}
- assert(frame_code != -1);
+
+ if (frame_code < 0)
+ return AVERROR_BUG;
+
fc = &nut->frame_code[frame_code];
flags = fc->flags;
needed_flags = get_needed_flags(nut, nus, fc, pkt);
nus->last_pts = pkt->pts;
//FIXME just store one per syncpoint
- if (flags & FLAG_KEY)
+ if (flags & FLAG_KEY && !(nut->flags & NUT_PIPE))
av_add_index_entry(
s->streams[pkt->stream_index],
nut->last_syncpoint_pos,
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 = {
.name = "nut",
.long_name = NULL_IF_CONFIG_SMALL("NUT"),
.write_trailer = nut_write_trailer,
.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS,
.codec_tag = ff_nut_codec_tags,
+ .priv_class = &class,
};