#include "libavutil/tree.h"
#include "avio_internal.h"
#include "nut.h"
+#include "riff.h"
#undef NDEBUG
#include <assert.h>
return 0;
}
-#define GET_V(dst, check) \
- tmp = ffio_read_varlen(bc); \
- if (!(check)) { \
- av_log(s, AV_LOG_ERROR, "Error " #dst " is (%"PRId64")\n", tmp); \
- return -1; \
- } \
- dst = tmp;
+#define GET_V(dst, check) \
+ do { \
+ tmp = ffio_read_varlen(bc); \
+ if (!(check)) { \
+ av_log(s, AV_LOG_ERROR, "Error " #dst " is (%"PRId64")\n", tmp); \
+ return AVERROR_INVALIDDATA; \
+ } \
+ dst = tmp; \
+ } while (0)
static int skip_reserved(AVIOContext *bc, int64_t pos)
{
pos -= avio_tell(bc);
if (pos < 0) {
avio_seek(bc, pos, SEEK_CUR);
- return -1;
+ return AVERROR_INVALIDDATA;
} else {
while (pos--)
avio_r8(bc);
end = get_packetheader(nut, bc, 1, MAIN_STARTCODE);
end += avio_tell(bc);
- GET_V(tmp, tmp >= 2 && tmp <= 3)
- GET_V(stream_count, tmp > 0 && tmp <= NUT_MAX_STREAMS)
+ nut->version = ffio_read_varlen(bc);
+ if (nut->version < NUT_MIN_VERSION &&
+ nut->version > NUT_MAX_VERSION) {
+ av_log(s, AV_LOG_ERROR, "Version %d not supported.\n",
+ nut->version);
+ return AVERROR(ENOSYS);
+ }
+
+ GET_V(stream_count, tmp > 0 && tmp <= NUT_MAX_STREAMS);
nut->max_distance = ffio_read_varlen(bc);
if (nut->max_distance > 65536) {
nut->max_distance = 65536;
}
- GET_V(nut->time_base_count, tmp > 0 && tmp < INT_MAX / sizeof(AVRational))
+ GET_V(nut->time_base_count, tmp > 0 && tmp < INT_MAX / sizeof(AVRational));
nut->time_base = av_malloc(nut->time_base_count * sizeof(AVRational));
+ if (!nut->time_base)
+ return AVERROR(ENOMEM);
for (i = 0; i < nut->time_base_count; i++) {
- GET_V(nut->time_base[i].num, tmp > 0 && tmp < (1ULL << 31))
- GET_V(nut->time_base[i].den, tmp > 0 && tmp < (1ULL << 31))
+ GET_V(nut->time_base[i].num, tmp > 0 && tmp < (1ULL << 31));
+ GET_V(nut->time_base[i].den, tmp > 0 && tmp < (1ULL << 31));
if (av_gcd(nut->time_base[i].num, nut->time_base[i].den) != 1) {
av_log(s, AV_LOG_ERROR, "time base invalid\n");
return AVERROR_INVALIDDATA;
if (end > avio_tell(bc) + 4) {
int rem = 1024;
- GET_V(nut->header_count, tmp < 128U)
+ GET_V(nut->header_count, tmp < 128U);
nut->header_count++;
for (i = 1; i < nut->header_count; i++) {
uint8_t *hdr;
assert(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)) {
av_log(s, AV_LOG_ERROR, "main header checksum mismatch\n");
return AVERROR_INVALIDDATA;
}
nut->stream = av_mallocz(sizeof(StreamContext) * stream_count);
+ if (!nut->stream)
+ return AVERROR(ENOMEM);
for (i = 0; i < stream_count; i++)
avformat_new_stream(s, NULL);
case 0:
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) {
- ff_codec_bmp_tags,
ff_nut_video_tags,
+ ff_codec_bmp_tags,
0
},
tmp);
break;
case 1:
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
- st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, tmp);
+ st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) {
+ ff_nut_audio_tags,
+ ff_codec_wav_tags,
+ 0
+ },
+ tmp);
break;
case 2:
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
break;
case 3:
st->codec->codec_type = AVMEDIA_TYPE_DATA;
+ st->codec->codec_id = ff_codec_get_id(ff_nut_data_tags, tmp);
break;
default:
av_log(s, AV_LOG_ERROR, "unknown stream class (%d)\n", class);
- return -1;
+ return AVERROR(ENOSYS);
}
if (class < 3 && st->codec->codec_id == AV_CODEC_ID_NONE)
av_log(s, AV_LOG_ERROR,
GET_V(stc->msb_pts_shift, tmp < 16);
stc->max_pts_distance = ffio_read_varlen(bc);
GET_V(stc->decode_delay, tmp < 1000); // sanity limit, raise this if Moore's law is true
- st->codec->has_b_frames = stc->decode_delay;
ffio_read_varlen(bc); // stream flags
GET_V(st->codec->extradata_size, tmp < (1 << 30));
if (st->codec->extradata_size) {
st->codec->extradata = av_mallocz(st->codec->extradata_size +
- FF_INPUT_BUFFER_PADDING_SIZE);
+ AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!st->codec->extradata)
+ return AVERROR(ENOMEM);
avio_read(bc, st->codec->extradata, st->codec->extradata_size);
}
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
- GET_V(st->codec->width, tmp > 0)
- GET_V(st->codec->height, tmp > 0)
+ GET_V(st->codec->width, tmp > 0);
+ GET_V(st->codec->height, tmp > 0);
st->sample_aspect_ratio.num = ffio_read_varlen(bc);
st->sample_aspect_ratio.den = ffio_read_varlen(bc);
if ((!st->sample_aspect_ratio.num) != (!st->sample_aspect_ratio.den)) {
av_log(s, AV_LOG_ERROR, "invalid aspect ratio %d/%d\n",
st->sample_aspect_ratio.num, st->sample_aspect_ratio.den);
- return -1;
+ return AVERROR_INVALIDDATA;
}
ffio_read_varlen(bc); /* csp type */
} else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
- GET_V(st->codec->sample_rate, tmp > 0)
+ GET_V(st->codec->sample_rate, tmp > 0);
ffio_read_varlen(bc); // samplerate_den
- GET_V(st->codec->channels, tmp > 0)
+ GET_V(st->codec->channels, tmp > 0);
}
if (skip_reserved(bc, end) || ffio_get_checksum(bc)) {
av_log(s, AV_LOG_ERROR,
"stream header %d checksum mismatch\n", stream_id);
- return -1;
+ return AVERROR_INVALIDDATA;
}
stc->time_base = &nut->time_base[stc->time_base_id];
avpriv_set_pts_info(s->streams[stream_id], 63, stc->time_base->num,
int64_t value, end;
char name[256], str_value[1024], type_str[256];
const char *type;
+ int *event_flags = NULL;
AVChapter *chapter = NULL;
AVStream *st = NULL;
AVDictionary **metadata = NULL;
+ int metadata_flag = 0;
end = get_packetheader(nut, bc, 1, INFO_STARTCODE);
end += avio_tell(bc);
- GET_V(stream_id_plus1, tmp <= s->nb_streams)
+ GET_V(stream_id_plus1, tmp <= s->nb_streams);
chapter_id = get_s(bc);
chapter_start = ffio_read_varlen(bc);
chapter_len = ffio_read_varlen(bc);
nut->time_base[chapter_start %
nut->time_base_count],
start, start + chapter_len, NULL);
+ if (!chapter) {
+ av_log(s, AV_LOG_ERROR, "Could not create chapter.\n");
+ return AVERROR(ENOMEM);
+ }
metadata = &chapter->metadata;
} else if (stream_id_plus1) {
st = s->streams[stream_id_plus1 - 1];
metadata = &st->metadata;
- } else
+ event_flags = &st->event_flags;
+ metadata_flag = AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
+ } else {
metadata = &s->metadata;
+ event_flags = &s->event_flags;
+ metadata_flag = AVFMT_EVENT_FLAG_METADATA_UPDATED;
+ }
for (i = 0; i < count; i++) {
get_str(bc, name, sizeof(name));
continue;
}
if (metadata && av_strcasecmp(name, "Uses") &&
- av_strcasecmp(name, "Depends") && av_strcasecmp(name, "Replaces"))
+ av_strcasecmp(name, "Depends") && av_strcasecmp(name, "Replaces")) {
+ if (event_flags)
+ *event_flags |= metadata_flag;
av_dict_set(metadata, name, str_value, 0);
+ }
}
}
if (skip_reserved(bc, end) || ffio_get_checksum(bc)) {
av_log(s, AV_LOG_ERROR, "info header checksum mismatch\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
return 0;
}
AVFormatContext *s = nut->avf;
AVIOContext *bc = s->pb;
int64_t end, tmp;
+ int ret;
nut->last_syncpoint_pos = avio_tell(bc) - 8;
ff_nut_reset_ts(nut, nut->time_base[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)) {
av_log(s, AV_LOG_ERROR, "sync point checksum mismatch\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
*ts = tmp / s->nb_streams *
av_q2d(nut->time_base[tmp % s->nb_streams]) * AV_TIME_BASE;
- ff_nut_add_sp(nut, nut->last_syncpoint_pos, *back_ptr, *ts);
+
+ if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, *back_ptr, *ts)) < 0)
+ return ret;
return 0;
}
int64_t filesize = avio_size(bc);
int64_t *syncpoints;
int8_t *has_keyframe;
- int ret = -1;
+ int ret = AVERROR_INVALIDDATA;
avio_seek(bc, filesize - 12, SEEK_SET);
avio_seek(bc, filesize - avio_rb64(bc), SEEK_SET);
if (avio_rb64(bc) != INDEX_STARTCODE) {
av_log(s, AV_LOG_ERROR, "no index at the end\n");
- return -1;
+ return ret;
}
end = get_packetheader(nut, bc, 1, INDEX_STARTCODE);
end += avio_tell(bc);
ffio_read_varlen(bc); // max_pts
- GET_V(syncpoint_count, tmp < INT_MAX / 8 && tmp > 0)
+ GET_V(syncpoint_count, tmp < INT_MAX / 8 && tmp > 0);
syncpoints = av_malloc(sizeof(int64_t) * syncpoint_count);
has_keyframe = av_malloc(sizeof(int8_t) * (syncpoint_count + 1));
+ if (!syncpoints || !has_keyframe) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
for (i = 0; i < syncpoint_count; i++) {
syncpoints[i] = ffio_read_varlen(bc);
if (syncpoints[i] <= 0)
return ret;
}
+static int nut_read_close(AVFormatContext *s)
+{
+ NUTContext *nut = s->priv_data;
+ int i;
+
+ av_freep(&nut->time_base);
+ av_freep(&nut->stream);
+ ff_nut_free_sp(nut);
+ for (i = 1; i < nut->header_count; i++)
+ av_freep(&nut->header[i]);
+
+ return 0;
+}
+
static int nut_read_header(AVFormatContext *s)
{
NUTContext *nut = s->priv_data;
pos = find_startcode(bc, MAIN_STARTCODE, pos) + 1;
if (pos < 0 + 1) {
av_log(s, AV_LOG_ERROR, "No main startcode found.\n");
- return AVERROR_INVALIDDATA;
+ goto fail;
}
} while (decode_main_header(nut) < 0);
pos = find_startcode(bc, STREAM_STARTCODE, pos) + 1;
if (pos < 0 + 1) {
av_log(s, AV_LOG_ERROR, "Not all stream headers found.\n");
- return AVERROR_INVALIDDATA;
+ goto fail;
}
if (decode_stream_header(nut) >= 0)
initialized_stream_count++;
if (startcode == 0) {
av_log(s, AV_LOG_ERROR, "EOF before video frames\n");
- return AVERROR_INVALIDDATA;
+ goto fail;
} else if (startcode == SYNCPOINT_STARTCODE) {
nut->next_startcode = startcode;
break;
decode_info_header(nut);
}
- s->data_offset = pos - 8;
+ s->internal->data_offset = pos - 8;
if (bc->seekable) {
int64_t orig_pos = avio_tell(bc);
ff_metadata_conv_ctx(s, NULL, ff_nut_metadata_conv);
return 0;
+
+fail:
+ nut_read_close(s);
+
+ return AVERROR_INVALIDDATA;
}
static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id,
int size, flags, size_mul, pts_delta, i, reserved_count;
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,
"Last frame must have been damaged %"PRId64" > %"PRId64" + %d\n",
avio_tell(bc), nut->last_syncpoint_pos, nut->max_distance);
if (flags & FLAG_CODED)
flags ^= ffio_read_varlen(bc);
if (flags & FLAG_STREAM_ID) {
- GET_V(*stream_id, tmp < s->nb_streams)
+ GET_V(*stream_id, tmp < s->nb_streams);
}
stc = &nut->stream[*stream_id];
if (flags & FLAG_CODED_PTS) {
if (flags & FLAG_CHECKSUM) {
avio_rb32(bc); // FIXME check this
- } else if (size > 2 * nut->max_distance || FFABS(stc->last_pts - *pts) >
- stc->max_pts_distance) {
+ } else if (!(nut->flags & NUT_PIPE) &&
+ 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");
return AVERROR_INVALIDDATA;
}
{
AVFormatContext *s = nut->avf;
AVIOContext *bc = s->pb;
- int size, stream_id, discard;
+ int size, stream_id, discard, ret;
int64_t pts, last_IP_pts;
StreamContext *stc;
uint8_t header_idx;
return 1;
}
- av_new_packet(pkt, size + nut->header_len[header_idx]);
+ ret = av_new_packet(pkt, size + nut->header_len[header_idx]);
+ if (ret < 0)
+ return ret;
memcpy(pkt->data, nut->header[header_idx], nut->header_len[header_idx]);
pkt->pos = avio_tell(bc); // FIXME
avio_read(bc, pkt->data + nut->header_len[header_idx], size);
} else {
frame_code = avio_r8(bc);
if (bc->eof_reached)
- return -1;
+ return AVERROR_EOF;
if (frame_code == 'N') {
tmp = frame_code;
for (i = 1; i < 8; i++)
else if (stream_index == -2)
return back_ptr;
- assert(0);
+ return AV_NOPTS_VALUE;
}
static int read_seek(AVFormatContext *s, int stream_index,
int64_t pos, pos2, ts;
int i;
+ if (nut->flags & NUT_PIPE) {
+ return AVERROR(ENOSYS);
+ }
+
if (st->index_entries) {
int index = av_index_search_timestamp(st, pts, flags);
if (index < 0)
return 0;
}
-static int nut_read_close(AVFormatContext *s)
-{
- NUTContext *nut = s->priv_data;
- int i;
-
- av_freep(&nut->time_base);
- av_freep(&nut->stream);
- ff_nut_free_sp(nut);
- for (i = 1; i < nut->header_count; i++)
- av_freep(&nut->header[i]);
-
- return 0;
-}
-
AVInputFormat ff_nut_demuxer = {
.name = "nut",
.long_name = NULL_IF_CONFIG_SMALL("NUT"),
.read_close = nut_read_close,
.read_seek = read_seek,
.extensions = "nut",
- .codec_tag = (const AVCodecTag * const []) {
- ff_codec_bmp_tags, ff_nut_video_tags, ff_codec_wav_tags,
- ff_nut_subtitle_tags, 0
- },
+ .codec_tag = ff_nut_codec_tags,
};