yading@11: /* yading@11: * "NUT" Container Format demuxer yading@11: * Copyright (c) 2004-2006 Michael Niedermayer yading@11: * Copyright (c) 2003 Alex Beregszaszi yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: #include "libavutil/avstring.h" yading@11: #include "libavutil/avassert.h" yading@11: #include "libavutil/bswap.h" yading@11: #include "libavutil/dict.h" yading@11: #include "libavutil/mathematics.h" yading@11: #include "libavutil/tree.h" yading@11: #include "avio_internal.h" yading@11: #include "nut.h" yading@11: #include "riff.h" yading@11: yading@11: #define NUT_MAX_STREAMS 256 /* arbitrary sanity check value */ yading@11: yading@11: static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index, yading@11: int64_t *pos_arg, int64_t pos_limit); yading@11: yading@11: static int get_str(AVIOContext *bc, char *string, unsigned int maxlen) yading@11: { yading@11: unsigned int len = ffio_read_varlen(bc); yading@11: yading@11: if (len && maxlen) yading@11: avio_read(bc, string, FFMIN(len, maxlen)); yading@11: while (len > maxlen) { yading@11: avio_r8(bc); yading@11: len--; yading@11: } yading@11: yading@11: if (maxlen) yading@11: string[FFMIN(len, maxlen - 1)] = 0; yading@11: yading@11: if (maxlen == len) yading@11: return -1; yading@11: else yading@11: return 0; yading@11: } yading@11: yading@11: static int64_t get_s(AVIOContext *bc) yading@11: { yading@11: int64_t v = ffio_read_varlen(bc) + 1; yading@11: yading@11: if (v & 1) yading@11: return -(v >> 1); yading@11: else yading@11: return (v >> 1); yading@11: } yading@11: yading@11: static uint64_t get_fourcc(AVIOContext *bc) yading@11: { yading@11: unsigned int len = ffio_read_varlen(bc); yading@11: yading@11: if (len == 2) yading@11: return avio_rl16(bc); yading@11: else if (len == 4) yading@11: return avio_rl32(bc); yading@11: else { yading@11: av_log(NULL, AV_LOG_ERROR, "Unsupported fourcc length %d\n", len); yading@11: return -1; yading@11: } yading@11: } yading@11: yading@11: #ifdef TRACE yading@11: static inline uint64_t get_v_trace(AVIOContext *bc, const char *file, yading@11: const char *func, int line) yading@11: { yading@11: uint64_t v = ffio_read_varlen(bc); yading@11: yading@11: av_log(NULL, AV_LOG_DEBUG, "get_v %5"PRId64" / %"PRIX64" in %s %s:%d\n", yading@11: v, v, file, func, line); yading@11: return v; yading@11: } yading@11: yading@11: static inline int64_t get_s_trace(AVIOContext *bc, const char *file, yading@11: const char *func, int line) yading@11: { yading@11: int64_t v = get_s(bc); yading@11: yading@11: av_log(NULL, AV_LOG_DEBUG, "get_s %5"PRId64" / %"PRIX64" in %s %s:%d\n", yading@11: v, v, file, func, line); yading@11: return v; yading@11: } yading@11: yading@11: static inline uint64_t get_4cc_trace(AVIOContext *bc, char *file, yading@11: char *func, int line) yading@11: { yading@11: uint64_t v = get_fourcc(bc); yading@11: yading@11: av_log(NULL, AV_LOG_DEBUG, "get_fourcc %5"PRId64" / %"PRIX64" in %s %s:%d\n", yading@11: v, v, file, func, line); yading@11: return v; yading@11: } yading@11: #define ffio_read_varlen(bc) get_v_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) yading@11: #define get_s(bc) get_s_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) yading@11: #define get_fourcc(bc) get_4cc_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) yading@11: #endif yading@11: yading@11: static int get_packetheader(NUTContext *nut, AVIOContext *bc, yading@11: int calculate_checksum, uint64_t startcode) yading@11: { yading@11: int64_t size; yading@11: // start = avio_tell(bc) - 8; yading@11: yading@11: startcode = av_be2ne64(startcode); yading@11: startcode = ff_crc04C11DB7_update(0, (uint8_t*) &startcode, 8); yading@11: yading@11: ffio_init_checksum(bc, ff_crc04C11DB7_update, startcode); yading@11: size = ffio_read_varlen(bc); yading@11: if (size > 4096) yading@11: avio_rb32(bc); yading@11: if (ffio_get_checksum(bc) && size > 4096) yading@11: return -1; yading@11: yading@11: ffio_init_checksum(bc, calculate_checksum ? ff_crc04C11DB7_update : NULL, 0); yading@11: yading@11: return size; yading@11: } yading@11: yading@11: static uint64_t find_any_startcode(AVIOContext *bc, int64_t pos) yading@11: { yading@11: uint64_t state = 0; yading@11: yading@11: if (pos >= 0) yading@11: /* Note, this may fail if the stream is not seekable, but that should yading@11: * not matter, as in this case we simply start where we currently are */ yading@11: avio_seek(bc, pos, SEEK_SET); yading@11: while (!url_feof(bc)) { yading@11: state = (state << 8) | avio_r8(bc); yading@11: if ((state >> 56) != 'N') yading@11: continue; yading@11: switch (state) { yading@11: case MAIN_STARTCODE: yading@11: case STREAM_STARTCODE: yading@11: case SYNCPOINT_STARTCODE: yading@11: case INFO_STARTCODE: yading@11: case INDEX_STARTCODE: yading@11: return state; yading@11: } yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: /** yading@11: * Find the given startcode. yading@11: * @param code the startcode yading@11: * @param pos the start position of the search, or -1 if the current position yading@11: * @return the position of the startcode or -1 if not found yading@11: */ yading@11: static int64_t find_startcode(AVIOContext *bc, uint64_t code, int64_t pos) yading@11: { yading@11: for (;;) { yading@11: uint64_t startcode = find_any_startcode(bc, pos); yading@11: if (startcode == code) yading@11: return avio_tell(bc) - 8; yading@11: else if (startcode == 0) yading@11: return -1; yading@11: pos = -1; yading@11: } yading@11: } yading@11: yading@11: static int nut_probe(AVProbeData *p) yading@11: { yading@11: int i; yading@11: uint64_t code = 0; yading@11: yading@11: for (i = 0; i < p->buf_size; i++) { yading@11: code = (code << 8) | p->buf[i]; yading@11: if (code == MAIN_STARTCODE) yading@11: return AVPROBE_SCORE_MAX; yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: #define GET_V(dst, check) \ yading@11: do { \ yading@11: tmp = ffio_read_varlen(bc); \ yading@11: if (!(check)) { \ yading@11: av_log(s, AV_LOG_ERROR, "Error " #dst " is (%"PRId64")\n", tmp); \ yading@11: return -1; \ yading@11: } \ yading@11: dst = tmp; \ yading@11: } while (0) yading@11: yading@11: static int skip_reserved(AVIOContext *bc, int64_t pos) yading@11: { yading@11: pos -= avio_tell(bc); yading@11: if (pos < 0) { yading@11: avio_seek(bc, pos, SEEK_CUR); yading@11: return -1; yading@11: } else { yading@11: while (pos--) yading@11: avio_r8(bc); yading@11: return 0; yading@11: } yading@11: } yading@11: yading@11: static int decode_main_header(NUTContext *nut) yading@11: { yading@11: AVFormatContext *s = nut->avf; yading@11: AVIOContext *bc = s->pb; yading@11: uint64_t tmp, end; yading@11: unsigned int stream_count; yading@11: int i, j, count; yading@11: int tmp_stream, tmp_mul, tmp_pts, tmp_size, tmp_res, tmp_head_idx; yading@11: yading@11: end = get_packetheader(nut, bc, 1, MAIN_STARTCODE); yading@11: end += avio_tell(bc); yading@11: yading@11: GET_V(tmp, tmp >= 2 && tmp <= 3); yading@11: GET_V(stream_count, tmp > 0 && tmp <= NUT_MAX_STREAMS); yading@11: yading@11: nut->max_distance = ffio_read_varlen(bc); yading@11: if (nut->max_distance > 65536) { yading@11: av_log(s, AV_LOG_DEBUG, "max_distance %d\n", nut->max_distance); yading@11: nut->max_distance = 65536; yading@11: } yading@11: yading@11: GET_V(nut->time_base_count, tmp > 0 && tmp < INT_MAX / sizeof(AVRational)); yading@11: nut->time_base = av_malloc(nut->time_base_count * sizeof(AVRational)); yading@11: yading@11: for (i = 0; i < nut->time_base_count; i++) { yading@11: GET_V(nut->time_base[i].num, tmp > 0 && tmp < (1ULL << 31)); yading@11: GET_V(nut->time_base[i].den, tmp > 0 && tmp < (1ULL << 31)); yading@11: if (av_gcd(nut->time_base[i].num, nut->time_base[i].den) != 1) { yading@11: av_log(s, AV_LOG_ERROR, "time base invalid\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: } yading@11: tmp_pts = 0; yading@11: tmp_mul = 1; yading@11: tmp_stream = 0; yading@11: tmp_head_idx = 0; yading@11: for (i = 0; i < 256;) { yading@11: int tmp_flags = ffio_read_varlen(bc); yading@11: int tmp_fields = ffio_read_varlen(bc); yading@11: yading@11: if (tmp_fields > 0) yading@11: tmp_pts = get_s(bc); yading@11: if (tmp_fields > 1) yading@11: tmp_mul = ffio_read_varlen(bc); yading@11: if (tmp_fields > 2) yading@11: tmp_stream = ffio_read_varlen(bc); yading@11: if (tmp_fields > 3) yading@11: tmp_size = ffio_read_varlen(bc); yading@11: else yading@11: tmp_size = 0; yading@11: if (tmp_fields > 4) yading@11: tmp_res = ffio_read_varlen(bc); yading@11: else yading@11: tmp_res = 0; yading@11: if (tmp_fields > 5) yading@11: count = ffio_read_varlen(bc); yading@11: else yading@11: count = tmp_mul - tmp_size; yading@11: if (tmp_fields > 6) yading@11: get_s(bc); yading@11: if (tmp_fields > 7) yading@11: tmp_head_idx = ffio_read_varlen(bc); yading@11: yading@11: while (tmp_fields-- > 8) yading@11: ffio_read_varlen(bc); yading@11: yading@11: if (count == 0 || i + count > 256) { yading@11: av_log(s, AV_LOG_ERROR, "illegal count %d at %d\n", count, i); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: if (tmp_stream >= stream_count) { yading@11: av_log(s, AV_LOG_ERROR, "illegal stream number\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: for (j = 0; j < count; j++, i++) { yading@11: if (i == 'N') { yading@11: nut->frame_code[i].flags = FLAG_INVALID; yading@11: j--; yading@11: continue; yading@11: } yading@11: nut->frame_code[i].flags = tmp_flags; yading@11: nut->frame_code[i].pts_delta = tmp_pts; yading@11: nut->frame_code[i].stream_id = tmp_stream; yading@11: nut->frame_code[i].size_mul = tmp_mul; yading@11: nut->frame_code[i].size_lsb = tmp_size + j; yading@11: nut->frame_code[i].reserved_count = tmp_res; yading@11: nut->frame_code[i].header_idx = tmp_head_idx; yading@11: } yading@11: } yading@11: av_assert0(nut->frame_code['N'].flags == FLAG_INVALID); yading@11: yading@11: if (end > avio_tell(bc) + 4) { yading@11: int rem = 1024; yading@11: GET_V(nut->header_count, tmp < 128U); yading@11: nut->header_count++; yading@11: for (i = 1; i < nut->header_count; i++) { yading@11: uint8_t *hdr; yading@11: GET_V(nut->header_len[i], tmp > 0 && tmp < 256); yading@11: rem -= nut->header_len[i]; yading@11: if (rem < 0) { yading@11: av_log(s, AV_LOG_ERROR, "invalid elision header\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: hdr = av_malloc(nut->header_len[i]); yading@11: if (!hdr) yading@11: return AVERROR(ENOMEM); yading@11: avio_read(bc, hdr, nut->header_len[i]); yading@11: nut->header[i] = hdr; yading@11: } yading@11: av_assert0(nut->header_len[0] == 0); yading@11: } yading@11: yading@11: if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { yading@11: av_log(s, AV_LOG_ERROR, "main header checksum mismatch\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: nut->stream = av_mallocz(sizeof(StreamContext) * stream_count); yading@11: for (i = 0; i < stream_count; i++) yading@11: avformat_new_stream(s, NULL); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int decode_stream_header(NUTContext *nut) yading@11: { yading@11: AVFormatContext *s = nut->avf; yading@11: AVIOContext *bc = s->pb; yading@11: StreamContext *stc; yading@11: int class, stream_id; yading@11: uint64_t tmp, end; yading@11: AVStream *st; yading@11: yading@11: end = get_packetheader(nut, bc, 1, STREAM_STARTCODE); yading@11: end += avio_tell(bc); yading@11: yading@11: GET_V(stream_id, tmp < s->nb_streams && !nut->stream[tmp].time_base); yading@11: stc = &nut->stream[stream_id]; yading@11: st = s->streams[stream_id]; yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: class = ffio_read_varlen(bc); yading@11: tmp = get_fourcc(bc); yading@11: st->codec->codec_tag = tmp; yading@11: switch (class) { yading@11: case 0: yading@11: st->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) { yading@11: ff_nut_video_tags, yading@11: ff_codec_bmp_tags, yading@11: 0 yading@11: }, yading@11: tmp); yading@11: break; yading@11: case 1: yading@11: st->codec->codec_type = AVMEDIA_TYPE_AUDIO; yading@11: st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) { yading@11: ff_nut_audio_tags, yading@11: ff_codec_wav_tags, yading@11: 0 yading@11: }, yading@11: tmp); yading@11: break; yading@11: case 2: yading@11: st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; yading@11: st->codec->codec_id = ff_codec_get_id(ff_nut_subtitle_tags, tmp); yading@11: break; yading@11: case 3: yading@11: st->codec->codec_type = AVMEDIA_TYPE_DATA; yading@11: st->codec->codec_id = ff_codec_get_id(ff_nut_data_tags, tmp); yading@11: break; yading@11: default: yading@11: av_log(s, AV_LOG_ERROR, "unknown stream class (%d)\n", class); yading@11: return -1; yading@11: } yading@11: if (class < 3 && st->codec->codec_id == AV_CODEC_ID_NONE) yading@11: av_log(s, AV_LOG_ERROR, yading@11: "Unknown codec tag '0x%04x' for stream number %d\n", yading@11: (unsigned int) tmp, stream_id); yading@11: yading@11: GET_V(stc->time_base_id, tmp < nut->time_base_count); yading@11: GET_V(stc->msb_pts_shift, tmp < 16); yading@11: stc->max_pts_distance = ffio_read_varlen(bc); yading@11: GET_V(stc->decode_delay, tmp < 1000); // sanity limit, raise this if Moore's law is true yading@11: st->codec->has_b_frames = stc->decode_delay; yading@11: ffio_read_varlen(bc); // stream flags yading@11: yading@11: GET_V(st->codec->extradata_size, tmp < (1 << 30)); yading@11: if (st->codec->extradata_size) { yading@11: st->codec->extradata = av_mallocz(st->codec->extradata_size + yading@11: FF_INPUT_BUFFER_PADDING_SIZE); yading@11: avio_read(bc, st->codec->extradata, st->codec->extradata_size); yading@11: } yading@11: yading@11: if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { yading@11: GET_V(st->codec->width, tmp > 0); yading@11: GET_V(st->codec->height, tmp > 0); yading@11: st->sample_aspect_ratio.num = ffio_read_varlen(bc); yading@11: st->sample_aspect_ratio.den = ffio_read_varlen(bc); yading@11: if ((!st->sample_aspect_ratio.num) != (!st->sample_aspect_ratio.den)) { yading@11: av_log(s, AV_LOG_ERROR, "invalid aspect ratio %d/%d\n", yading@11: st->sample_aspect_ratio.num, st->sample_aspect_ratio.den); yading@11: return -1; yading@11: } yading@11: ffio_read_varlen(bc); /* csp type */ yading@11: } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { yading@11: GET_V(st->codec->sample_rate, tmp > 0); yading@11: ffio_read_varlen(bc); // samplerate_den yading@11: GET_V(st->codec->channels, tmp > 0); yading@11: } yading@11: if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { yading@11: av_log(s, AV_LOG_ERROR, yading@11: "stream header %d checksum mismatch\n", stream_id); yading@11: return -1; yading@11: } yading@11: stc->time_base = &nut->time_base[stc->time_base_id]; yading@11: avpriv_set_pts_info(s->streams[stream_id], 63, stc->time_base->num, yading@11: stc->time_base->den); yading@11: return 0; yading@11: } yading@11: yading@11: static void set_disposition_bits(AVFormatContext *avf, char *value, yading@11: int stream_id) yading@11: { yading@11: int flag = 0, i; yading@11: yading@11: for (i = 0; ff_nut_dispositions[i].flag; ++i) yading@11: if (!strcmp(ff_nut_dispositions[i].str, value)) yading@11: flag = ff_nut_dispositions[i].flag; yading@11: if (!flag) yading@11: av_log(avf, AV_LOG_INFO, "unknown disposition type '%s'\n", value); yading@11: for (i = 0; i < avf->nb_streams; ++i) yading@11: if (stream_id == i || stream_id == -1) yading@11: avf->streams[i]->disposition |= flag; yading@11: } yading@11: yading@11: static int decode_info_header(NUTContext *nut) yading@11: { yading@11: AVFormatContext *s = nut->avf; yading@11: AVIOContext *bc = s->pb; yading@11: uint64_t tmp, chapter_start, chapter_len; yading@11: unsigned int stream_id_plus1, count; yading@11: int chapter_id, i; yading@11: int64_t value, end; yading@11: char name[256], str_value[1024], type_str[256]; yading@11: const char *type; yading@11: AVChapter *chapter = NULL; yading@11: AVStream *st = NULL; yading@11: AVDictionary **metadata = NULL; yading@11: yading@11: end = get_packetheader(nut, bc, 1, INFO_STARTCODE); yading@11: end += avio_tell(bc); yading@11: yading@11: GET_V(stream_id_plus1, tmp <= s->nb_streams); yading@11: chapter_id = get_s(bc); yading@11: chapter_start = ffio_read_varlen(bc); yading@11: chapter_len = ffio_read_varlen(bc); yading@11: count = ffio_read_varlen(bc); yading@11: yading@11: if (chapter_id && !stream_id_plus1) { yading@11: int64_t start = chapter_start / nut->time_base_count; yading@11: chapter = avpriv_new_chapter(s, chapter_id, yading@11: nut->time_base[chapter_start % yading@11: nut->time_base_count], yading@11: start, start + chapter_len, NULL); yading@11: metadata = &chapter->metadata; yading@11: } else if (stream_id_plus1) { yading@11: st = s->streams[stream_id_plus1 - 1]; yading@11: metadata = &st->metadata; yading@11: } else yading@11: metadata = &s->metadata; yading@11: yading@11: for (i = 0; i < count; i++) { yading@11: get_str(bc, name, sizeof(name)); yading@11: value = get_s(bc); yading@11: if (value == -1) { yading@11: type = "UTF-8"; yading@11: get_str(bc, str_value, sizeof(str_value)); yading@11: } else if (value == -2) { yading@11: get_str(bc, type_str, sizeof(type_str)); yading@11: type = type_str; yading@11: get_str(bc, str_value, sizeof(str_value)); yading@11: } else if (value == -3) { yading@11: type = "s"; yading@11: value = get_s(bc); yading@11: } else if (value == -4) { yading@11: type = "t"; yading@11: value = ffio_read_varlen(bc); yading@11: } else if (value < -4) { yading@11: type = "r"; yading@11: get_s(bc); yading@11: } else { yading@11: type = "v"; yading@11: } yading@11: yading@11: if (stream_id_plus1 > s->nb_streams) { yading@11: av_log(s, AV_LOG_ERROR, "invalid stream id for info packet\n"); yading@11: continue; yading@11: } yading@11: yading@11: if (!strcmp(type, "UTF-8")) { yading@11: if (chapter_id == 0 && !strcmp(name, "Disposition")) { yading@11: set_disposition_bits(s, str_value, stream_id_plus1 - 1); yading@11: continue; yading@11: } yading@11: yading@11: if (stream_id_plus1 && !strcmp(name, "r_frame_rate")) { yading@11: sscanf(str_value, "%d/%d", &st->r_frame_rate.num, &st->r_frame_rate.den); yading@11: if (st->r_frame_rate.num >= 1000LL*st->r_frame_rate.den) yading@11: st->r_frame_rate.num = st->r_frame_rate.den = 0; yading@11: continue; yading@11: } yading@11: yading@11: if (metadata && av_strcasecmp(name, "Uses") && yading@11: av_strcasecmp(name, "Depends") && av_strcasecmp(name, "Replaces")) yading@11: av_dict_set(metadata, name, str_value, 0); yading@11: } yading@11: } yading@11: yading@11: if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { yading@11: av_log(s, AV_LOG_ERROR, "info header checksum mismatch\n"); yading@11: return -1; yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) yading@11: { yading@11: AVFormatContext *s = nut->avf; yading@11: AVIOContext *bc = s->pb; yading@11: int64_t end; yading@11: uint64_t tmp; yading@11: yading@11: nut->last_syncpoint_pos = avio_tell(bc) - 8; yading@11: yading@11: end = get_packetheader(nut, bc, 1, SYNCPOINT_STARTCODE); yading@11: end += avio_tell(bc); yading@11: yading@11: tmp = ffio_read_varlen(bc); yading@11: *back_ptr = nut->last_syncpoint_pos - 16 * ffio_read_varlen(bc); yading@11: if (*back_ptr < 0) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count], yading@11: tmp / nut->time_base_count); yading@11: yading@11: if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { yading@11: av_log(s, AV_LOG_ERROR, "sync point checksum mismatch\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: *ts = tmp / nut->time_base_count * yading@11: av_q2d(nut->time_base[tmp % nut->time_base_count]) * AV_TIME_BASE; yading@11: ff_nut_add_sp(nut, nut->last_syncpoint_pos, *back_ptr, *ts); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: //FIXME calculate exactly, this is just a good approximation. yading@11: static int64_t find_duration(NUTContext *nut, int64_t filesize) yading@11: { yading@11: AVFormatContext *s = nut->avf; yading@11: int64_t duration = 0; yading@11: yading@11: int64_t pos = FFMAX(0, filesize - 2*nut->max_distance); yading@11: for(;;){ yading@11: int64_t ts = nut_read_timestamp(s, -1, &pos, INT64_MAX); yading@11: if(ts < 0) yading@11: break; yading@11: duration = FFMAX(duration, ts); yading@11: pos++; yading@11: } yading@11: if(duration > 0) yading@11: s->duration_estimation_method = AVFMT_DURATION_FROM_PTS; yading@11: return duration; yading@11: } yading@11: yading@11: static int find_and_decode_index(NUTContext *nut) yading@11: { yading@11: AVFormatContext *s = nut->avf; yading@11: AVIOContext *bc = s->pb; yading@11: uint64_t tmp, end; yading@11: int i, j, syncpoint_count; yading@11: int64_t filesize = avio_size(bc); yading@11: int64_t *syncpoints; yading@11: int8_t *has_keyframe; yading@11: int ret = -1; yading@11: yading@11: if(filesize <= 0) yading@11: return -1; yading@11: yading@11: avio_seek(bc, filesize - 12, SEEK_SET); yading@11: avio_seek(bc, filesize - avio_rb64(bc), SEEK_SET); yading@11: if (avio_rb64(bc) != INDEX_STARTCODE) { yading@11: av_log(s, AV_LOG_ERROR, "no index at the end\n"); yading@11: yading@11: if(s->duration<=0) yading@11: s->duration = find_duration(nut, filesize); yading@11: return -1; yading@11: } yading@11: yading@11: end = get_packetheader(nut, bc, 1, INDEX_STARTCODE); yading@11: end += avio_tell(bc); yading@11: yading@11: ffio_read_varlen(bc); // max_pts yading@11: GET_V(syncpoint_count, tmp < INT_MAX / 8 && tmp > 0); yading@11: syncpoints = av_malloc(sizeof(int64_t) * syncpoint_count); yading@11: has_keyframe = av_malloc(sizeof(int8_t) * (syncpoint_count + 1)); yading@11: for (i = 0; i < syncpoint_count; i++) { yading@11: syncpoints[i] = ffio_read_varlen(bc); yading@11: if (syncpoints[i] <= 0) yading@11: goto fail; yading@11: if (i) yading@11: syncpoints[i] += syncpoints[i - 1]; yading@11: } yading@11: yading@11: for (i = 0; i < s->nb_streams; i++) { yading@11: int64_t last_pts = -1; yading@11: for (j = 0; j < syncpoint_count;) { yading@11: uint64_t x = ffio_read_varlen(bc); yading@11: int type = x & 1; yading@11: int n = j; yading@11: x >>= 1; yading@11: if (type) { yading@11: int flag = x & 1; yading@11: x >>= 1; yading@11: if (n + x >= syncpoint_count + 1) { yading@11: av_log(s, AV_LOG_ERROR, "index overflow A %d + %"PRIu64" >= %d\n", n, x, syncpoint_count + 1); yading@11: goto fail; yading@11: } yading@11: while (x--) yading@11: has_keyframe[n++] = flag; yading@11: has_keyframe[n++] = !flag; yading@11: } else { yading@11: while (x != 1) { yading@11: if (n >= syncpoint_count + 1) { yading@11: av_log(s, AV_LOG_ERROR, "index overflow B\n"); yading@11: goto fail; yading@11: } yading@11: has_keyframe[n++] = x & 1; yading@11: x >>= 1; yading@11: } yading@11: } yading@11: if (has_keyframe[0]) { yading@11: av_log(s, AV_LOG_ERROR, "keyframe before first syncpoint in index\n"); yading@11: goto fail; yading@11: } yading@11: av_assert0(n <= syncpoint_count + 1); yading@11: for (; j < n && j < syncpoint_count; j++) { yading@11: if (has_keyframe[j]) { yading@11: uint64_t B, A = ffio_read_varlen(bc); yading@11: if (!A) { yading@11: A = ffio_read_varlen(bc); yading@11: B = ffio_read_varlen(bc); yading@11: // eor_pts[j][i] = last_pts + A + B yading@11: } else yading@11: B = 0; yading@11: av_add_index_entry(s->streams[i], 16 * syncpoints[j - 1], yading@11: last_pts + A, 0, 0, AVINDEX_KEYFRAME); yading@11: last_pts += A + B; yading@11: } yading@11: } yading@11: } yading@11: } yading@11: yading@11: if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { yading@11: av_log(s, AV_LOG_ERROR, "index checksum mismatch\n"); yading@11: goto fail; yading@11: } yading@11: ret = 0; yading@11: yading@11: fail: yading@11: av_free(syncpoints); yading@11: av_free(has_keyframe); yading@11: return ret; yading@11: } yading@11: yading@11: static int nut_read_header(AVFormatContext *s) yading@11: { yading@11: NUTContext *nut = s->priv_data; yading@11: AVIOContext *bc = s->pb; yading@11: int64_t pos; yading@11: int initialized_stream_count; yading@11: yading@11: nut->avf = s; yading@11: yading@11: /* main header */ yading@11: pos = 0; yading@11: do { yading@11: pos = find_startcode(bc, MAIN_STARTCODE, pos) + 1; yading@11: if (pos < 0 + 1) { yading@11: av_log(s, AV_LOG_ERROR, "No main startcode found.\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: } while (decode_main_header(nut) < 0); yading@11: yading@11: /* stream headers */ yading@11: pos = 0; yading@11: for (initialized_stream_count = 0; initialized_stream_count < s->nb_streams;) { yading@11: pos = find_startcode(bc, STREAM_STARTCODE, pos) + 1; yading@11: if (pos < 0 + 1) { yading@11: av_log(s, AV_LOG_ERROR, "Not all stream headers found.\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: if (decode_stream_header(nut) >= 0) yading@11: initialized_stream_count++; yading@11: } yading@11: yading@11: /* info headers */ yading@11: pos = 0; yading@11: for (;;) { yading@11: uint64_t startcode = find_any_startcode(bc, pos); yading@11: pos = avio_tell(bc); yading@11: yading@11: if (startcode == 0) { yading@11: av_log(s, AV_LOG_ERROR, "EOF before video frames\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } else if (startcode == SYNCPOINT_STARTCODE) { yading@11: nut->next_startcode = startcode; yading@11: break; yading@11: } else if (startcode != INFO_STARTCODE) { yading@11: continue; yading@11: } yading@11: yading@11: decode_info_header(nut); yading@11: } yading@11: yading@11: s->data_offset = pos - 8; yading@11: yading@11: if (bc->seekable) { yading@11: int64_t orig_pos = avio_tell(bc); yading@11: find_and_decode_index(nut); yading@11: avio_seek(bc, orig_pos, SEEK_SET); yading@11: } yading@11: av_assert0(nut->next_startcode == SYNCPOINT_STARTCODE); yading@11: yading@11: ff_metadata_conv_ctx(s, NULL, ff_nut_metadata_conv); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, yading@11: uint8_t *header_idx, int frame_code) yading@11: { yading@11: AVFormatContext *s = nut->avf; yading@11: AVIOContext *bc = s->pb; yading@11: StreamContext *stc; yading@11: int size, flags, size_mul, pts_delta, i, reserved_count; yading@11: uint64_t tmp; yading@11: yading@11: if (avio_tell(bc) > nut->last_syncpoint_pos + nut->max_distance) { yading@11: av_log(s, AV_LOG_ERROR, yading@11: "Last frame must have been damaged %"PRId64" > %"PRId64" + %d\n", yading@11: avio_tell(bc), nut->last_syncpoint_pos, nut->max_distance); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: flags = nut->frame_code[frame_code].flags; yading@11: size_mul = nut->frame_code[frame_code].size_mul; yading@11: size = nut->frame_code[frame_code].size_lsb; yading@11: *stream_id = nut->frame_code[frame_code].stream_id; yading@11: pts_delta = nut->frame_code[frame_code].pts_delta; yading@11: reserved_count = nut->frame_code[frame_code].reserved_count; yading@11: *header_idx = nut->frame_code[frame_code].header_idx; yading@11: yading@11: if (flags & FLAG_INVALID) yading@11: return AVERROR_INVALIDDATA; yading@11: if (flags & FLAG_CODED) yading@11: flags ^= ffio_read_varlen(bc); yading@11: if (flags & FLAG_STREAM_ID) { yading@11: GET_V(*stream_id, tmp < s->nb_streams); yading@11: } yading@11: stc = &nut->stream[*stream_id]; yading@11: if (flags & FLAG_CODED_PTS) { yading@11: int coded_pts = ffio_read_varlen(bc); yading@11: // FIXME check last_pts validity? yading@11: if (coded_pts < (1 << stc->msb_pts_shift)) { yading@11: *pts = ff_lsb2full(stc, coded_pts); yading@11: } else yading@11: *pts = coded_pts - (1LL << stc->msb_pts_shift); yading@11: } else yading@11: *pts = stc->last_pts + pts_delta; yading@11: if (flags & FLAG_SIZE_MSB) yading@11: size += size_mul * ffio_read_varlen(bc); yading@11: if (flags & FLAG_MATCH_TIME) yading@11: get_s(bc); yading@11: if (flags & FLAG_HEADER_IDX) yading@11: *header_idx = ffio_read_varlen(bc); yading@11: if (flags & FLAG_RESERVED) yading@11: reserved_count = ffio_read_varlen(bc); yading@11: for (i = 0; i < reserved_count; i++) yading@11: ffio_read_varlen(bc); yading@11: yading@11: if (*header_idx >= (unsigned)nut->header_count) { yading@11: av_log(s, AV_LOG_ERROR, "header_idx invalid\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: if (size > 4096) yading@11: *header_idx = 0; yading@11: size -= nut->header_len[*header_idx]; yading@11: yading@11: if (flags & FLAG_CHECKSUM) { yading@11: avio_rb32(bc); // FIXME check this yading@11: } else if (size > 2 * nut->max_distance || FFABS(stc->last_pts - *pts) > yading@11: stc->max_pts_distance) { yading@11: av_log(s, AV_LOG_ERROR, "frame size > 2max_distance and no checksum\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: stc->last_pts = *pts; yading@11: stc->last_flags = flags; yading@11: yading@11: return size; yading@11: } yading@11: yading@11: static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) yading@11: { yading@11: AVFormatContext *s = nut->avf; yading@11: AVIOContext *bc = s->pb; yading@11: int size, stream_id, discard; yading@11: int64_t pts, last_IP_pts; yading@11: StreamContext *stc; yading@11: uint8_t header_idx; yading@11: yading@11: size = decode_frame_header(nut, &pts, &stream_id, &header_idx, frame_code); yading@11: if (size < 0) yading@11: return size; yading@11: yading@11: stc = &nut->stream[stream_id]; yading@11: yading@11: if (stc->last_flags & FLAG_KEY) yading@11: stc->skip_until_key_frame = 0; yading@11: yading@11: discard = s->streams[stream_id]->discard; yading@11: last_IP_pts = s->streams[stream_id]->last_IP_pts; yading@11: if ((discard >= AVDISCARD_NONKEY && !(stc->last_flags & FLAG_KEY)) || yading@11: (discard >= AVDISCARD_BIDIR && last_IP_pts != AV_NOPTS_VALUE && yading@11: last_IP_pts > pts) || yading@11: discard >= AVDISCARD_ALL || yading@11: stc->skip_until_key_frame) { yading@11: avio_skip(bc, size); yading@11: return 1; yading@11: } yading@11: yading@11: if (av_new_packet(pkt, size + nut->header_len[header_idx]) < 0) yading@11: return AVERROR(ENOMEM); yading@11: memcpy(pkt->data, nut->header[header_idx], nut->header_len[header_idx]); yading@11: pkt->pos = avio_tell(bc); // FIXME yading@11: avio_read(bc, pkt->data + nut->header_len[header_idx], size); yading@11: yading@11: pkt->stream_index = stream_id; yading@11: if (stc->last_flags & FLAG_KEY) yading@11: pkt->flags |= AV_PKT_FLAG_KEY; yading@11: pkt->pts = pts; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) yading@11: { yading@11: NUTContext *nut = s->priv_data; yading@11: AVIOContext *bc = s->pb; yading@11: int i, frame_code = 0, ret, skip; yading@11: int64_t ts, back_ptr; yading@11: yading@11: for (;;) { yading@11: int64_t pos = avio_tell(bc); yading@11: uint64_t tmp = nut->next_startcode; yading@11: nut->next_startcode = 0; yading@11: yading@11: if (tmp) { yading@11: pos -= 8; yading@11: } else { yading@11: frame_code = avio_r8(bc); yading@11: if (url_feof(bc)) yading@11: return -1; yading@11: if (frame_code == 'N') { yading@11: tmp = frame_code; yading@11: for (i = 1; i < 8; i++) yading@11: tmp = (tmp << 8) + avio_r8(bc); yading@11: } yading@11: } yading@11: switch (tmp) { yading@11: case MAIN_STARTCODE: yading@11: case STREAM_STARTCODE: yading@11: case INDEX_STARTCODE: yading@11: skip = get_packetheader(nut, bc, 0, tmp); yading@11: avio_skip(bc, skip); yading@11: break; yading@11: case INFO_STARTCODE: yading@11: if (decode_info_header(nut) < 0) yading@11: goto resync; yading@11: break; yading@11: case SYNCPOINT_STARTCODE: yading@11: if (decode_syncpoint(nut, &ts, &back_ptr) < 0) yading@11: goto resync; yading@11: frame_code = avio_r8(bc); yading@11: case 0: yading@11: ret = decode_frame(nut, pkt, frame_code); yading@11: if (ret == 0) yading@11: return 0; yading@11: else if (ret == 1) // OK but discard packet yading@11: break; yading@11: default: yading@11: resync: yading@11: av_log(s, AV_LOG_DEBUG, "syncing from %"PRId64"\n", pos); yading@11: tmp = find_any_startcode(bc, nut->last_syncpoint_pos + 1); yading@11: if (tmp == 0) yading@11: return AVERROR_INVALIDDATA; yading@11: av_log(s, AV_LOG_DEBUG, "sync\n"); yading@11: nut->next_startcode = tmp; yading@11: } yading@11: } yading@11: } yading@11: yading@11: static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index, yading@11: int64_t *pos_arg, int64_t pos_limit) yading@11: { yading@11: NUTContext *nut = s->priv_data; yading@11: AVIOContext *bc = s->pb; yading@11: int64_t pos, pts, back_ptr; yading@11: av_log(s, AV_LOG_DEBUG, "read_timestamp(X,%d,%"PRId64",%"PRId64")\n", yading@11: stream_index, *pos_arg, pos_limit); yading@11: yading@11: pos = *pos_arg; yading@11: do { yading@11: pos = find_startcode(bc, SYNCPOINT_STARTCODE, pos) + 1; yading@11: if (pos < 1) { yading@11: av_log(s, AV_LOG_ERROR, "read_timestamp failed.\n"); yading@11: return AV_NOPTS_VALUE; yading@11: } yading@11: } while (decode_syncpoint(nut, &pts, &back_ptr) < 0); yading@11: *pos_arg = pos - 1; yading@11: av_assert0(nut->last_syncpoint_pos == *pos_arg); yading@11: yading@11: av_log(s, AV_LOG_DEBUG, "return %"PRId64" %"PRId64"\n", pts, back_ptr); yading@11: if (stream_index == -2) yading@11: return back_ptr; yading@11: av_assert0(stream_index == -1); yading@11: return pts; yading@11: } yading@11: yading@11: static int read_seek(AVFormatContext *s, int stream_index, yading@11: int64_t pts, int flags) yading@11: { yading@11: NUTContext *nut = s->priv_data; yading@11: AVStream *st = s->streams[stream_index]; yading@11: Syncpoint dummy = { .ts = pts * av_q2d(st->time_base) * AV_TIME_BASE }; yading@11: Syncpoint nopts_sp = { .ts = AV_NOPTS_VALUE, .back_ptr = AV_NOPTS_VALUE }; yading@11: Syncpoint *sp, *next_node[2] = { &nopts_sp, &nopts_sp }; yading@11: int64_t pos, pos2, ts; yading@11: int i; yading@11: yading@11: if (st->index_entries) { yading@11: int index = av_index_search_timestamp(st, pts, flags); yading@11: if (index < 0) yading@11: index = av_index_search_timestamp(st, pts, flags ^ AVSEEK_FLAG_BACKWARD); yading@11: if (index < 0) yading@11: return -1; yading@11: yading@11: pos2 = st->index_entries[index].pos; yading@11: ts = st->index_entries[index].timestamp; yading@11: } else { yading@11: av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pts_cmp, yading@11: (void **) next_node); yading@11: av_log(s, AV_LOG_DEBUG, "%"PRIu64"-%"PRIu64" %"PRId64"-%"PRId64"\n", yading@11: next_node[0]->pos, next_node[1]->pos, next_node[0]->ts, yading@11: next_node[1]->ts); yading@11: pos = ff_gen_search(s, -1, dummy.ts, next_node[0]->pos, yading@11: next_node[1]->pos, next_node[1]->pos, yading@11: next_node[0]->ts, next_node[1]->ts, yading@11: AVSEEK_FLAG_BACKWARD, &ts, nut_read_timestamp); yading@11: yading@11: if (!(flags & AVSEEK_FLAG_BACKWARD)) { yading@11: dummy.pos = pos + 16; yading@11: next_node[1] = &nopts_sp; yading@11: av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp, yading@11: (void **) next_node); yading@11: pos2 = ff_gen_search(s, -2, dummy.pos, next_node[0]->pos, yading@11: next_node[1]->pos, next_node[1]->pos, yading@11: next_node[0]->back_ptr, next_node[1]->back_ptr, yading@11: flags, &ts, nut_read_timestamp); yading@11: if (pos2 >= 0) yading@11: pos = pos2; yading@11: // FIXME dir but I think it does not matter yading@11: } yading@11: dummy.pos = pos; yading@11: sp = av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp, yading@11: NULL); yading@11: yading@11: av_assert0(sp); yading@11: pos2 = sp->back_ptr - 15; yading@11: } yading@11: av_log(NULL, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos2); yading@11: pos = find_startcode(s->pb, SYNCPOINT_STARTCODE, pos2); yading@11: avio_seek(s->pb, pos, SEEK_SET); yading@11: av_log(NULL, AV_LOG_DEBUG, "SP: %"PRId64"\n", pos); yading@11: if (pos2 > pos || pos2 + 15 < pos) yading@11: av_log(NULL, AV_LOG_ERROR, "no syncpoint at backptr pos\n"); yading@11: for (i = 0; i < s->nb_streams; i++) yading@11: nut->stream[i].skip_until_key_frame = 1; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int nut_read_close(AVFormatContext *s) yading@11: { yading@11: NUTContext *nut = s->priv_data; yading@11: int i; yading@11: yading@11: av_freep(&nut->time_base); yading@11: av_freep(&nut->stream); yading@11: ff_nut_free_sp(nut); yading@11: for (i = 1; i < nut->header_count; i++) yading@11: av_freep(&nut->header[i]); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: AVInputFormat ff_nut_demuxer = { yading@11: .name = "nut", yading@11: .long_name = NULL_IF_CONFIG_SMALL("NUT"), yading@11: .flags = AVFMT_SEEK_TO_PTS, yading@11: .priv_data_size = sizeof(NUTContext), yading@11: .read_probe = nut_probe, yading@11: .read_header = nut_read_header, yading@11: .read_packet = nut_read_packet, yading@11: .read_close = nut_read_close, yading@11: .read_seek = read_seek, yading@11: .extensions = "nut", yading@11: .codec_tag = ff_nut_codec_tags, yading@11: };