X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fnutdec.c;h=b554f85101e8446824de835c760e59890fd10ac2;hb=9200514ad8717c63f82101dc394f4378854325bf;hp=32db763a6771373514a0ceaed2f5c5c712543fe1;hpb=a559d65c0711284fc63689991f69db01bcfbb140;p=ffmpeg diff --git a/libavformat/nutdec.c b/libavformat/nutdec.c index 32db763a677..b554f85101e 100644 --- a/libavformat/nutdec.c +++ b/libavformat/nutdec.c @@ -27,6 +27,7 @@ #include "libavutil/tree.h" #include "avio_internal.h" #include "nut.h" +#include "riff.h" #undef NDEBUG #include @@ -76,8 +77,8 @@ static uint64_t get_fourcc(AVIOContext *bc) } #ifdef TRACE -static inline uint64_t get_v_trace(AVIOContext *bc, char *file, - char *func, int line) +static inline uint64_t get_v_trace(AVIOContext *bc, const char *file, + const char *func, int line) { uint64_t v = ffio_read_varlen(bc); @@ -86,8 +87,8 @@ static inline uint64_t get_v_trace(AVIOContext *bc, char *file, return v; } -static inline int64_t get_s_trace(AVIOContext *bc, char *file, - char *func, int line) +static inline int64_t get_s_trace(AVIOContext *bc, const char *file, + const char *func, int line) { int64_t v = get_s(bc); @@ -96,18 +97,8 @@ static inline int64_t get_s_trace(AVIOContext *bc, char *file, return v; } -static inline uint64_t get_vb_trace(AVIOContext *bc, char *file, - char *func, int line) -{ - uint64_t v = get_vb(bc); - - av_log(NULL, AV_LOG_DEBUG, "get_vb %5"PRId64" / %"PRIX64" in %s %s:%d\n", - v, v, file, func, line); - return v; -} #define ffio_read_varlen(bc) get_v_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) #define get_s(bc) get_s_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) -#define get_vb(bc) get_vb_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) #endif static int get_packetheader(NUTContext *nut, AVIOContext *bc, @@ -187,20 +178,22 @@ static int nut_probe(AVProbeData *p) 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); @@ -220,8 +213,15 @@ static int decode_main_header(NUTContext *nut) 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) { @@ -229,14 +229,18 @@ static int decode_main_header(NUTContext *nut) 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"); + av_log(s, AV_LOG_ERROR, "invalid time base %d/%d\n", + nut->time_base[i].num, + nut->time_base[i].den); return AVERROR_INVALIDDATA; } } @@ -279,7 +283,8 @@ static int decode_main_header(NUTContext *nut) return AVERROR_INVALIDDATA; } if (tmp_stream >= stream_count) { - av_log(s, AV_LOG_ERROR, "illegal stream number\n"); + av_log(s, AV_LOG_ERROR, "illegal stream number %d >= %d\n", + tmp_stream, stream_count); return AVERROR_INVALIDDATA; } @@ -302,27 +307,40 @@ static int decode_main_header(NUTContext *nut) 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; GET_V(nut->header_len[i], tmp > 0 && tmp < 256); - rem -= nut->header_len[i]; - if (rem < 0) { - av_log(s, AV_LOG_ERROR, "invalid elision header\n"); + if (rem < nut->header_len[i]) { + av_log(s, AV_LOG_ERROR, + "invalid elision header %d : %d > %d\n", + i, nut->header_len[i], rem); return AVERROR_INVALIDDATA; } - nut->header[i] = av_malloc(nut->header_len[i]); - avio_read(bc, nut->header[i], nut->header_len[i]); + rem -= nut->header_len[i]; + hdr = av_malloc(nut->header_len[i]); + if (!hdr) + return AVERROR(ENOMEM); + avio_read(bc, hdr, nut->header_len[i]); + nut->header[i] = 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); @@ -349,33 +367,39 @@ static int decode_stream_header(NUTContext *nut) class = ffio_read_varlen(bc); tmp = get_fourcc(bc); - st->codec->codec_tag = tmp; + st->codecpar->codec_tag = tmp; switch (class) { case 0: - st->codec->codec_type = AVMEDIA_TYPE_VIDEO; - st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) { - ff_codec_bmp_tags, + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = av_codec_get_id((const AVCodecTag * const []) { 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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->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; - st->codec->codec_id = ff_codec_get_id(ff_nut_subtitle_tags, tmp); + st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codecpar->codec_id = ff_codec_get_id(ff_nut_subtitle_tags, tmp); break; case 3: - st->codec->codec_type = AVMEDIA_TYPE_DATA; + st->codecpar->codec_type = AVMEDIA_TYPE_DATA; + st->codecpar->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 == CODEC_ID_NONE) + if (class < 3 && st->codecpar->codec_id == AV_CODEC_ID_NONE) av_log(s, AV_LOG_ERROR, "Unknown codec tag '0x%04x' for stream number %d\n", (unsigned int) tmp, stream_id); @@ -384,36 +408,37 @@ static int decode_stream_header(NUTContext *nut) 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); - avio_read(bc, st->codec->extradata, st->codec->extradata_size); + GET_V(st->codecpar->extradata_size, tmp < (1 << 30)); + if (st->codecpar->extradata_size) { + st->codecpar->extradata = av_mallocz(st->codecpar->extradata_size + + AV_INPUT_BUFFER_PADDING_SIZE); + if (!st->codecpar->extradata) + return AVERROR(ENOMEM); + avio_read(bc, st->codecpar->extradata, st->codecpar->extradata_size); } - if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - GET_V(st->codec->width, tmp > 0) - GET_V(st->codec->height, tmp > 0) + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + GET_V(st->codecpar->width, tmp > 0); + GET_V(st->codecpar->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) + } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + GET_V(st->codecpar->sample_rate, tmp > 0); ffio_read_varlen(bc); // samplerate_den - GET_V(st->codec->channels, tmp > 0) + GET_V(st->codecpar->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, @@ -446,14 +471,16 @@ static int decode_info_header(NUTContext *nut) 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); @@ -465,12 +492,21 @@ static int decode_info_header(NUTContext *nut) 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)); @@ -496,7 +532,9 @@ static int decode_info_header(NUTContext *nut) } if (stream_id_plus1 > s->nb_streams) { - av_log(s, AV_LOG_ERROR, "invalid stream id for info packet\n"); + av_log(s, AV_LOG_WARNING, + "invalid stream id %d for info packet\n", + stream_id_plus1); continue; } @@ -506,14 +544,17 @@ static int decode_info_header(NUTContext *nut) 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; } @@ -523,6 +564,7 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) AVFormatContext *s = nut->avf; AVIOContext *bc = s->pb; int64_t end, tmp; + int ret; nut->last_syncpoint_pos = avio_tell(bc) - 8; @@ -537,14 +579,24 @@ 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], 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; } @@ -558,22 +610,26 @@ static int find_and_decode_index(NUTContext *nut) 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; + av_log(s, AV_LOG_WARNING, "no index at the end\n"); + 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) @@ -643,6 +699,20 @@ fail: 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; @@ -658,7 +728,7 @@ static int nut_read_header(AVFormatContext *s) 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); @@ -668,7 +738,7 @@ static int nut_read_header(AVFormatContext *s) 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++; @@ -682,7 +752,7 @@ static int nut_read_header(AVFormatContext *s) 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; @@ -693,7 +763,7 @@ static int nut_read_header(AVFormatContext *s) 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); @@ -705,6 +775,11 @@ static int nut_read_header(AVFormatContext *s) 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, @@ -716,7 +791,8 @@ 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); @@ -736,7 +812,7 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, 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) { @@ -769,8 +845,9 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, 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; } @@ -785,7 +862,7 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) { 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; @@ -810,8 +887,11 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) return 1; } - av_new_packet(pkt, size + nut->header_len[header_idx]); - memcpy(pkt->data, nut->header[header_idx], nut->header_len[header_idx]); + ret = av_new_packet(pkt, size + nut->header_len[header_idx]); + if (ret < 0) + return ret; + if (nut->header[header_idx]) + 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); @@ -840,7 +920,7 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) } 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++) @@ -907,7 +987,7 @@ static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index, else if (stream_index == -2) return back_ptr; - assert(0); + return AV_NOPTS_VALUE; } static int read_seek(AVFormatContext *s, int stream_index, @@ -921,6 +1001,10 @@ 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) @@ -971,24 +1055,9 @@ static int read_seek(AVFormatContext *s, int stream_index, 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; -} - -#if CONFIG_NUT_DEMUXER AVInputFormat ff_nut_demuxer = { .name = "nut", - .long_name = NULL_IF_CONFIG_SMALL("NUT format"), + .long_name = NULL_IF_CONFIG_SMALL("NUT"), .priv_data_size = sizeof(NUTContext), .read_probe = nut_probe, .read_header = nut_read_header, @@ -996,9 +1065,5 @@ AVInputFormat ff_nut_demuxer = { .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, }; -#endif