improved wav reader

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5615 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
beveloper
2003-12-07 21:07:20 +00:00
parent c9c1abc3bf
commit a3d7908e26
4 changed files with 213 additions and 68 deletions
@@ -41,6 +41,8 @@ RawDecoder::NegotiateOutputFormat(media_format *ioDecodedFormat)
string_for_format(*ioDecodedFormat, s, sizeof(s));
printf("RawDecoder::NegotiateOutputFormat enter: %s\n", s);
// XXX we should allow different buffer size, sample format and byte order and do translation in decode!
*ioDecodedFormat = fInputFormat;
string_for_format(*ioDecodedFormat, s, sizeof(s));
@@ -15,14 +15,16 @@
#define BUFFER_SIZE 16384
#define FOURCC(fcc) (*reinterpret_cast<const uint32 *>(fcc))
#define FOURCC(a,b,c,d) ((((uint32)(d)) << 24) | (((uint32)(c)) << 16) | (((uint32)(b)) << 8) | ((uint32)(a)))
#define UINT16(a) ((uint16)B_LENDIAN_TO_HOST_INT16((a)))
#define UINT32(a) ((uint32)B_LENDIAN_TO_HOST_INT32((a)))
struct wavdata
{
uint64 position;
uint64 datasize;
int32 framesize;
int32 bitsperframe;
int32 fps;
void *buffer;
@@ -57,43 +59,142 @@ WavReader::Sniff(int32 *streamCount)
fSource = dynamic_cast<BPositionIO *>(Reader::Source());
fDataSize = Source()->Seek(0, SEEK_END);
if (fDataSize < sizeof(fRawHeader)) {
TRACE("WavReader::Sniff: too small\n");
return B_ERROR;
}
fDataSize -= sizeof(fRawHeader);
if (sizeof(fRawHeader) != Source()->ReadAt(0, &fRawHeader, sizeof(fRawHeader))) {
TRACE("WavReader::Sniff: header reading failed\n");
fFileSize = Source()->Seek(0, SEEK_END);
if (fFileSize < 44) {
TRACE("WavReader::Sniff: File too small\n");
return B_ERROR;
}
if (B_LENDIAN_TO_HOST_INT32(fRawHeader.riff.riff_id) != FOURCC("RIFF")
|| B_LENDIAN_TO_HOST_INT32(fRawHeader.riff.wave_id) != FOURCC("WAVE")
|| B_LENDIAN_TO_HOST_INT32(fRawHeader.format.fourcc) != FOURCC("fmt ")
|| B_LENDIAN_TO_HOST_INT32(fRawHeader.data.fourcc) != FOURCC("data")) {
TRACE("WavReader::Sniff: header not recognized\n");
int64 pos = 0;
riff_struct riff;
if (sizeof(riff) != Source()->ReadAt(pos, &riff, sizeof(riff))) {
TRACE("WavReader::Sniff: RIFF WAVE header reading failed\n");
return B_ERROR;
}
pos += sizeof(riff);
if (UINT32(riff.riff_id) != FOURCC('R','I','F','F') || UINT32(riff.wave_id) != FOURCC('W','A','V','E')) {
TRACE("WavReader::Sniff: RIFF WAVE header not recognized\n");
return B_ERROR;
}
format_struct format;
format_struct_extensible format_ext;
fact_struct fact;
// read all chunks and search for "fact", "fmt" (normal or extensible) and "data"
// everything else is ignored;
bool foundFact = false;
bool foundFmt = false;
bool foundFmtExt = false;
bool foundData = false;
while (pos + sizeof(chunk_struct) <= fFileSize) {
chunk_struct chunk;
if (sizeof(chunk) != Source()->ReadAt(pos, &chunk, sizeof(chunk))) {
TRACE("WavReader::Sniff: chunk header reading failed\n");
return B_ERROR;
}
pos += sizeof(chunk);
switch (UINT32(chunk.fourcc)) {
case FOURCC('f','m','t',' '):
if (UINT32(chunk.len) >= sizeof(format)) {
if (sizeof(format) != Source()->ReadAt(pos, &format, sizeof(format))) {
TRACE("WavReader::Sniff: format chunk reading failed\n");
break;
}
foundFmt = true;
if (UINT32(chunk.len) >= sizeof(format_ext) && UINT16(format.format_tag) == 0xfffe) {
if (sizeof(format_ext) != Source()->ReadAt(pos, &format_ext, sizeof(format_ext))) {
TRACE("WavReader::Sniff: format extensible chunk reading failed\n");
break;
}
foundFmtExt = true;
}
}
break;
case FOURCC('f','a','c','t'):
if (UINT32(chunk.len) >= sizeof(fact)) {
if (sizeof(fact) != Source()->ReadAt(pos, &fact, sizeof(fact))) {
TRACE("WavReader::Sniff: fact chunk reading failed\n");
break;
}
foundFact = true;
}
break;
case FOURCC('d','a','t','a'):
fDataStart = pos;
fDataSize = UINT32(chunk.len);
foundData = true;
// If file size is >= 2GB and we already found a format chunk,
// assume the rest of the file is data and get out of here.
// This should allow reading wav files much bigger than 2 or 4 GB.
if (fFileSize >= 0x7fffffff && foundFmt) {
pos += fFileSize;
fDataSize = fFileSize - fDataStart;
TRACE("WavReader::Sniff: big file size %Ld, indicated data size %lu, assuming data size is %Ld\n",
fFileSize, UINT32(chunk.len), fDataSize);
}
break;
default:
TRACE("WavReader::Sniff: ignoring chunk 0x%08lx of %lu bytes\n", UINT32(chunk.fourcc), chunk.len);
break;
}
pos += UINT32(chunk.len);
pos += (pos & 1);
}
TRACE("WavReader::Sniff: looks like we found something\n");
if (!foundFmt) {
TRACE("WavReader::Sniff: couldn't find format chunk\n");
return B_ERROR;
}
if (!foundData) {
TRACE("WavReader::Sniff: couldn't find data chunk\n");
return B_ERROR;
}
TRACE(" format_tag 0x%04x\n", B_LENDIAN_TO_HOST_INT16(fRawHeader.common.format_tag));
TRACE(" channels 0x%04x\n", B_LENDIAN_TO_HOST_INT16(fRawHeader.common.channels));
TRACE(" samples_per_sec 0x%08lx\n", B_LENDIAN_TO_HOST_INT32(fRawHeader.common.samples_per_sec));
TRACE(" avg_bytes_per_sec 0x%08lx\n", B_LENDIAN_TO_HOST_INT32(fRawHeader.common.avg_bytes_per_sec));
TRACE(" block_align 0x%04x\n", B_LENDIAN_TO_HOST_INT16(fRawHeader.common.block_align));
TRACE(" bits_per_sample 0x%04x\n", B_LENDIAN_TO_HOST_INT16(fRawHeader.common.bits_per_sample));
TRACE(" format_tag 0x%04x\n", B_LENDIAN_TO_HOST_INT16(fRawHeader.common.format_tag));
TRACE("WavReader::Sniff: we found something that looks like:\n");
if (fDataSize > B_LENDIAN_TO_HOST_INT32(fRawHeader.data.len) && fDataSize < 0xffffffff) {
TRACE("WavReader::Sniff: reducing data size from %Ld to %ld\n",
fDataSize, B_LENDIAN_TO_HOST_INT32(fRawHeader.data.len));
fDataSize = B_LENDIAN_TO_HOST_INT32(fRawHeader.data.len);
TRACE(" format_tag 0x%04x\n", UINT16(format.format_tag));
TRACE(" channels %d\n", UINT16(format.channels));
TRACE(" samples_per_sec %ld\n", UINT32(format.samples_per_sec));
TRACE(" avg_bytes_per_sec %ld\n", UINT32(format.avg_bytes_per_sec));
TRACE(" block_align %d\n", UINT16(format.block_align));
TRACE(" bits_per_sample %d\n", UINT16(format.bits_per_sample));
if (foundFmtExt) {
TRACE(" ext_size %d\n", UINT16(format_ext.ext_size));
TRACE(" valid_bits_per_sample %d\n", UINT16(format_ext.valid_bits_per_sample));
TRACE(" channel_mask %ld\n", UINT32(format_ext.channel_mask));
TRACE(" guid[0-1] format 0x%04x\n", (format_ext.guid[1] << 8) | format_ext.guid[0]);
}
if (foundFact) {
TRACE(" sample_length %ld\n", UINT32(fact.sample_length));
}
TRACE("WavReader::Sniff: data size is %Ld bytes\n", fDataSize);
fChannelCount = UINT16(format.channels);
fFrameRate = UINT32(format.samples_per_sec);
fBitsPerSample = UINT16(format.bits_per_sample);
if (fBitsPerSample == 0) {
printf("WavReader::Sniff: Error, bits_per_sample = 0\n");
fBitsPerSample = 8;
}
fBlockAlign = UINT16(format.block_align);
int min_align = (fBitsPerSample * fChannelCount + 7) / 8;
if (fBlockAlign < min_align)
fBlockAlign = min_align;
fFormatCode = UINT16(format.format_tag);
if (fFormatCode == 0xfffe && foundFmtExt)
fFormatCode = (format_ext.guid[1] << 8) | format_ext.guid[0];
TRACE(" fChannelCount %ld\n", fChannelCount);
TRACE(" fFrameRate %ld\n", fFrameRate);
TRACE(" fBitsPerSample %d\n", fBitsPerSample);
TRACE(" fBlockAlign %d\n", fBlockAlign);
TRACE(" min_align %d\n", min_align);
TRACE(" fFormatCode 0x%04x\n", fFormatCode);
// XXX fact.sample_length contains duration of encodec files?
*streamCount = 1;
return B_OK;
@@ -124,32 +225,53 @@ WavReader::AllocateCookie(int32 streamNumber, void **cookie)
data->position = 0;
data->datasize = fDataSize;
data->framesize = (B_LENDIAN_TO_HOST_INT16(fRawHeader.common.bits_per_sample) / 8) * B_LENDIAN_TO_HOST_INT16(fRawHeader.common.channels);
data->fps = B_LENDIAN_TO_HOST_INT32(fRawHeader.common.samples_per_sec); // WAVE samples_per_sec really means frames per sec
data->buffersize = (BUFFER_SIZE / data->framesize) * data->framesize;
data->bitsperframe = fChannelCount * fBitsPerSample;
data->fps = fFrameRate;
data->buffersize = (BUFFER_SIZE / fBlockAlign) * fBlockAlign;
data->buffer = malloc(data->buffersize);
data->framecount = fDataSize / data->framesize;
data->framecount = (8 * fDataSize) / data->bitsperframe;
data->duration = (data->framecount * 1000000LL) / data->fps;
TRACE(" framesize %ld\n", data->framesize);
TRACE(" bitsperframe %ld\n", data->bitsperframe);
TRACE(" fps %ld\n", data->fps);
TRACE(" buffersize %ld\n", data->buffersize);
TRACE(" framecount %Ld\n", data->framecount);
TRACE(" duration %Ld\n", data->duration);
// BMediaFormats formats;
media_format_description description;
description.family = B_BEOS_FORMAT_FAMILY;
description.u.beos.format = B_BEOS_FORMAT_RAW_AUDIO;
// formats.GetFormatFor(description, &data->format);
_get_format_for_description(&data->format, description);
data->format.u.raw_audio.frame_rate = data->fps;
data->format.u.raw_audio.channel_count = B_LENDIAN_TO_HOST_INT16(fRawHeader.common.channels);
data->format.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT; // XXX fixme
data->format.u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN;
data->format.u.raw_audio.buffer_size = data->buffersize;
BMediaFormats formats;
if (fFormatCode == 0x0001) {
// a raw PCM format
media_format_description description;
description.family = B_BEOS_FORMAT_FAMILY;
description.u.beos.format = B_BEOS_FORMAT_RAW_AUDIO;
formats.GetFormatFor(description, &data->format);
data->format.u.raw_audio.frame_rate = fFrameRate;
data->format.u.raw_audio.channel_count = fChannelCount;
switch (fBitsPerSample) {
case 8:
data->format.u.raw_audio.format = media_raw_audio_format::B_AUDIO_UCHAR;
break;
case 16:
data->format.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
break;
case 32:
data->format.u.raw_audio.format = media_raw_audio_format::B_AUDIO_INT;
break;
default:
TRACE("WavReader::AllocateCookie: unhandled bits per sample %d\n", fBitsPerSample);
return B_ERROR;
}
data->format.u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN;
data->format.u.raw_audio.buffer_size = data->buffersize;
} else {
// some encoded format
media_format_description description;
description.family = B_WAV_FORMAT_FAMILY;
description.u.wav.codec = fFormatCode;
formats.GetFormatFor(description, &data->format);
data->format.u.encoded_audio.output.frame_rate = fFrameRate;
data->format.u.encoded_audio.output.channel_count = fChannelCount;
}
// store the cookie
*cookie = data;
@@ -194,21 +316,22 @@ WavReader::Seek(void *cookie,
uint64 pos;
if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
pos = *frame * data->framesize;
*time = (pos * 1000000LL) / data->fps;
pos = (*frame * data->bitsperframe) / 8;
pos = (pos / fBlockAlign) * fBlockAlign; // round down to a block start
TRACE("WavReader::Seek to frame %Ld, pos %Ld\n", *frame, pos);
TRACE("WavReader::Seek newtime %Ld\n", *time);
} else if (seekTo & B_MEDIA_SEEK_TO_TIME) {
pos = (*time * data->fps) / 1000000LL;
pos = (pos / data->framesize) * data->framesize; // round down to frame start
pos = (*time * data->fps * data->bitsperframe) / (1000000LL * 8);
pos = (pos / fBlockAlign) * fBlockAlign; // round down to a block start
TRACE("WavReader::Seek to time %Ld, pos %Ld\n", *time, pos);
*time = (pos * 1000000LL) / data->fps;
*frame = pos / data->framesize;
TRACE("WavReader::Seek newtime %Ld\n", *time);
TRACE("WavReader::Seek newframe %Ld\n", *frame);
} else {
return B_ERROR;
}
*frame = (8 * pos) / data->bitsperframe;
*time = (*frame * 1000000LL) / data->fps;
TRACE("WavReader::Seek newtime %Ld\n", *time);
TRACE("WavReader::Seek newframe %Ld\n", *frame);
if (pos < 0 || pos > data->datasize) {
TRACE("WavReader::Seek invalid position %Ld\n", pos);
@@ -228,8 +351,8 @@ WavReader::GetNextChunk(void *cookie,
wavdata *data = reinterpret_cast<wavdata *>(cookie);
status_t result = B_OK;
mediaHeader->start_time = ((data->position / data->framesize) * 1000000LL) / data->fps;
mediaHeader->file_pos = 44 + data->position;
mediaHeader->start_time = (((8 * data->position) / data->bitsperframe) * 1000000LL) / data->fps;
mediaHeader->file_pos = fDataStart + data->position;
int64 maxreadsize = data->datasize - data->position;
int32 readsize = data->buffersize;
@@ -238,11 +361,13 @@ WavReader::GetNextChunk(void *cookie,
result = B_LAST_BUFFER_ERROR;
}
if (readsize != Source()->ReadAt(44 + data->position, data->buffer, readsize)) {
if (readsize != Source()->ReadAt(fDataStart + data->position, data->buffer, readsize)) {
TRACE("WavReader::GetNextChunk: unexpected read error\n");
return B_ERROR;
}
// XXX if the stream has more than two channels, we need to reorder channel data here
data->position += readsize;
*chunkBuffer = data->buffer;
*chunkSize = readsize;
@@ -31,8 +31,15 @@ public:
private:
BPositionIO * fSource;
uint64 fDataSize;
wave_header fRawHeader;
int64 fFileSize;
int64 fDataStart;
int64 fDataSize;
int32 fChannelCount;
int32 fFrameRate;
int fBitsPerSample;
uint16 fFormatCode;
uint16 fBlockAlign;
};
+18 -7
View File
@@ -12,7 +12,7 @@ struct chunk_struct
uint32 len;
};
struct common_struct
struct format_struct
{
uint16 format_tag;
uint16 channels;
@@ -22,10 +22,21 @@ struct common_struct
uint16 bits_per_sample;
};
struct wave_header
struct format_struct_extensible
{
riff_struct riff;
chunk_struct format;
common_struct common;
chunk_struct data;
};
uint16 format_tag; // 0xfffe for extensible format
uint16 channels;
uint32 samples_per_sec;
uint32 avg_bytes_per_sec;
uint16 block_align;
uint16 bits_per_sample;
uint16 ext_size;
uint16 valid_bits_per_sample;
uint32 channel_mask;
uint8 guid[16]; // first two bytes are format code
};
struct fact_struct
{
uint32 sample_length;
};