Add support for VBRI header, remove duplicated code, always print error messages

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34195 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
David McPaul 2009-11-23 12:18:34 +00:00
parent f75975a907
commit 13af0e5bc0
2 changed files with 188 additions and 67 deletions

View File

@ -138,6 +138,18 @@ struct mp3Reader::xing_vbr_info
struct mp3Reader::fhg_vbr_info
{
uint32 SampleRate;
char VbriSignature[4];
uint16 VbriVersion;
uint16 VbriDelay;
uint16 VbriQuality; // Actually a float, convert on request
uint32 VbriStreamBytes;
uint32 VbriStreamFrames;
uint16 VbriTableSize;
uint16 VbriTableScale;
uint16 VbriEntryBytes;
uint16 VbriEntryFrames;
int *VbriTable;
};
@ -238,12 +250,27 @@ mp3Reader::AllocateCookie(int32 streamNumber, void **cookie)
int frame_size;
if (fXingVbrInfo && fXingVbrInfo->frameCount != -1 && fXingVbrInfo->duration != -1) {
TRACE("mp3Reader::AllocateCookie: using timing info from VBR header\n");
TRACE("mp3Reader::AllocateCookie: using timing info from Xing VBR header\n");
bit_rate = (fXingVbrInfo->byteCount * 8 * 1000000) / fXingVbrInfo->duration; // average bit rate
frame_size = fXingVbrInfo->byteCount / fXingVbrInfo->encodedFramesCount; // average frame size
data->duration = fXingVbrInfo->duration;
data->frameCount = fXingVbrInfo->frameCount;
data->frameRate = fXingVbrInfo->frameRate;
} else if (fFhgVbrInfo) {
TRACE("mp3Reader::AllocateCookie: using timing info from Fraunhofer VBR header\n");
int sampling_rate_index = (header[2] >> 2) & 0x03;
uint32 SamplesPerFrame;
fFhgVbrInfo->SampleRate = frame_rate_table[mpeg_version_index][sampling_rate_index];
(fFhgVbrInfo->SampleRate >= 32000) ? (SamplesPerFrame = 1152) : (SamplesPerFrame = 576);
data->duration = (int64)(((float)fFhgVbrInfo->VbriStreamFrames * (float)SamplesPerFrame)
/ (float)fFhgVbrInfo->SampleRate * 1000000.0f);
bit_rate = (fFhgVbrInfo->VbriStreamBytes * 8 * 1000000) / data->duration; // average bit rate
frame_size = fFhgVbrInfo->VbriStreamBytes / fFhgVbrInfo->VbriStreamFrames; // average frame size
data->frameCount = fFhgVbrInfo->VbriStreamFrames * SamplesPerFrame;
data->frameRate = fFhgVbrInfo->SampleRate;
} else {
TRACE("mp3Reader::AllocateCookie: assuming CBR, calculating timing info from file\n");
int sampling_rate_index = (header[2] >> 2) & 0x03;
@ -320,100 +347,65 @@ mp3Reader::Seek(void *cookie,
uint32 flags,
int64 *frame, bigtime_t *time)
{
// Find a seek position and seek to it
if (!fSeekableSource)
return B_ERROR;
mp3data *data = reinterpret_cast<mp3data *>(cookie);
int64 pos;
// this isn't very accurate
if (flags & B_MEDIA_SEEK_TO_FRAME) {
pos = fXingVbrInfo ? XingSeekPoint(100.0 * *frame / (float)data->frameCount) : -1;
if (pos < 0)
pos = (*frame * fDataSize) / data->frameCount;
TRACE("mp3Reader::Seek to frame %Ld, pos %Ld\n", *frame, pos);
*time = (*frame * data->duration) / data->frameCount;
TRACE("mp3Reader::Seek newtime %Ld\n", *time);
} else if (flags & B_MEDIA_SEEK_TO_TIME) {
pos = fXingVbrInfo ? XingSeekPoint(100.0 * *time / (float)data->duration) : -1;
if (pos < 0)
pos = (*time * fDataSize) / data->duration;
TRACE("mp3Reader::Seek to time %Ld, pos %Ld\n", *time, pos);
*frame = (*time * data->frameCount) / data->duration;
TRACE("mp3Reader::Seek newframe %Ld\n", *frame);
status_t result = CalculatePosition(cookie, flags, frame, time, &data->position);
if (result == B_OK) {
data->framePosition = *frame; // this is not exact
TRACE("mp3Reader::Seek: synchronized at position %Ld\n", data->position);
} else {
return B_ERROR;
TRACE("mp3Reader::Seek: failed to seek\n");
}
// We ignore B_MEDIA_SEEK_CLOSEST_FORWARD, B_MEDIA_SEEK_CLOSEST_BACKWARD
uint8 buffer[16000];
if (pos > fDataSize - 16000)
pos = fDataSize - 16000;
if (pos < 0)
pos = 0;
int64 size = fDataSize - pos;
if (size > 16000)
size = 16000;
if (size != Source()->ReadAt(fDataStart + pos, buffer, size)) {
TRACE("mp3Reader::Seek: unexpected read error\n");
return B_ERROR;
}
int32 end = size - 4;
int32 ofs;
for (ofs = 0; ofs < end; ofs++) {
if (buffer[ofs] != 0xff) // quick check
continue;
if (IsValidStream(&buffer[ofs], size - ofs))
break;
}
if (ofs == end) {
TRACE("mp3Reader::Seek: couldn't synchronize\n");
return B_ERROR;
}
data->position = pos + ofs;
data->framePosition = *frame; // this is not exact
TRACE("mp3Reader::Seek: synchronized at position %Ld\n", data->position);
return B_OK;
return result;
}
status_t
mp3Reader::FindKeyFrame(void* cookie, uint32 flags,
int64* frame, bigtime_t* time)
{
// Find a seek position without actually seeking
off_t pos;
return CalculatePosition(cookie, flags, frame, time, &pos);
}
status_t
mp3Reader::CalculatePosition(void* cookie, uint32 flags,
int64* frame, bigtime_t* time, off_t *position) {
// Calculate the position within the file for the requested time or frame
if (!fSeekableSource)
return B_ERROR;
mp3data *data = reinterpret_cast<mp3data *>(cookie);
int64 pos;
// this isn't very accurate
if (flags & B_MEDIA_SEEK_TO_FRAME) {
pos = fXingVbrInfo ? XingSeekPoint(100.0 * *frame / (float)data->frameCount) : -1;
if (pos < 0)
pos = fFhgVbrInfo ? VBRISeekPoint(100.0 * *frame / (float)data->frameCount) : -1;
if (pos < 0)
pos = (*frame * fDataSize) / data->frameCount;
TRACE("mp3Reader::FindKeyFrame to frame %Ld, pos %Ld\n", *frame, pos);
*time = (*frame * data->duration) / data->frameCount;
TRACE("mp3Reader::FindKeyFrame newtime %Ld\n", *time);
TRACE("mp3Reader::CalculatePosition using frame %Ld gives time %Ld and pos %Ld\n", *frame, *time, pos);
} else if (flags & B_MEDIA_SEEK_TO_TIME) {
pos = fXingVbrInfo ? XingSeekPoint(100.0 * *time / (float)data->duration) : -1;
if (pos < 0)
pos = fFhgVbrInfo ? VBRISeekPoint(100.0 * *time / (float)data->duration) : -1;
if (pos < 0)
pos = (*time * fDataSize) / data->duration;
TRACE("mp3Reader::FindKeyFrame to time %Ld, pos %Ld\n", *time, pos);
*frame = (*time * data->frameCount) / data->duration;
TRACE("mp3Reader::FindKeyFrame newframe %Ld\n", *frame);
TRACE("mp3Reader::CalculatePosition using time %Ld gives frame %Ld and pos %Ld\n", *time, *frame, pos);
} else {
return B_ERROR;
}
// We ignore B_MEDIA_SEEK_CLOSEST_FORWARD, B_MEDIA_SEEK_CLOSEST_BACKWARD
// Align position to the sync bytes
uint8 buffer[16000];
if (pos > fDataSize - 16000)
pos = fDataSize - 16000;
@ -423,7 +415,7 @@ mp3Reader::FindKeyFrame(void* cookie, uint32 flags,
if (size > 16000)
size = 16000;
if (size != Source()->ReadAt(fDataStart + pos, buffer, size)) {
TRACE("mp3Reader::FindKeyFrame: unexpected read error\n");
TRACE("mp3Reader::CalculatePosition: unexpected read error\n");
return B_ERROR;
}
int32 end = size - 4;
@ -435,18 +427,23 @@ mp3Reader::FindKeyFrame(void* cookie, uint32 flags,
break;
}
if (ofs == end) {
TRACE("mp3Reader::FindKeyFrame: couldn't synchronize\n");
printf("mp3Reader::CalculatePosition: couldn't synchronize\n");
return B_ERROR;
}
*position = pos + ofs;
return B_OK;
}
status_t
mp3Reader::GetNextChunk(void *cookie,
const void **chunkBuffer, size_t *chunkSize,
media_header *mediaHeader)
{
// TODO This returns a chunk composed of a single frame
// Would be better to just return chunks of 16K and let the decoder break up the frames.
mp3data *data = reinterpret_cast<mp3data *>(cookie);
int64 maxbytes = fDataSize - data->position;
@ -480,13 +477,13 @@ retry:
int size = GetFrameLength(data->chunkBuffer) - 4;
if (size <= 0) {
TRACE("mp3Reader::GetNextChunk: invalid frame encountered at position %Ld, header %02x %02x %02x %02x \n",
printf("mp3Reader::GetNextChunk: invalid frame encountered at position %Ld, header %02x %02x %02x %02x \n",
data->position, (uint8)data->chunkBuffer[0], (uint8)data->chunkBuffer[1], (uint8)data->chunkBuffer[2], (uint8)data->chunkBuffer[3]);
if (ResynchronizeStream(data)) {
TRACE("mp3Reader::GetNextChunk: synchronized again at position %Ld\n", data->position);
goto retry;
} else {
TRACE("mp3Reader::GetNextChunk: synchronization failed\n");
printf("mp3Reader::GetNextChunk: synchronization failed\n");
return B_ERROR;
}
}
@ -608,7 +605,7 @@ mp3Reader::ParseFile()
hdr_length = GetFraunhoferVbrLength(&buf[pos]);
if (hdr_length > 0) {
TRACE("mp3ReaderPlugin::ParseFile found a Fraunhofer VBR header of %d bytes at position %Ld\n", hdr_length, offset + pos);
ParseFraunhoferVbrHeader(offset + pos);
ParseFraunhoferVbrHeader(offset + pos + 36);
goto skip_header;
}
@ -873,6 +870,122 @@ mp3Reader::GetFraunhoferVbrLength(uint8 *header)
void
mp3Reader::ParseFraunhoferVbrHeader(int64 pos)
{
uint32 i, entry;
fFhgVbrInfo = new fhg_vbr_info;
Source()->ReadAt(pos, &fFhgVbrInfo->VbriSignature[0], 4);
Source()->ReadAt(pos+4, &fFhgVbrInfo->VbriVersion, 2);
Source()->ReadAt(pos+6, &fFhgVbrInfo->VbriDelay, 2);
Source()->ReadAt(pos+8, &fFhgVbrInfo->VbriQuality, 2);
Source()->ReadAt(pos+10, &fFhgVbrInfo->VbriStreamBytes, 4);
Source()->ReadAt(pos+14, &fFhgVbrInfo->VbriStreamFrames, 4);
Source()->ReadAt(pos+18, &fFhgVbrInfo->VbriTableSize, 2);
Source()->ReadAt(pos+20, &fFhgVbrInfo->VbriTableScale, 2);
Source()->ReadAt(pos+22, &fFhgVbrInfo->VbriEntryBytes, 2);
Source()->ReadAt(pos+24, &fFhgVbrInfo->VbriEntryFrames, 2);
pos += 26;
// Double check we are dealing with a VBRI (Fraunhofer) header
if (fFhgVbrInfo->VbriSignature[0] == 'V' &&
fFhgVbrInfo->VbriSignature[1] == 'B' &&
fFhgVbrInfo->VbriSignature[2] == 'R' &&
fFhgVbrInfo->VbriSignature[3] == 'I') {
fFhgVbrInfo->VbriVersion = B_BENDIAN_TO_HOST_INT16(fFhgVbrInfo->VbriVersion);
fFhgVbrInfo->VbriDelay = B_BENDIAN_TO_HOST_INT16(fFhgVbrInfo->VbriDelay);
fFhgVbrInfo->VbriQuality = B_BENDIAN_TO_HOST_INT16(fFhgVbrInfo->VbriQuality);
fFhgVbrInfo->VbriStreamBytes = B_BENDIAN_TO_HOST_INT32(fFhgVbrInfo->VbriStreamBytes);
fFhgVbrInfo->VbriStreamFrames = B_BENDIAN_TO_HOST_INT32(fFhgVbrInfo->VbriStreamFrames);
fFhgVbrInfo->VbriTableSize = B_BENDIAN_TO_HOST_INT16(fFhgVbrInfo->VbriTableSize);
fFhgVbrInfo->VbriTableScale = B_BENDIAN_TO_HOST_INT16(fFhgVbrInfo->VbriTableScale);
fFhgVbrInfo->VbriEntryBytes = B_BENDIAN_TO_HOST_INT16(fFhgVbrInfo->VbriEntryBytes);
fFhgVbrInfo->VbriEntryFrames = B_BENDIAN_TO_HOST_INT16(fFhgVbrInfo->VbriEntryFrames);
fFhgVbrInfo->VbriTable = new int[(const int) fFhgVbrInfo->VbriTableSize + 1] ;
for ( i = 0 ; i <= fFhgVbrInfo->VbriTableSize ; i++) {
Source()->ReadAt(pos, &entry, fFhgVbrInfo->VbriEntryBytes);
pos += fFhgVbrInfo->VbriEntryBytes;
if (fFhgVbrInfo->VbriEntryBytes == 2) {
entry = B_BENDIAN_TO_HOST_INT16(entry);
} else if (fFhgVbrInfo->VbriEntryBytes == 4) {
entry = B_BENDIAN_TO_HOST_INT32(entry);
}
fFhgVbrInfo->VbriTable[i] = entry * fFhgVbrInfo->VbriTableScale;
}
TRACE("FraunhoferVbrHeader(Version %d, Delay %d, Quality %f, StreamBytes %ld, StreamFrames %ld, TableSize %d, TableScale %d, EntryBytes %d, EntryFrames %d)\n",
fFhgVbrInfo->VbriVersion, fFhgVbrInfo->VbriDelay, (float)fFhgVbrInfo->VbriQuality, fFhgVbrInfo->VbriStreamBytes, fFhgVbrInfo->VbriStreamFrames,
fFhgVbrInfo->VbriTableSize,fFhgVbrInfo->VbriTableScale,fFhgVbrInfo->VbriEntryBytes,fFhgVbrInfo->VbriEntryFrames);
} else {
delete fFhgVbrInfo;
fFhgVbrInfo = NULL;
}
}
int64
mp3Reader::VBRIseekPointByTime(float EntryTimeInMilliSeconds) {
unsigned int SamplesPerFrame, i = 0, SeekPoint = 0 , fraction = 0;
float TotalDuration;
float DurationPerVbriFrames;
float AccumulatedTime = 0.0f;
(fFhgVbrInfo->SampleRate >= 32000) ? (SamplesPerFrame = 1152) : (SamplesPerFrame = 576);
TotalDuration = ((float)fFhgVbrInfo->VbriStreamFrames * (float)SamplesPerFrame)
/ (float)fFhgVbrInfo->SampleRate * 1000.0f;
DurationPerVbriFrames = (float)TotalDuration / (float)(fFhgVbrInfo->VbriTableSize+1);
if ( EntryTimeInMilliSeconds > TotalDuration ) EntryTimeInMilliSeconds = TotalDuration;
while ( AccumulatedTime <= EntryTimeInMilliSeconds ) {
SeekPoint += fFhgVbrInfo->VbriTable[i];
AccumulatedTime += DurationPerVbriFrames;
i++;
}
// Searched too far; correct result
fraction = ( (int)(((( AccumulatedTime - EntryTimeInMilliSeconds ) / DurationPerVbriFrames )
+ (1.0f / (2.0f * (float)fFhgVbrInfo->VbriEntryFrames))) * (float)fFhgVbrInfo->VbriEntryFrames));
SeekPoint -= (int)((float)fFhgVbrInfo->VbriTable[i-1] * (float)(fraction)
/ (float)fFhgVbrInfo->VbriEntryFrames);
return SeekPoint;
}
int64
mp3Reader::VBRISeekPoint(float percent)
{
if (!fFhgVbrInfo)
return -1;
int SamplesPerFrame;
float TotalDuration;
if (percent >= 100.0f) percent = 100.0f;
if (percent <= 0.0f) percent = 0.0f;
(fFhgVbrInfo->SampleRate >= 32000) ? (SamplesPerFrame = 1152) : (SamplesPerFrame = 576);
TotalDuration = ((float)fFhgVbrInfo->VbriStreamFrames * (float)SamplesPerFrame)
/ (float)fFhgVbrInfo->SampleRate;
return VBRIseekPointByTime( (percent/100.0f) * TotalDuration * 1000.0f );
}
int

View File

@ -82,9 +82,17 @@ private:
int64 XingSeekPoint(float percent);
int64 VBRIseekPointByTime(float EntryTimeInMilliSeconds);
int64 VBRISeekPoint(float percent);
bool ResynchronizeStream(mp3data *data);
private:
status_t CalculatePosition(void* cookie, uint32 flags,
int64* frame, bigtime_t* time, off_t *position);
bool isVBR() {return (fXingVbrInfo || fFhgVbrInfo);}
BPositionIO * fSeekableSource;
int64 fFileSize;