| @@ -36,7 +36,7 @@ | |||
| #define WV_END_BLOCK 0x1000 | |||
| #define WV_SINGLE_BLOCK (WV_START_BLOCK | WV_END_BLOCK) | |||
| enum WV_FLAGS{ | |||
| enum WV_FLAGS { | |||
| WV_MONO = 0x0004, | |||
| WV_HYBRID = 0x0008, | |||
| WV_JOINT = 0x0010, | |||
| @@ -51,11 +51,11 @@ enum WV_FLAGS{ | |||
| }; | |||
| static const int wv_rates[16] = { | |||
| 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, | |||
| 32000, 44100, 48000, 64000, 88200, 96000, 192000, -1 | |||
| 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, | |||
| 32000, 44100, 48000, 64000, 88200, 96000, 192000, -1 | |||
| }; | |||
| typedef struct{ | |||
| typedef struct { | |||
| uint32_t blksize, flags; | |||
| int rate, chan, bpp; | |||
| uint32_t chmask; | |||
| @@ -64,7 +64,7 @@ typedef struct{ | |||
| int block_parsed; | |||
| uint8_t extra[WV_EXTRA_SIZE]; | |||
| int64_t pos; | |||
| }WVContext; | |||
| } WVContext; | |||
| static int wv_probe(AVProbeData *p) | |||
| { | |||
| @@ -78,7 +78,8 @@ static int wv_probe(AVProbeData *p) | |||
| return 0; | |||
| } | |||
| static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb, int append) | |||
| static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb, | |||
| int append) | |||
| { | |||
| WVContext *wc = ctx->priv_data; | |||
| uint32_t tag, ver; | |||
| @@ -87,64 +88,67 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb, int appen | |||
| uint32_t chmask; | |||
| wc->pos = avio_tell(pb); | |||
| if(!append){ | |||
| if (!append) { | |||
| tag = avio_rl32(pb); | |||
| if (tag != MKTAG('w', 'v', 'p', 'k')) | |||
| return AVERROR_INVALIDDATA; | |||
| size = avio_rl32(pb); | |||
| if(size < 24 || size > WV_BLOCK_LIMIT){ | |||
| if (size < 24 || size > WV_BLOCK_LIMIT) { | |||
| av_log(ctx, AV_LOG_ERROR, "Incorrect block size %i\n", size); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| wc->blksize = size; | |||
| ver = avio_rl16(pb); | |||
| if(ver < 0x402 || ver > 0x410){ | |||
| if (ver < 0x402 || ver > 0x410) { | |||
| av_log(ctx, AV_LOG_ERROR, "Unsupported version %03X\n", ver); | |||
| return AVERROR_PATCHWELCOME; | |||
| } | |||
| avio_r8(pb); // track no | |||
| avio_r8(pb); // track sub index | |||
| wc->samples = avio_rl32(pb); // total samples in file | |||
| wc->soff = avio_rl32(pb); // offset in samples of current block | |||
| wc->soff = avio_rl32(pb); // offset in samples of current block | |||
| avio_read(pb, wc->extra, WV_EXTRA_SIZE); | |||
| }else{ | |||
| } else { | |||
| size = wc->blksize; | |||
| } | |||
| wc->flags = AV_RL32(wc->extra + 4); | |||
| // blocks with zero samples don't contain actual audio information and should be ignored | |||
| /* Blocks with zero samples don't contain actual audio information | |||
| * and should be ignored */ | |||
| if (!AV_RN32(wc->extra)) | |||
| return 0; | |||
| //parse flags | |||
| bpp = ((wc->flags & 3) + 1) << 3; | |||
| chan = 1 + !(wc->flags & WV_MONO); | |||
| // parse flags | |||
| bpp = ((wc->flags & 3) + 1) << 3; | |||
| chan = 1 + !(wc->flags & WV_MONO); | |||
| chmask = wc->flags & WV_MONO ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO; | |||
| rate = wv_rates[(wc->flags >> 23) & 0xF]; | |||
| rate = wv_rates[(wc->flags >> 23) & 0xF]; | |||
| wc->multichannel = !!((wc->flags & WV_SINGLE_BLOCK) != WV_SINGLE_BLOCK); | |||
| if(wc->multichannel){ | |||
| chan = wc->chan; | |||
| if (wc->multichannel) { | |||
| chan = wc->chan; | |||
| chmask = wc->chmask; | |||
| } | |||
| if((rate == -1 || !chan) && !wc->block_parsed){ | |||
| if ((rate == -1 || !chan) && !wc->block_parsed) { | |||
| int64_t block_end = avio_tell(pb) + wc->blksize - 24; | |||
| if(!pb->seekable){ | |||
| av_log(ctx, AV_LOG_ERROR, "Cannot determine additional parameters\n"); | |||
| if (!pb->seekable) { | |||
| av_log(ctx, AV_LOG_ERROR, | |||
| "Cannot determine additional parameters\n"); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| while(avio_tell(pb) < block_end){ | |||
| while (avio_tell(pb) < block_end) { | |||
| int id, size; | |||
| id = avio_r8(pb); | |||
| id = avio_r8(pb); | |||
| size = (id & 0x80) ? avio_rl24(pb) : avio_r8(pb); | |||
| size <<= 1; | |||
| if(id&0x40) | |||
| if (id & 0x40) | |||
| size--; | |||
| switch(id&0x3F){ | |||
| switch (id & 0x3F) { | |||
| case 0xD: | |||
| if(size <= 1){ | |||
| av_log(ctx, AV_LOG_ERROR, "Insufficient channel information\n"); | |||
| if (size <= 1) { | |||
| av_log(ctx, AV_LOG_ERROR, | |||
| "Insufficient channel information\n"); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| chan = avio_r8(pb); | |||
| switch(size - 2){ | |||
| switch (size - 2) { | |||
| case 0: | |||
| chmask = avio_r8(pb); | |||
| break; | |||
| @@ -159,11 +163,12 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb, int appen | |||
| break; | |||
| case 5: | |||
| avio_skip(pb, 1); | |||
| chan |= (avio_r8(pb) & 0xF) << 8; | |||
| chan |= (avio_r8(pb) & 0xF) << 8; | |||
| chmask = avio_rl24(pb); | |||
| break; | |||
| default: | |||
| av_log(ctx, AV_LOG_ERROR, "Invalid channel info size %d\n", size); | |||
| av_log(ctx, AV_LOG_ERROR, | |||
| "Invalid channel info size %d\n", size); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| break; | |||
| @@ -173,30 +178,41 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb, int appen | |||
| default: | |||
| avio_skip(pb, size); | |||
| } | |||
| if(id&0x40) | |||
| if (id & 0x40) | |||
| avio_skip(pb, 1); | |||
| } | |||
| if(rate == -1){ | |||
| av_log(ctx, AV_LOG_ERROR, "Cannot determine custom sampling rate\n"); | |||
| if (rate == -1) { | |||
| av_log(ctx, AV_LOG_ERROR, | |||
| "Cannot determine custom sampling rate\n"); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| avio_seek(pb, block_end - wc->blksize + 24, SEEK_SET); | |||
| } | |||
| if(!wc->bpp) wc->bpp = bpp; | |||
| if(!wc->chan) wc->chan = chan; | |||
| if(!wc->chmask) wc->chmask = chmask; | |||
| if(!wc->rate) wc->rate = rate; | |||
| if (!wc->bpp) | |||
| wc->bpp = bpp; | |||
| if (!wc->chan) | |||
| wc->chan = chan; | |||
| if (!wc->chmask) | |||
| wc->chmask = chmask; | |||
| if (!wc->rate) | |||
| wc->rate = rate; | |||
| if(wc->flags && bpp != wc->bpp){ | |||
| av_log(ctx, AV_LOG_ERROR, "Bits per sample differ, this block: %i, header block: %i\n", bpp, wc->bpp); | |||
| if (wc->flags && bpp != wc->bpp) { | |||
| av_log(ctx, AV_LOG_ERROR, | |||
| "Bits per sample differ, this block: %i, header block: %i\n", | |||
| bpp, wc->bpp); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| if(wc->flags && !wc->multichannel && chan != wc->chan){ | |||
| av_log(ctx, AV_LOG_ERROR, "Channels differ, this block: %i, header block: %i\n", chan, wc->chan); | |||
| if (wc->flags && !wc->multichannel && chan != wc->chan) { | |||
| av_log(ctx, AV_LOG_ERROR, | |||
| "Channels differ, this block: %i, header block: %i\n", | |||
| chan, wc->chan); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| if(wc->flags && rate != -1 && rate != wc->rate){ | |||
| av_log(ctx, AV_LOG_ERROR, "Sampling rate differ, this block: %i, header block: %i\n", rate, wc->rate); | |||
| if (wc->flags && rate != -1 && rate != wc->rate) { | |||
| av_log(ctx, AV_LOG_ERROR, | |||
| "Sampling rate differ, this block: %i, header block: %i\n", | |||
| rate, wc->rate); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| wc->blksize = size - 24; | |||
| @@ -211,10 +227,10 @@ static int wv_read_header(AVFormatContext *s) | |||
| int ret; | |||
| wc->block_parsed = 0; | |||
| for(;;){ | |||
| for (;;) { | |||
| if ((ret = wv_read_block_header(s, pb, 0)) < 0) | |||
| return ret; | |||
| if(!AV_RN32(wc->extra)) | |||
| if (!AV_RN32(wc->extra)) | |||
| avio_skip(pb, wc->blksize - 24); | |||
| else | |||
| break; | |||
| @@ -224,20 +240,20 @@ static int wv_read_header(AVFormatContext *s) | |||
| st = avformat_new_stream(s, NULL); | |||
| if (!st) | |||
| return AVERROR(ENOMEM); | |||
| st->codec->codec_type = AVMEDIA_TYPE_AUDIO; | |||
| st->codec->codec_id = CODEC_ID_WAVPACK; | |||
| st->codec->channels = wc->chan; | |||
| st->codec->channel_layout = wc->chmask; | |||
| st->codec->sample_rate = wc->rate; | |||
| st->codec->codec_type = AVMEDIA_TYPE_AUDIO; | |||
| st->codec->codec_id = CODEC_ID_WAVPACK; | |||
| st->codec->channels = wc->chan; | |||
| st->codec->channel_layout = wc->chmask; | |||
| st->codec->sample_rate = wc->rate; | |||
| st->codec->bits_per_coded_sample = wc->bpp; | |||
| avpriv_set_pts_info(st, 64, 1, wc->rate); | |||
| st->start_time = 0; | |||
| st->duration = wc->samples; | |||
| st->duration = wc->samples; | |||
| if(s->pb->seekable) { | |||
| if (s->pb->seekable) { | |||
| int64_t cur = avio_tell(s->pb); | |||
| ff_ape_parse_tag(s); | |||
| if(!av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) | |||
| if (!av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) | |||
| ff_id3v1_read(s); | |||
| avio_seek(s->pb, cur, SEEK_SET); | |||
| } | |||
| @@ -245,8 +261,7 @@ static int wv_read_header(AVFormatContext *s) | |||
| return 0; | |||
| } | |||
| static int wv_read_packet(AVFormatContext *s, | |||
| AVPacket *pkt) | |||
| static int wv_read_packet(AVFormatContext *s, AVPacket *pkt) | |||
| { | |||
| WVContext *wc = s->priv_data; | |||
| int ret; | |||
| @@ -256,41 +271,41 @@ static int wv_read_packet(AVFormatContext *s, | |||
| if (s->pb->eof_reached) | |||
| return AVERROR_EOF; | |||
| if(wc->block_parsed){ | |||
| if (wc->block_parsed) { | |||
| if ((ret = wv_read_block_header(s, s->pb, 0)) < 0) | |||
| return ret; | |||
| } | |||
| pos = wc->pos; | |||
| off = wc->multichannel ? 4 : 0; | |||
| if(av_new_packet(pkt, wc->blksize + WV_EXTRA_SIZE + off) < 0) | |||
| if (av_new_packet(pkt, wc->blksize + WV_EXTRA_SIZE + off) < 0) | |||
| return AVERROR(ENOMEM); | |||
| if(wc->multichannel) | |||
| if (wc->multichannel) | |||
| AV_WL32(pkt->data, wc->blksize + WV_EXTRA_SIZE + 12); | |||
| memcpy(pkt->data + off, wc->extra, WV_EXTRA_SIZE); | |||
| ret = avio_read(s->pb, pkt->data + WV_EXTRA_SIZE + off, wc->blksize); | |||
| if(ret != wc->blksize){ | |||
| if (ret != wc->blksize) { | |||
| av_free_packet(pkt); | |||
| return AVERROR(EIO); | |||
| } | |||
| while(!(wc->flags & WV_END_BLOCK)){ | |||
| if(avio_rl32(s->pb) != MKTAG('w', 'v', 'p', 'k')){ | |||
| while (!(wc->flags & WV_END_BLOCK)) { | |||
| if (avio_rl32(s->pb) != MKTAG('w', 'v', 'p', 'k')) { | |||
| av_free_packet(pkt); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| if((ret = av_append_packet(s->pb, pkt, 4)) < 0){ | |||
| if ((ret = av_append_packet(s->pb, pkt, 4)) < 0) { | |||
| av_free_packet(pkt); | |||
| return ret; | |||
| } | |||
| size = AV_RL32(pkt->data + pkt->size - 4); | |||
| if(size < 24 || size > WV_BLOCK_LIMIT){ | |||
| if (size < 24 || size > WV_BLOCK_LIMIT) { | |||
| av_free_packet(pkt); | |||
| av_log(s, AV_LOG_ERROR, "Incorrect block size %d\n", size); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| wc->blksize = size; | |||
| ver = avio_rl16(s->pb); | |||
| if(ver < 0x402 || ver > 0x410){ | |||
| ver = avio_rl16(s->pb); | |||
| if (ver < 0x402 || ver > 0x410) { | |||
| av_free_packet(pkt); | |||
| av_log(s, AV_LOG_ERROR, "Unsupported version %03X\n", ver); | |||
| return AVERROR_PATCHWELCOME; | |||
| @@ -298,29 +313,30 @@ static int wv_read_packet(AVFormatContext *s, | |||
| avio_r8(s->pb); // track no | |||
| avio_r8(s->pb); // track sub index | |||
| wc->samples = avio_rl32(s->pb); // total samples in file | |||
| wc->soff = avio_rl32(s->pb); // offset in samples of current block | |||
| if((ret = av_append_packet(s->pb, pkt, WV_EXTRA_SIZE)) < 0){ | |||
| wc->soff = avio_rl32(s->pb); // offset in samples of current block | |||
| if ((ret = av_append_packet(s->pb, pkt, WV_EXTRA_SIZE)) < 0) { | |||
| av_free_packet(pkt); | |||
| return ret; | |||
| } | |||
| memcpy(wc->extra, pkt->data + pkt->size - WV_EXTRA_SIZE, WV_EXTRA_SIZE); | |||
| if ((ret = wv_read_block_header(s, s->pb, 1)) < 0){ | |||
| if ((ret = wv_read_block_header(s, s->pb, 1)) < 0) { | |||
| av_free_packet(pkt); | |||
| return ret; | |||
| } | |||
| ret = av_append_packet(s->pb, pkt, wc->blksize); | |||
| if(ret < 0){ | |||
| if (ret < 0) { | |||
| av_free_packet(pkt); | |||
| return ret; | |||
| } | |||
| } | |||
| pkt->stream_index = 0; | |||
| wc->block_parsed = 1; | |||
| pkt->pts = wc->soff; | |||
| block_samples = AV_RN32(wc->extra); | |||
| wc->block_parsed = 1; | |||
| pkt->pts = wc->soff; | |||
| block_samples = AV_RN32(wc->extra); | |||
| if (block_samples > INT32_MAX) | |||
| av_log(s, AV_LOG_WARNING, "Too many samples in block: %"PRIu32"\n", block_samples); | |||
| av_log(s, AV_LOG_WARNING, | |||
| "Too many samples in block: %"PRIu32"\n", block_samples); | |||
| else | |||
| pkt->duration = block_samples; | |||
| @@ -328,9 +344,10 @@ static int wv_read_packet(AVFormatContext *s, | |||
| return 0; | |||
| } | |||
| static int wv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) | |||
| static int wv_read_seek(AVFormatContext *s, int stream_index, | |||
| int64_t timestamp, int flags) | |||
| { | |||
| AVStream *st = s->streams[stream_index]; | |||
| AVStream *st = s->streams[stream_index]; | |||
| WVContext *wc = s->priv_data; | |||
| AVPacket pkt1, *pkt = &pkt1; | |||
| int ret; | |||
| @@ -345,19 +362,19 @@ static int wv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, | |||
| return 0; | |||
| } | |||
| /* if timestamp is out of bounds, return error */ | |||
| if(timestamp < 0 || timestamp >= s->duration) | |||
| if (timestamp < 0 || timestamp >= s->duration) | |||
| return AVERROR(EINVAL); | |||
| pos = avio_tell(s->pb); | |||
| do{ | |||
| do { | |||
| ret = av_read_frame(s, pkt); | |||
| if (ret < 0){ | |||
| if (ret < 0) { | |||
| avio_seek(s->pb, pos, SEEK_SET); | |||
| return ret; | |||
| } | |||
| pts = pkt->pts; | |||
| av_free_packet(pkt); | |||
| }while(pts < timestamp); | |||
| } while(pts < timestamp); | |||
| return 0; | |||
| } | |||