diff options
Diffstat (limited to 'libavformat/nutdec.c')
-rw-r--r-- | libavformat/nutdec.c | 411 |
1 files changed, 339 insertions, 72 deletions
diff --git a/libavformat/nutdec.c b/libavformat/nutdec.c index 47f6e318b3..13fb39924d 100644 --- a/libavformat/nutdec.c +++ b/libavformat/nutdec.c @@ -3,37 +3,41 @@ * Copyright (c) 2004-2006 Michael Niedermayer * Copyright (c) 2003 Alex Beregszaszi * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/avstring.h" +#include "libavutil/avassert.h" #include "libavutil/bswap.h" #include "libavutil/dict.h" +#include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/tree.h" +#include "libavcodec/bytestream.h" #include "avio_internal.h" +#include "isom.h" #include "nut.h" #include "riff.h" -#undef NDEBUG -#include <assert.h> - #define NUT_MAX_STREAMS 256 /* arbitrary sanity check value */ +static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index, + int64_t *pos_arg, int64_t pos_limit); + static int get_str(AVIOContext *bc, char *string, unsigned int maxlen) { unsigned int len = ffio_read_varlen(bc); @@ -43,11 +47,15 @@ static int get_str(AVIOContext *bc, char *string, unsigned int maxlen) while (len > maxlen) { avio_r8(bc); len--; + if (bc->eof_reached) + len = maxlen; } if (maxlen) string[FFMIN(len, maxlen - 1)] = 0; + if (bc->eof_reached) + return AVERROR_EOF; if (maxlen == len) return -1; else @@ -72,8 +80,10 @@ static uint64_t get_fourcc(AVIOContext *bc) return avio_rl16(bc); else if (len == 4) return avio_rl32(bc); - else + else { + av_log(NULL, AV_LOG_ERROR, "Unsupported fourcc length %d\n", len); return -1; + } } #ifdef TRACE @@ -97,8 +107,18 @@ static inline int64_t get_s_trace(AVIOContext *bc, const char *file, return v; } +static inline uint64_t get_4cc_trace(AVIOContext *bc, char *file, + char *func, int line) +{ + uint64_t v = get_fourcc(bc); + + av_log(NULL, AV_LOG_DEBUG, "get_fourcc %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_fourcc(bc) get_4cc_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) #endif static int get_packetheader(NUTContext *nut, AVIOContext *bc, @@ -130,7 +150,7 @@ static uint64_t find_any_startcode(AVIOContext *bc, int64_t pos) /* Note, this may fail if the stream is not seekable, but that should * not matter, as in this case we simply start where we currently are */ avio_seek(bc, pos, SEEK_SET); - while (!bc->eof_reached) { + while (!avio_feof(bc)) { state = (state << 8) | avio_r8(bc); if ((state >> 56) != 'N') continue; @@ -168,11 +188,11 @@ static int64_t find_startcode(AVIOContext *bc, uint64_t code, int64_t pos) static int nut_probe(AVProbeData *p) { int i; - uint64_t code = 0; - for (i = 0; i < p->buf_size; i++) { - code = (code << 8) | p->buf[i]; - if (code == MAIN_STARTCODE) + for (i = 0; i < p->buf_size-8; i++) { + if (AV_RB32(p->buf+i) != MAIN_STARTCODE>>32) + continue; + if (AV_RB32(p->buf+i+4) == (MAIN_STARTCODE & 0xFFFFFFFF)) return AVPROBE_SCORE_MAX; } return 0; @@ -183,7 +203,8 @@ static int nut_probe(AVProbeData *p) tmp = ffio_read_varlen(bc); \ if (!(check)) { \ av_log(s, AV_LOG_ERROR, "Error " #dst " is (%"PRId64")\n", tmp); \ - return AVERROR_INVALIDDATA; \ + ret = AVERROR_INVALIDDATA; \ + goto fail; \ } \ dst = tmp; \ } while (0) @@ -195,8 +216,11 @@ static int skip_reserved(AVIOContext *bc, int64_t pos) avio_seek(bc, pos, SEEK_CUR); return AVERROR_INVALIDDATA; } else { - while (pos--) + while (pos--) { + if (bc->eof_reached) + return AVERROR_INVALIDDATA; avio_r8(bc); + } return 0; } } @@ -207,7 +231,7 @@ static int decode_main_header(NUTContext *nut) AVIOContext *bc = s->pb; uint64_t tmp, end; unsigned int stream_count; - int i, j, count; + int i, j, count, ret; int tmp_stream, tmp_mul, tmp_pts, tmp_size, tmp_res, tmp_head_idx; end = get_packetheader(nut, bc, 1, MAIN_STARTCODE); @@ -220,6 +244,8 @@ static int decode_main_header(NUTContext *nut) nut->version); return AVERROR(ENOSYS); } + if (nut->version > 3) + nut->minor_version = ffio_read_varlen(bc); GET_V(stream_count, tmp > 0 && tmp <= NUT_MAX_STREAMS); @@ -230,7 +256,7 @@ static int decode_main_header(NUTContext *nut) } GET_V(nut->time_base_count, tmp > 0 && tmp < INT_MAX / sizeof(AVRational)); - nut->time_base = av_malloc(nut->time_base_count * sizeof(AVRational)); + nut->time_base = av_malloc_array(nut->time_base_count, sizeof(AVRational)); if (!nut->time_base) return AVERROR(ENOMEM); @@ -239,7 +265,8 @@ static int decode_main_header(NUTContext *nut) 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; + ret = AVERROR_INVALIDDATA; + goto fail; } } tmp_pts = 0; @@ -273,16 +300,24 @@ static int decode_main_header(NUTContext *nut) if (tmp_fields > 7) tmp_head_idx = ffio_read_varlen(bc); - while (tmp_fields-- > 8) + while (tmp_fields-- > 8) { + if (bc->eof_reached) { + av_log(s, AV_LOG_ERROR, "reached EOF while decoding main header\n"); + ret = AVERROR_INVALIDDATA; + goto fail; + } ffio_read_varlen(bc); + } - if (count == 0 || i + count > 256) { + if (count <= 0 || count > 256 - (i <= 'N') - i) { av_log(s, AV_LOG_ERROR, "illegal count %d at %d\n", count, i); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } if (tmp_stream >= stream_count) { av_log(s, AV_LOG_ERROR, "illegal stream number\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } for (j = 0; j < count; j++, i++) { @@ -300,7 +335,7 @@ static int decode_main_header(NUTContext *nut) nut->frame_code[i].header_idx = tmp_head_idx; } } - assert(nut->frame_code['N'].flags == FLAG_INVALID); + av_assert0(nut->frame_code['N'].flags == FLAG_INVALID); if (end > avio_tell(bc) + 4) { int rem = 1024; @@ -312,34 +347,47 @@ static int decode_main_header(NUTContext *nut) rem -= nut->header_len[i]; if (rem < 0) { av_log(s, AV_LOG_ERROR, "invalid elision header\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } hdr = av_malloc(nut->header_len[i]); - if (!hdr) - return AVERROR(ENOMEM); + if (!hdr) { + ret = AVERROR(ENOMEM); + goto fail; + } avio_read(bc, hdr, nut->header_len[i]); nut->header[i] = hdr; } - assert(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) { + if (nut->version > 3 && end > avio_tell(bc) + 4) { 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; + ret = AVERROR_INVALIDDATA; + goto fail; } - nut->stream = av_mallocz(sizeof(StreamContext) * stream_count); - if (!nut->stream) - return AVERROR(ENOMEM); + nut->stream = av_calloc(stream_count, sizeof(StreamContext)); + if (!nut->stream) { + ret = AVERROR(ENOMEM); + goto fail; + } for (i = 0; i < stream_count; i++) avformat_new_stream(s, NULL); return 0; +fail: + av_freep(&nut->time_base); + for (i = 1; i < nut->header_count; i++) { + av_freep(&nut->header[i]); + } + nut->header_count = 0; + return ret; } static int decode_stream_header(NUTContext *nut) @@ -347,9 +395,9 @@ static int decode_stream_header(NUTContext *nut) AVFormatContext *s = nut->avf; AVIOContext *bc = s->pb; StreamContext *stc; - int class, stream_id; + int class, stream_id, ret; uint64_t tmp, end; - AVStream *st; + AVStream *st = NULL; end = get_packetheader(nut, bc, 1, STREAM_STARTCODE); end += avio_tell(bc); @@ -369,6 +417,7 @@ static int decode_stream_header(NUTContext *nut) st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) { ff_nut_video_tags, ff_codec_bmp_tags, + ff_codec_movvideo_tags, 0 }, tmp); @@ -378,6 +427,7 @@ static int decode_stream_header(NUTContext *nut) st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) { ff_nut_audio_tags, ff_codec_wav_tags, + ff_nut_audio_extra_tags, 0 }, tmp); @@ -403,15 +453,13 @@ 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); - if (!st->codec->extradata) + if (ff_get_extradata(st->codec, bc, st->codec->extradata_size) < 0) return AVERROR(ENOMEM); - avio_read(bc, st->codec->extradata, st->codec->extradata_size); } if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { @@ -422,7 +470,8 @@ static int decode_stream_header(NUTContext *nut) 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 AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } ffio_read_varlen(bc); /* csp type */ } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { @@ -433,12 +482,19 @@ static int decode_stream_header(NUTContext *nut) if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { av_log(s, AV_LOG_ERROR, "stream header %d checksum mismatch\n", stream_id); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } stc->time_base = &nut->time_base[stc->time_base_id]; avpriv_set_pts_info(s->streams[stream_id], 63, stc->time_base->num, stc->time_base->den); return 0; +fail: + if (st && st->codec) { + av_freep(&st->codec->extradata); + st->codec->extradata_size = 0; + } + return ret; } static void set_disposition_bits(AVFormatContext *avf, char *value, @@ -462,7 +518,7 @@ static int decode_info_header(NUTContext *nut) AVIOContext *bc = s->pb; uint64_t tmp, chapter_start, chapter_len; unsigned int stream_id_plus1, count; - int chapter_id, i; + int chapter_id, i, ret = 0; int64_t value, end; char name[256], str_value[1024], type_str[256]; const char *type; @@ -504,15 +560,25 @@ static int decode_info_header(NUTContext *nut) } for (i = 0; i < count; i++) { - get_str(bc, name, sizeof(name)); + ret = get_str(bc, name, sizeof(name)); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "get_str failed while decoding info header\n"); + return ret; + } value = get_s(bc); + str_value[0] = 0; + if (value == -1) { type = "UTF-8"; - get_str(bc, str_value, sizeof(str_value)); + ret = get_str(bc, str_value, sizeof(str_value)); } else if (value == -2) { - get_str(bc, type_str, sizeof(type_str)); + ret = get_str(bc, type_str, sizeof(type_str)); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "get_str failed while decoding info header\n"); + return ret; + } type = type_str; - get_str(bc, str_value, sizeof(str_value)); + ret = get_str(bc, str_value, sizeof(str_value)); } else if (value == -3) { type = "s"; value = get_s(bc); @@ -526,6 +592,11 @@ static int decode_info_header(NUTContext *nut) type = "v"; } + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "get_str failed while decoding info header\n"); + return ret; + } + if (stream_id_plus1 > s->nb_streams) { av_log(s, AV_LOG_ERROR, "invalid stream id for info packet\n"); continue; @@ -536,6 +607,15 @@ static int decode_info_header(NUTContext *nut) set_disposition_bits(s, str_value, stream_id_plus1 - 1); continue; } + + if (stream_id_plus1 && !strcmp(name, "r_frame_rate")) { + sscanf(str_value, "%d/%d", &st->r_frame_rate.num, &st->r_frame_rate.den); + if (st->r_frame_rate.num >= 1000LL*st->r_frame_rate.den || + st->r_frame_rate.num < 0 || st->r_frame_rate.num < 0) + st->r_frame_rate.num = st->r_frame_rate.den = 0; + continue; + } + if (metadata && av_strcasecmp(name, "Uses") && av_strcasecmp(name, "Depends") && av_strcasecmp(name, "Replaces")) { if (event_flags) @@ -549,14 +629,16 @@ static int decode_info_header(NUTContext *nut) av_log(s, AV_LOG_ERROR, "info header checksum mismatch\n"); return AVERROR_INVALIDDATA; } - return 0; +fail: + return FFMIN(ret, 0); } 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; + int64_t end; + uint64_t tmp; int ret; nut->last_syncpoint_pos = avio_tell(bc) - 8; @@ -567,7 +649,7 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) tmp = ffio_read_varlen(bc); *back_ptr = nut->last_syncpoint_pos - 16 * ffio_read_varlen(bc); if (*back_ptr < 0) - return -1; + return AVERROR_INVALIDDATA; ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count], tmp / nut->time_base_count); @@ -585,8 +667,8 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) return AVERROR_INVALIDDATA; } - *ts = tmp / s->nb_streams * - av_q2d(nut->time_base[tmp % s->nb_streams]) * AV_TIME_BASE; + *ts = tmp / nut->time_base_count * + av_q2d(nut->time_base[tmp % nut->time_base_count]) * AV_TIME_BASE; if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, *back_ptr, *ts)) < 0) return ret; @@ -594,6 +676,19 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) return 0; } +//FIXME calculate exactly, this is just a good approximation. +static int64_t find_duration(NUTContext *nut, int64_t filesize) +{ + AVFormatContext *s = nut->avf; + int64_t duration = 0; + + ff_find_last_ts(s, -1, &duration, NULL, nut_read_timestamp); + + if(duration > 0) + s->duration_estimation_method = AVFMT_DURATION_FROM_PTS; + return duration; +} + static int find_and_decode_index(NUTContext *nut) { AVFormatContext *s = nut->avf; @@ -601,24 +696,36 @@ static int find_and_decode_index(NUTContext *nut) uint64_t tmp, end; int i, j, syncpoint_count; int64_t filesize = avio_size(bc); - int64_t *syncpoints; - int8_t *has_keyframe; + int64_t *syncpoints = NULL; + uint64_t max_pts; + int8_t *has_keyframe = NULL; int ret = AVERROR_INVALIDDATA; + if(filesize <= 0) + return -1; + 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"); + + if(s->duration<=0) + s->duration = find_duration(nut, filesize); return ret; } end = get_packetheader(nut, bc, 1, INDEX_STARTCODE); end += avio_tell(bc); - ffio_read_varlen(bc); // max_pts + max_pts = ffio_read_varlen(bc); + s->duration = av_rescale_q(max_pts / nut->time_base_count, + nut->time_base[max_pts % nut->time_base_count], + AV_TIME_BASE_Q); + s->duration_estimation_method = AVFMT_DURATION_FROM_PTS; + 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)); + syncpoints = av_malloc_array(syncpoint_count, sizeof(int64_t)); + has_keyframe = av_malloc_array(syncpoint_count + 1, sizeof(int8_t)); if (!syncpoints || !has_keyframe) { ret = AVERROR(ENOMEM); goto fail; @@ -642,13 +749,17 @@ static int find_and_decode_index(NUTContext *nut) int flag = x & 1; x >>= 1; if (n + x >= syncpoint_count + 1) { - av_log(s, AV_LOG_ERROR, "index overflow A\n"); + av_log(s, AV_LOG_ERROR, "index overflow A %d + %"PRIu64" >= %d\n", n, x, syncpoint_count + 1); goto fail; } while (x--) has_keyframe[n++] = flag; has_keyframe[n++] = !flag; } else { + if (x <= 1) { + av_log(s, AV_LOG_ERROR, "index: x %"PRIu64" is invalid\n", x); + goto fail; + } while (x != 1) { if (n >= syncpoint_count + 1) { av_log(s, AV_LOG_ERROR, "index overflow B\n"); @@ -662,7 +773,7 @@ static int find_and_decode_index(NUTContext *nut) av_log(s, AV_LOG_ERROR, "keyframe before first syncpoint in index\n"); goto fail; } - assert(n <= syncpoint_count + 1); + av_assert0(n <= syncpoint_count + 1); for (; j < n && j < syncpoint_count; j++) { if (has_keyframe[j]) { uint64_t B, A = ffio_read_varlen(bc); @@ -763,7 +874,7 @@ static int nut_read_header(AVFormatContext *s) find_and_decode_index(nut); avio_seek(bc, orig_pos, SEEK_SET); } - assert(nut->next_startcode == SYNCPOINT_STARTCODE); + av_assert0(nut->next_startcode == SYNCPOINT_STARTCODE); ff_metadata_conv_ctx(s, NULL, ff_nut_metadata_conv); @@ -775,13 +886,135 @@ fail: return AVERROR_INVALIDDATA; } +static int read_sm_data(AVFormatContext *s, AVIOContext *bc, AVPacket *pkt, int is_meta, int64_t maxpos) +{ + int count = ffio_read_varlen(bc); + int skip_start = 0; + int skip_end = 0; + int channels = 0; + int64_t channel_layout = 0; + int sample_rate = 0; + int width = 0; + int height = 0; + int i, ret; + + for (i=0; i<count; i++) { + uint8_t name[256], str_value[256], type_str[256]; + int value; + if (avio_tell(bc) >= maxpos) + return AVERROR_INVALIDDATA; + ret = get_str(bc, name, sizeof(name)); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "get_str failed while reading sm data\n"); + return ret; + } + value = get_s(bc); + + if (value == -1) { + ret = get_str(bc, str_value, sizeof(str_value)); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "get_str failed while reading sm data\n"); + return ret; + } + av_log(s, AV_LOG_WARNING, "Unknown string %s / %s\n", name, str_value); + } else if (value == -2) { + uint8_t *dst = NULL; + int64_t v64, value_len; + + ret = get_str(bc, type_str, sizeof(type_str)); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "get_str failed while reading sm data\n"); + return ret; + } + value_len = ffio_read_varlen(bc); + if (avio_tell(bc) + value_len >= maxpos) + return AVERROR_INVALIDDATA; + if (!strcmp(name, "Palette")) { + dst = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, value_len); + } else if (!strcmp(name, "Extradata")) { + dst = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, value_len); + } else if (sscanf(name, "CodecSpecificSide%"SCNd64"", &v64) == 1) { + dst = av_packet_new_side_data(pkt, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, value_len + 8); + if(!dst) + return AVERROR(ENOMEM); + AV_WB64(dst, v64); + dst += 8; + } else if (!strcmp(name, "ChannelLayout") && value_len == 8) { + channel_layout = avio_rl64(bc); + continue; + } else { + av_log(s, AV_LOG_WARNING, "Unknown data %s / %s\n", name, type_str); + avio_skip(bc, value_len); + continue; + } + if(!dst) + return AVERROR(ENOMEM); + avio_read(bc, dst, value_len); + } else if (value == -3) { + value = get_s(bc); + } else if (value == -4) { + value = ffio_read_varlen(bc); + } else if (value < -4) { + get_s(bc); + } else { + if (!strcmp(name, "SkipStart")) { + skip_start = value; + } else if (!strcmp(name, "SkipEnd")) { + skip_end = value; + } else if (!strcmp(name, "Channels")) { + channels = value; + } else if (!strcmp(name, "SampleRate")) { + sample_rate = value; + } else if (!strcmp(name, "Width")) { + width = value; + } else if (!strcmp(name, "Height")) { + height = value; + } else { + av_log(s, AV_LOG_WARNING, "Unknown integer %s\n", name); + } + } + } + + if (channels || channel_layout || sample_rate || width || height) { + uint8_t *dst = av_packet_new_side_data(pkt, AV_PKT_DATA_PARAM_CHANGE, 28); + if (!dst) + return AVERROR(ENOMEM); + bytestream_put_le32(&dst, + AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT*(!!channels) + + AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT*(!!channel_layout) + + AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE*(!!sample_rate) + + AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS*(!!(width|height)) + ); + if (channels) + bytestream_put_le32(&dst, channels); + if (channel_layout) + bytestream_put_le64(&dst, channel_layout); + if (sample_rate) + bytestream_put_le32(&dst, sample_rate); + if (width || height){ + bytestream_put_le32(&dst, width); + bytestream_put_le32(&dst, height); + } + } + + if (skip_start || skip_end) { + uint8_t *dst = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10); + if (!dst) + return AVERROR(ENOMEM); + AV_WL32(dst, skip_start); + AV_WL32(dst+4, skip_end); + } + + return 0; +} + static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, uint8_t *header_idx, int frame_code) { AVFormatContext *s = nut->avf; AVIOContext *bc = s->pb; StreamContext *stc; - int size, flags, size_mul, pts_delta, i, reserved_count; + int size, flags, size_mul, pts_delta, i, reserved_count, ret; uint64_t tmp; if (!(nut->flags & NUT_PIPE) && @@ -814,7 +1047,7 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, if (coded_pts < (1 << stc->msb_pts_shift)) { *pts = ff_lsb2full(stc, coded_pts); } else - *pts = coded_pts - (1 << stc->msb_pts_shift); + *pts = coded_pts - (1LL << stc->msb_pts_shift); } else *pts = stc->last_pts + pts_delta; if (flags & FLAG_SIZE_MSB) @@ -825,8 +1058,13 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, *header_idx = ffio_read_varlen(bc); if (flags & FLAG_RESERVED) reserved_count = ffio_read_varlen(bc); - for (i = 0; i < reserved_count; i++) + for (i = 0; i < reserved_count; i++) { + if (bc->eof_reached) { + av_log(s, AV_LOG_ERROR, "reached EOF while decoding frame header\n"); + return AVERROR_INVALIDDATA; + } ffio_read_varlen(bc); + } if (*header_idx >= (unsigned)nut->header_count) { av_log(s, AV_LOG_ERROR, "header_idx invalid\n"); @@ -849,6 +1087,8 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, stc->last_flags = flags; return size; +fail: + return ret; } static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) @@ -885,7 +1125,27 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) 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); + if (stc->last_flags & FLAG_SM_DATA) { + int sm_size; + if (read_sm_data(s, bc, pkt, 0, pkt->pos + size) < 0) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + if (read_sm_data(s, bc, pkt, 1, pkt->pos + size) < 0) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + sm_size = avio_tell(bc) - pkt->pos; + size -= sm_size; + pkt->size -= sm_size; + } + + ret = avio_read(bc, pkt->data + nut->header_len[header_idx], size); + if (ret != size) { + if (ret < 0) + goto fail; + } + av_shrink_packet(pkt, nut->header_len[header_idx] + ret); pkt->stream_index = stream_id; if (stc->last_flags & FLAG_KEY) @@ -893,6 +1153,9 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) pkt->pts = pts; return 0; +fail: + av_free_packet(pkt); + return ret; } static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) @@ -911,7 +1174,7 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) pos -= 8; } else { frame_code = avio_r8(bc); - if (bc->eof_reached) + if (avio_feof(bc)) return AVERROR_EOF; if (frame_code == 'N') { tmp = frame_code; @@ -943,7 +1206,8 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) default: resync: av_log(s, AV_LOG_DEBUG, "syncing from %"PRId64"\n", pos); - tmp = find_any_startcode(bc, nut->last_syncpoint_pos + 1); + tmp = find_any_startcode(bc, FFMAX(nut->last_syncpoint_pos, nut->last_resync_pos) + 1); + nut->last_resync_pos = avio_tell(bc); if (tmp == 0) return AVERROR_INVALIDDATA; av_log(s, AV_LOG_DEBUG, "sync\n"); @@ -965,21 +1229,18 @@ static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index, do { pos = find_startcode(bc, SYNCPOINT_STARTCODE, pos) + 1; if (pos < 1) { - assert(nut->next_startcode == 0); av_log(s, AV_LOG_ERROR, "read_timestamp failed.\n"); return AV_NOPTS_VALUE; } } while (decode_syncpoint(nut, &pts, &back_ptr) < 0); *pos_arg = pos - 1; - assert(nut->last_syncpoint_pos == *pos_arg); + av_assert0(nut->last_syncpoint_pos == *pos_arg); av_log(s, AV_LOG_DEBUG, "return %"PRId64" %"PRId64"\n", pts, back_ptr); - if (stream_index == -1) - return pts; - else if (stream_index == -2) + if (stream_index == -2) return back_ptr; - - return AV_NOPTS_VALUE; + av_assert0(stream_index == -1); + return pts; } static int read_seek(AVFormatContext *s, int stream_index, @@ -1000,6 +1261,8 @@ static int read_seek(AVFormatContext *s, int stream_index, if (st->index_entries) { int index = av_index_search_timestamp(st, pts, flags); if (index < 0) + index = av_index_search_timestamp(st, pts, flags ^ AVSEEK_FLAG_BACKWARD); + if (index < 0) return -1; pos2 = st->index_entries[index].pos; @@ -1032,24 +1295,28 @@ static int read_seek(AVFormatContext *s, int stream_index, sp = av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp, NULL); - assert(sp); + av_assert0(sp); pos2 = sp->back_ptr - 15; } av_log(NULL, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos2); pos = find_startcode(s->pb, SYNCPOINT_STARTCODE, pos2); avio_seek(s->pb, pos, SEEK_SET); + nut->last_syncpoint_pos = pos; av_log(NULL, AV_LOG_DEBUG, "SP: %"PRId64"\n", pos); if (pos2 > pos || pos2 + 15 < pos) av_log(NULL, AV_LOG_ERROR, "no syncpoint at backptr pos\n"); for (i = 0; i < s->nb_streams; i++) nut->stream[i].skip_until_key_frame = 1; + nut->last_resync_pos = 0; + return 0; } AVInputFormat ff_nut_demuxer = { .name = "nut", .long_name = NULL_IF_CONFIG_SMALL("NUT"), + .flags = AVFMT_SEEK_TO_PTS, .priv_data_size = sizeof(NUTContext), .read_probe = nut_probe, .read_header = nut_read_header, |