diff options
Diffstat (limited to 'libavformat/nutdec.c')
-rw-r--r-- | libavformat/nutdec.c | 266 |
1 files changed, 227 insertions, 39 deletions
diff --git a/libavformat/nutdec.c b/libavformat/nutdec.c index 838c181589..7da6f2c441 100644 --- a/libavformat/nutdec.c +++ b/libavformat/nutdec.c @@ -3,37 +3,40 @@ * 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 "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); @@ -72,8 +75,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 +102,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 +145,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 (!url_feof(bc)) { state = (state << 8) | avio_r8(bc); if ((state >> 56) != 'N') continue; @@ -213,12 +228,15 @@ static int decode_main_header(NUTContext *nut) end = get_packetheader(nut, bc, 1, MAIN_STARTCODE); end += avio_tell(bc); - nut->version = ffio_read_varlen(bc); + tmp = ffio_read_varlen(bc); if (tmp < NUT_MIN_VERSION && tmp > NUT_MAX_VERSION) { av_log(s, AV_LOG_ERROR, "Version %"PRId64" not supported.\n", tmp); return AVERROR(ENOSYS); } + nut->version = tmp; + if (nut->version > 3) + nut->minor_version = ffio_read_varlen(bc); GET_V(stream_count, tmp > 0 && tmp <= NUT_MAX_STREAMS); @@ -230,6 +248,8 @@ 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)); + 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)); @@ -297,7 +317,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; @@ -317,11 +337,11 @@ static int decode_main_header(NUTContext *nut) 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); } @@ -330,7 +350,9 @@ static int decode_main_header(NUTContext *nut) return AVERROR_INVALIDDATA; } - nut->stream = av_mallocz(sizeof(StreamContext) * stream_count); + nut->stream = av_calloc(stream_count, sizeof(StreamContext)); + if (!nut->stream) + return AVERROR(ENOMEM); for (i = 0; i < stream_count; i++) avformat_new_stream(s, NULL); @@ -373,6 +395,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,9 +426,8 @@ static int decode_stream_header(NUTContext *nut) 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); + if (ff_get_extradata(st->codec, bc, st->codec->extradata_size) < 0) + return AVERROR(ENOMEM); } if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { @@ -519,6 +541,14 @@ 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 = st->r_frame_rate.den = 0; + continue; + } + if (metadata && av_strcasecmp(name, "Uses") && av_strcasecmp(name, "Depends") && av_strcasecmp(name, "Replaces")) av_dict_set(metadata, name, str_value, 0); @@ -536,7 +566,8 @@ 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; @@ -547,7 +578,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); @@ -565,8 +596,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; @@ -574,6 +605,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; @@ -582,23 +626,39 @@ static int find_and_decode_index(NUTContext *nut) int i, j, syncpoint_count; int64_t filesize = avio_size(bc); int64_t *syncpoints; + uint64_t max_pts; int8_t *has_keyframe; 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; + } for (i = 0; i < syncpoint_count; i++) { syncpoints[i] = ffio_read_varlen(bc); if (syncpoints[i] <= 0) @@ -618,7 +678,7 @@ 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--) @@ -638,7 +698,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); @@ -725,13 +785,123 @@ 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); return 0; } +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; + + 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; + get_str(bc, name, sizeof(name)); + value = get_s(bc); + + if (value == -1) { + get_str(bc, str_value, sizeof(str_value)); + 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; + + get_str(bc, type_str, sizeof(type_str)); + 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) { @@ -771,7 +941,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) @@ -816,6 +986,7 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) int64_t pts, last_IP_pts; StreamContext *stc; uint8_t header_idx; + int ret; size = decode_frame_header(nut, &pts, &stream_id, &header_idx, frame_code); if (size < 0) @@ -837,10 +1008,27 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) return 1; } - av_new_packet(pkt, size + nut->header_len[header_idx]); + if (av_new_packet(pkt, size + nut->header_len[header_idx]) < 0) + return AVERROR(ENOMEM); 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) + return AVERROR_INVALIDDATA; + if (read_sm_data(s, bc, pkt, 1, pkt->pos + size) < 0) + return AVERROR_INVALIDDATA; + 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) + return ret; + } + av_shrink_packet(pkt, nut->header_len[header_idx] + ret); pkt->stream_index = stream_id; if (stc->last_flags & FLAG_KEY) @@ -866,7 +1054,7 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) pos -= 8; } else { frame_code = avio_r8(bc); - if (bc->eof_reached) + if (url_feof(bc)) return AVERROR_EOF; if (frame_code == 'N') { tmp = frame_code; @@ -920,21 +1108,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, @@ -955,6 +1140,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; @@ -987,7 +1174,7 @@ 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); @@ -1019,6 +1206,7 @@ static int nut_read_close(AVFormatContext *s) 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, |