1/* 2 * WavPack demuxer 3 * Copyright (c) 2006 Konstantin Shishkov 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include "libavutil/intreadwrite.h" 23#include "avformat.h" 24 25// specs say that maximum block size is 1Mb 26#define WV_BLOCK_LIMIT 1047576 27 28#define WV_EXTRA_SIZE 12 29 30enum WV_FLAGS{ 31 WV_MONO = 0x0004, 32 WV_HYBRID = 0x0008, 33 WV_JOINT = 0x0010, 34 WV_CROSSD = 0x0020, 35 WV_HSHAPE = 0x0040, 36 WV_FLOAT = 0x0080, 37 WV_INT32 = 0x0100, 38 WV_HBR = 0x0200, 39 WV_HBAL = 0x0400, 40 WV_MCINIT = 0x0800, 41 WV_MCEND = 0x1000, 42}; 43 44static const int wv_rates[16] = { 45 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, 46 32000, 44100, 48000, 64000, 88200, 96000, 192000, -1 47}; 48 49typedef struct{ 50 uint32_t blksize, flags; 51 int rate, chan, bpp; 52 uint32_t samples, soff; 53 int block_parsed; 54 uint8_t extra[WV_EXTRA_SIZE]; 55 int64_t pos; 56}WVContext; 57 58static int wv_probe(AVProbeData *p) 59{ 60 /* check file header */ 61 if (p->buf_size <= 32) 62 return 0; 63 if (p->buf[0] == 'w' && p->buf[1] == 'v' && 64 p->buf[2] == 'p' && p->buf[3] == 'k') 65 return AVPROBE_SCORE_MAX; 66 else 67 return 0; 68} 69 70static int wv_read_block_header(AVFormatContext *ctx, ByteIOContext *pb) 71{ 72 WVContext *wc = ctx->priv_data; 73 uint32_t tag, ver; 74 int size; 75 int rate, bpp, chan; 76 77 wc->pos = url_ftell(pb); 78 tag = get_le32(pb); 79 if (tag != MKTAG('w', 'v', 'p', 'k')) 80 return -1; 81 size = get_le32(pb); 82 if(size < 24 || size > WV_BLOCK_LIMIT){ 83 av_log(ctx, AV_LOG_ERROR, "Incorrect block size %i\n", size); 84 return -1; 85 } 86 wc->blksize = size; 87 ver = get_le16(pb); 88 if(ver < 0x402 || ver > 0x410){ 89 av_log(ctx, AV_LOG_ERROR, "Unsupported version %03X\n", ver); 90 return -1; 91 } 92 get_byte(pb); // track no 93 get_byte(pb); // track sub index 94 wc->samples = get_le32(pb); // total samples in file 95 wc->soff = get_le32(pb); // offset in samples of current block 96 get_buffer(pb, wc->extra, WV_EXTRA_SIZE); 97 wc->flags = AV_RL32(wc->extra + 4); 98 //parse flags 99 if(wc->flags & WV_FLOAT){ 100 av_log(ctx, AV_LOG_ERROR, "Floating point data is not supported\n"); 101 return -1; 102 } 103 104 bpp = ((wc->flags & 3) + 1) << 3; 105 chan = 1 + !(wc->flags & WV_MONO); 106 rate = wv_rates[(wc->flags >> 23) & 0xF]; 107 if(rate == -1){ 108 av_log(ctx, AV_LOG_ERROR, "Unknown sampling rate\n"); 109 return -1; 110 } 111 if(!wc->bpp) wc->bpp = bpp; 112 if(!wc->chan) wc->chan = chan; 113 if(!wc->rate) wc->rate = rate; 114 115 if(wc->flags && bpp != wc->bpp){ 116 av_log(ctx, AV_LOG_ERROR, "Bits per sample differ, this block: %i, header block: %i\n", bpp, wc->bpp); 117 return -1; 118 } 119 if(wc->flags && chan != wc->chan){ 120 av_log(ctx, AV_LOG_ERROR, "Channels differ, this block: %i, header block: %i\n", chan, wc->chan); 121 return -1; 122 } 123 if(wc->flags && rate != wc->rate){ 124 av_log(ctx, AV_LOG_ERROR, "Sampling rate differ, this block: %i, header block: %i\n", rate, wc->rate); 125 return -1; 126 } 127 wc->blksize = size - 24; 128 return 0; 129} 130 131static int wv_read_header(AVFormatContext *s, 132 AVFormatParameters *ap) 133{ 134 ByteIOContext *pb = s->pb; 135 WVContext *wc = s->priv_data; 136 AVStream *st; 137 138 if(wv_read_block_header(s, pb) < 0) 139 return -1; 140 141 wc->block_parsed = 0; 142 /* now we are ready: build format streams */ 143 st = av_new_stream(s, 0); 144 if (!st) 145 return -1; 146 st->codec->codec_type = CODEC_TYPE_AUDIO; 147 st->codec->codec_id = CODEC_ID_WAVPACK; 148 st->codec->channels = wc->chan; 149 st->codec->sample_rate = wc->rate; 150 st->codec->bits_per_coded_sample = wc->bpp; 151 av_set_pts_info(st, 64, 1, wc->rate); 152 s->start_time = 0; 153 s->duration = (int64_t)wc->samples * AV_TIME_BASE / st->codec->sample_rate; 154 return 0; 155} 156 157static int wv_read_packet(AVFormatContext *s, 158 AVPacket *pkt) 159{ 160 WVContext *wc = s->priv_data; 161 int ret; 162 163 if (url_feof(s->pb)) 164 return AVERROR(EIO); 165 if(wc->block_parsed){ 166 if(wv_read_block_header(s, s->pb) < 0) 167 return -1; 168 } 169 170 if(av_new_packet(pkt, wc->blksize + WV_EXTRA_SIZE) < 0) 171 return AVERROR(ENOMEM); 172 memcpy(pkt->data, wc->extra, WV_EXTRA_SIZE); 173 ret = get_buffer(s->pb, pkt->data + WV_EXTRA_SIZE, wc->blksize); 174 if(ret != wc->blksize){ 175 av_free_packet(pkt); 176 return AVERROR(EIO); 177 } 178 pkt->stream_index = 0; 179 wc->block_parsed = 1; 180 pkt->size = ret + WV_EXTRA_SIZE; 181 pkt->pts = wc->soff; 182 av_add_index_entry(s->streams[0], wc->pos, pkt->pts, 0, 0, AVINDEX_KEYFRAME); 183 return 0; 184} 185 186static int wv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) 187{ 188 AVStream *st = s->streams[stream_index]; 189 WVContext *wc = s->priv_data; 190 AVPacket pkt1, *pkt = &pkt1; 191 int ret; 192 int index = av_index_search_timestamp(st, timestamp, flags); 193 int64_t pos, pts; 194 195 /* if found, seek there */ 196 if (index >= 0){ 197 wc->block_parsed = 1; 198 url_fseek(s->pb, st->index_entries[index].pos, SEEK_SET); 199 return 0; 200 } 201 /* if timestamp is out of bounds, return error */ 202 if(timestamp < 0 || timestamp >= s->duration) 203 return -1; 204 205 pos = url_ftell(s->pb); 206 do{ 207 ret = av_read_frame(s, pkt); 208 if (ret < 0){ 209 url_fseek(s->pb, pos, SEEK_SET); 210 return -1; 211 } 212 pts = pkt->pts; 213 av_free_packet(pkt); 214 }while(pts < timestamp); 215 return 0; 216} 217 218AVInputFormat wv_demuxer = { 219 "wv", 220 NULL_IF_CONFIG_SMALL("WavPack"), 221 sizeof(WVContext), 222 wv_probe, 223 wv_read_header, 224 wv_read_packet, 225 NULL, 226 wv_read_seek, 227}; 228