X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmpc8.c;h=3564e81c3231855db2c5abc0f58631301e42714c;hb=f929ab0569ff31ed5a59b0b0adb7ce09df3fca39;hp=b6840b00d64c07c4a23f96d47041ad67d83844a1;hpb=12877faf7c3af83d60dc185cf78f0ee5a3c0f67d;p=ffmpeg diff --git a/libavformat/mpc8.c b/libavformat/mpc8.c index b6840b00d64..3564e81c323 100644 --- a/libavformat/mpc8.c +++ b/libavformat/mpc8.c @@ -2,25 +2,29 @@ * Musepack SV8 demuxer * Copyright (c) 2007 Konstantin Shishkov * - * This file is part of FFmpeg. + * This file is part of Libav. * - * FFmpeg is free software; you can redistribute it and/or + * Libav 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. * - * FFmpeg is distributed in the hope that it will be useful, + * Libav 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 FFmpeg; if not, write to the Free Software + * License along with Libav; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "libavcodec/get_bits.h" +#include "libavcodec/unary.h" +#include "apetag.h" #include "avformat.h" -#include "bitstream.h" -#include "unary.h" +#include "internal.h" +#include "avio_internal.h" /// Two-byte MPC tag #define MKMPCTAG(a, b) (a | (b << 8)) @@ -45,15 +49,60 @@ static const int mpc8_rate[8] = { 44100, 48000, 37800, 32000, -1, -1, -1, -1 }; typedef struct { int ver; - int frame; int64_t header_pos; int64_t samples; + + int64_t apetag_start; } MPCContext; +static inline int64_t bs_get_v(uint8_t **bs) +{ + int64_t v = 0; + int br = 0; + int c; + + do { + c = **bs; (*bs)++; + v <<= 7; + v |= c & 0x7F; + br++; + if (br > 10) + return -1; + } while (c & 0x80); + + return v - br; +} + static int mpc8_probe(AVProbeData *p) { - if (AV_RL32(p->buf) == TAG_MPCK) - return AVPROBE_SCORE_MAX; + uint8_t *bs = p->buf + 4; + uint8_t *bs_end = bs + p->buf_size; + int64_t size; + + if (p->buf_size < 16) + return 0; + if (AV_RL32(p->buf) != TAG_MPCK) + return 0; + while (bs < bs_end + 3) { + int header_found = (bs[0] == 'S' && bs[1] == 'H'); + if (bs[0] < 'A' || bs[0] > 'Z' || bs[1] < 'A' || bs[1] > 'Z') + return 0; + bs += 2; + size = bs_get_v(&bs); + if (size < 2) + return 0; + if (bs + size - 2 >= bs_end) + return AVPROBE_SCORE_EXTENSION - 1; // seems to be valid MPC but no header yet + if (header_found) { + if (size < 11 || size > 28) + return 0; + if (!AV_RL32(bs)) //zero CRC is invalid + return 0; + return AVPROBE_SCORE_MAX; + } else { + bs += size - 2; + } + } return 0; } @@ -72,13 +121,13 @@ static inline int64_t gb_get_v(GetBitContext *gb) return v; } -static void mpc8_get_chunk_header(ByteIOContext *pb, int *tag, int64_t *size) +static void mpc8_get_chunk_header(AVIOContext *pb, int *tag, int64_t *size) { int64_t pos; - pos = url_ftell(pb); - *tag = get_le16(pb); - *size = ff_get_v(pb); - *size -= url_ftell(pb) - pos; + pos = avio_tell(pb); + *tag = avio_rl16(pb); + *size = ffio_read_varlen(pb); + *size -= avio_tell(pb) - pos; } static void mpc8_parse_seektable(AVFormatContext *s, int64_t off) @@ -90,15 +139,24 @@ static void mpc8_parse_seektable(AVFormatContext *s, int64_t off) int i, t, seekd; GetBitContext gb; - url_fseek(&s->pb, off, SEEK_SET); - mpc8_get_chunk_header(&s->pb, &tag, &size); + if (s->nb_streams == 0) { + av_log(s, AV_LOG_ERROR, "No stream added before parsing seek table\n"); + return; + } + + avio_seek(s->pb, off, SEEK_SET); + mpc8_get_chunk_header(s->pb, &tag, &size); if(tag != TAG_SEEKTABLE){ av_log(s, AV_LOG_ERROR, "No seek table at given position\n"); return; } - if(!(buf = av_malloc(size))) + if (size < 0 || size >= INT_MAX / 2) { + av_log(s, AV_LOG_ERROR, "Bad seek table size\n"); + return; + } + if(!(buf = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE))) return; - get_buffer(&s->pb, buf, size); + avio_read(s->pb, buf, size); init_get_bits(&gb, buf, size * 8); size = gb_get_v(&gb); if(size > UINT_MAX/4 || size > c->samples/1152){ @@ -126,37 +184,37 @@ static void mpc8_parse_seektable(AVFormatContext *s, int64_t off) static void mpc8_handle_chunk(AVFormatContext *s, int tag, int64_t chunk_pos, int64_t size) { - ByteIOContext *pb = &s->pb; + AVIOContext *pb = s->pb; int64_t pos, off; switch(tag){ case TAG_SEEKTBLOFF: - pos = url_ftell(pb) + size; - off = ff_get_v(pb); + pos = avio_tell(pb) + size; + off = ffio_read_varlen(pb); mpc8_parse_seektable(s, chunk_pos + off); - url_fseek(pb, pos, SEEK_SET); + avio_seek(pb, pos, SEEK_SET); break; default: - url_fskip(pb, size); + avio_skip(pb, size); } } -static int mpc8_read_header(AVFormatContext *s, AVFormatParameters *ap) +static int mpc8_read_header(AVFormatContext *s) { MPCContext *c = s->priv_data; - ByteIOContext *pb = &s->pb; + AVIOContext *pb = s->pb; AVStream *st; int tag = 0; int64_t size, pos; - c->header_pos = url_ftell(pb); - if(get_le32(pb) != TAG_MPCK){ + c->header_pos = avio_tell(pb); + if(avio_rl32(pb) != TAG_MPCK){ av_log(s, AV_LOG_ERROR, "Not a Musepack8 file\n"); - return -1; + return AVERROR_INVALIDDATA; } - while(!url_feof(pb)){ - pos = url_ftell(pb); + while(!pb->eof_reached){ + pos = avio_tell(pb); mpc8_get_chunk_header(pb, &tag, &size); if(tag == TAG_STREAMHDR) break; @@ -164,34 +222,41 @@ static int mpc8_read_header(AVFormatContext *s, AVFormatParameters *ap) } if(tag != TAG_STREAMHDR){ av_log(s, AV_LOG_ERROR, "Stream header not found\n"); - return -1; + return AVERROR_INVALIDDATA; } - pos = url_ftell(pb); - url_fskip(pb, 4); //CRC - c->ver = get_byte(pb); + pos = avio_tell(pb); + avio_skip(pb, 4); //CRC + c->ver = avio_r8(pb); if(c->ver != 8){ av_log(s, AV_LOG_ERROR, "Unknown stream version %d\n", c->ver); - return -1; + return AVERROR_PATCHWELCOME; } - c->samples = ff_get_v(pb); - ff_get_v(pb); //silence samples at the beginning + c->samples = ffio_read_varlen(pb); + ffio_read_varlen(pb); //silence samples at the beginning - st = av_new_stream(s, 0); + st = avformat_new_stream(s, NULL); if (!st) return AVERROR(ENOMEM); - st->codec->codec_type = CODEC_TYPE_AUDIO; - st->codec->codec_id = CODEC_ID_MUSEPACK8; - st->codec->bits_per_sample = 16; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_MUSEPACK8; + st->codec->bits_per_coded_sample = 16; st->codec->extradata_size = 2; st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); - get_buffer(pb, st->codec->extradata, st->codec->extradata_size); + avio_read(pb, st->codec->extradata, st->codec->extradata_size); st->codec->channels = (st->codec->extradata[1] >> 4) + 1; st->codec->sample_rate = mpc8_rate[st->codec->extradata[0] >> 5]; - av_set_pts_info(st, 32, 1152 << (st->codec->extradata[1]&3)*2, st->codec->sample_rate); + avpriv_set_pts_info(st, 32, 1152 << (st->codec->extradata[1]&3)*2, st->codec->sample_rate); + st->start_time = 0; st->duration = c->samples / (1152 << (st->codec->extradata[1]&3)*2); - size -= url_ftell(pb) - pos; + size -= avio_tell(pb) - pos; + + if (pb->seekable) { + int64_t pos = avio_tell(s->pb); + c->apetag_start = ff_ape_parse_tag(s); + avio_seek(s->pb, pos, SEEK_SET); + } return 0; } @@ -202,43 +267,49 @@ static int mpc8_read_packet(AVFormatContext *s, AVPacket *pkt) int tag; int64_t pos, size; - while(!url_feof(&s->pb)){ - pos = url_ftell(&s->pb); - mpc8_get_chunk_header(&s->pb, &tag, &size); + while(!s->pb->eof_reached){ + pos = avio_tell(s->pb); + + /* don't return bogus packets with the ape tag data */ + if (c->apetag_start && pos >= c->apetag_start) + return AVERROR_EOF; + + mpc8_get_chunk_header(s->pb, &tag, &size); + if (size < 0) + return -1; if(tag == TAG_AUDIOPACKET){ - if(av_get_packet(&s->pb, pkt, size) < 0) + if(av_get_packet(s->pb, pkt, size) < 0) return AVERROR(ENOMEM); pkt->stream_index = 0; - pkt->pts = c->frame; + pkt->duration = 1; return 0; } if(tag == TAG_STREAMEND) return AVERROR(EIO); mpc8_handle_chunk(s, tag, pos, size); } - return 0; + return AVERROR_EOF; } static int mpc8_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { AVStream *st = s->streams[stream_index]; - MPCContext *c = s->priv_data; int index = av_index_search_timestamp(st, timestamp, flags); if(index < 0) return -1; - url_fseek(&s->pb, st->index_entries[index].pos, SEEK_SET); - c->frame = st->index_entries[index].timestamp; + if (avio_seek(s->pb, st->index_entries[index].pos, SEEK_SET) < 0) + return -1; + ff_update_cur_dts(s, st, st->index_entries[index].timestamp); return 0; } -AVInputFormat mpc8_demuxer = { - "mpc8", - "musepack8", - sizeof(MPCContext), - mpc8_probe, - mpc8_read_header, - mpc8_read_packet, - NULL, - mpc8_read_seek, +AVInputFormat ff_mpc8_demuxer = { + .name = "mpc8", + .long_name = NULL_IF_CONFIG_SMALL("Musepack SV8"), + .priv_data_size = sizeof(MPCContext), + .read_probe = mpc8_probe, + .read_header = mpc8_read_header, + .read_packet = mpc8_read_packet, + .read_seek = mpc8_read_seek, };