From a3d7908e2685e07258d20df35fb72f0fa9f63ced Mon Sep 17 00:00:00 2001 From: beveloper Date: Sun, 7 Dec 2003 21:07:20 +0000 Subject: [PATCH] improved wav reader git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5615 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../plugins/raw_decoder/RawDecoderPlugin.cpp | 2 + .../plugins/wav_reader/WavReaderPlugin.cpp | 243 +++++++++++++----- .../plugins/wav_reader/WavReaderPlugin.h | 11 +- src/add-ons/media/plugins/wav_reader/wav.h | 25 +- 4 files changed, 213 insertions(+), 68 deletions(-) diff --git a/src/add-ons/media/plugins/raw_decoder/RawDecoderPlugin.cpp b/src/add-ons/media/plugins/raw_decoder/RawDecoderPlugin.cpp index d7d70cb656..db4574e7a7 100644 --- a/src/add-ons/media/plugins/raw_decoder/RawDecoderPlugin.cpp +++ b/src/add-ons/media/plugins/raw_decoder/RawDecoderPlugin.cpp @@ -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)); diff --git a/src/add-ons/media/plugins/wav_reader/WavReaderPlugin.cpp b/src/add-ons/media/plugins/wav_reader/WavReaderPlugin.cpp index 2e68d4d49b..8d08611fbc 100644 --- a/src/add-ons/media/plugins/wav_reader/WavReaderPlugin.cpp +++ b/src/add-ons/media/plugins/wav_reader/WavReaderPlugin.cpp @@ -15,14 +15,16 @@ #define BUFFER_SIZE 16384 -#define FOURCC(fcc) (*reinterpret_cast(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(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(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; diff --git a/src/add-ons/media/plugins/wav_reader/WavReaderPlugin.h b/src/add-ons/media/plugins/wav_reader/WavReaderPlugin.h index 05707ef911..37feb26fe8 100644 --- a/src/add-ons/media/plugins/wav_reader/WavReaderPlugin.h +++ b/src/add-ons/media/plugins/wav_reader/WavReaderPlugin.h @@ -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; }; diff --git a/src/add-ons/media/plugins/wav_reader/wav.h b/src/add-ons/media/plugins/wav_reader/wav.h index 595fbde648..32005c38d3 100644 --- a/src/add-ons/media/plugins/wav_reader/wav.h +++ b/src/add-ons/media/plugins/wav_reader/wav.h @@ -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; +};