improved handling of compressed audio

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13811 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
David McPaul
2005-07-24 06:11:23 +00:00
parent 9e4b755834
commit 32661d2e83
10 changed files with 245 additions and 16 deletions
+2 -2
View File
@@ -4,5 +4,5 @@ Quicktime Reader TODO
KNOWN ISSUES
Edits lists are ignored the track is played start to finish regardless of edit lists
MP3 sound tracks create a noise at irregular intervals (too little data supplied to decoder?)
IMA4 sound tracks repeat (no idea)
IMA4 sound tracks stop too early
Seeking will not work for compressed audio
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2005, David McPaul
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "ChunkSuperIndex.h"
ChunkSuperIndex::ChunkSuperIndex()
{
}
ChunkSuperIndex::~ChunkSuperIndex()
{
theChunkIndexArray.erase(theChunkIndexArray.begin(),theChunkIndexArray.end());
}
void ChunkSuperIndex::AddChunkIndex(uint32 stream, uint32 chunkid, off_t chunk_start)
{
ChunkIndexPtr aChunkIndexPtr;
aChunkIndexPtr = new ChunkIndex;
aChunkIndexPtr->stream = stream;
aChunkIndexPtr->chunkid = chunkid;
aChunkIndexPtr->chunk_start = chunk_start;
theChunkIndexArray[chunk_start] = aChunkIndexPtr;
}
uint32 ChunkSuperIndex::getChunkSize(uint32 stream, uint32 chunkid, off_t chunk_start)
{
ChunkIndexPtr aChunkIndexPtr = theChunkIndexArray[chunk_start];
if (aChunkIndexPtr) {
// Find next chunkIndex
ChunkIndexPtr nextChunkIndex;
nextChunkIndex = theChunkIndexArray.upper_bound(chunk_start)->second;
// Assert stream
// Assert chunkid
return (nextChunkIndex->chunk_start - aChunkIndexPtr->chunk_start);
}
return 0;
}
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2005, David McPaul
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _CHUNK_INDEX_H
#define _CHUNK_INDEX_H
#include <SupportDefs.h>
// Std Headers
#include <map.h>
struct ChunkIndex {
uint32 stream;
uint32 chunkid;
off_t chunk_start;
};
typedef ChunkIndex* ChunkIndexPtr;
typedef std::map<off_t, ChunkIndexPtr, std::less<off_t> > ChunkIndexArray;
class ChunkSuperIndex {
public:
ChunkSuperIndex();
virtual ~ChunkSuperIndex();
void AddChunkIndex(uint32 stream, uint32 chunkid, off_t chunk_start);
uint32 getChunkSize(uint32 stream, uint32 chunkid, off_t chunk_start);
private:
ChunkIndexArray theChunkIndexArray;
};
#endif // _CHUNK_INDEX_H
@@ -5,4 +5,5 @@ StaticLibrary movreader :
MOVFileReader.cpp
MOVAtom.cpp
MOVTrakAtom.cpp
ChunkSuperIndex.cpp
;
@@ -101,6 +101,8 @@ public:
uint64 getAtomSize() {return atomSize;};
uint32 getAtomType() {return atomType;};
off_t getAtomOffset() {return atomOffset;};
off_t getStreamOffset() {return streamOffset;};
uint64 getBytesRemaining();
@@ -39,6 +39,7 @@ MOVFileReader::MOVFileReader(BPositionIO *pStream)
// Find Size of Stream, need to rethink this for non seekable streams
theStream->Seek(0,SEEK_END);
StreamSize = theStream->Position();
theStream->Seek(0,SEEK_SET);
TotalChildren = 0;
@@ -51,6 +52,19 @@ MOVFileReader::~MOVFileReader()
theMVHDAtom = NULL;
}
bool MOVFileReader::IsEndOfData(off_t pPosition)
{
AtomBase *aAtomBase;
aAtomBase = GetChildAtom(uint32('mdat'),0);
if (aAtomBase) {
MDATAtom *aMdatAtom = dynamic_cast<MDATAtom *>(aAtomBase);
return pPosition >= aMdatAtom->getEOF();
}
return true;
}
bool MOVFileReader::IsEndOfFile(off_t pPosition)
{
return (pPosition >= StreamSize);
@@ -332,6 +346,28 @@ uint64 MOVFileReader::getOffsetForFrame(uint32 stream_index, uint32 pFrameNo)
return 0;
}
void MOVFileReader::BuildSuperIndex()
{
AtomBase *aAtomBase;
for (uint32 stream=0;stream<getStreamCount();stream++) {
aAtomBase = GetChildAtom(uint32('trak'),stream);
if (aAtomBase) {
TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase);
for (uint32 chunkid=1;chunkid<=aTrakAtom->getTotalChunks();chunkid++) {
theChunkSuperIndex.AddChunkIndex(stream,chunkid,aTrakAtom->getOffsetForChunk(chunkid));
}
}
}
// Add end of file to index
aAtomBase = GetChildAtom(uint32('mdat'),0);
if (aAtomBase) {
MDATAtom *aMdatAtom = dynamic_cast<MDATAtom *>(aAtomBase);
theChunkSuperIndex.AddChunkIndex(0,0,aMdatAtom->getEOF());
}
}
status_t MOVFileReader::ParseFile()
{
AtomBase *aChild;
@@ -347,6 +383,8 @@ status_t MOVFileReader::ParseFile()
atomChildren[i]->DisplayAtoms();
}
BuildSuperIndex();
return B_OK;
}
@@ -514,22 +552,29 @@ uint32 MOVFileReader::getChunkSize(uint32 stream_index, uint32 pFrameNo)
if (IsAudio(stream_index)) {
// We read audio in chunk by chunk so chunk size is chunk size
uint32 ChunkNo = pFrameNo;
uint32 ChunkNo = pFrameNo;
off_t Chunk_Start = aTrakAtom->getOffsetForChunk(ChunkNo);
uint32 ChunkSize = ChunkSize = theChunkSuperIndex.getChunkSize(stream_index,ChunkNo,Chunk_Start);
uint32 NoSamples = aTrakAtom->getNoSamplesInChunk(ChunkNo);
uint32 TotalSampleSize = 0;
// Get first sample in chunk
uint32 SampleNo = aTrakAtom->getFirstSampleInChunk(ChunkNo);
uint32 ChunkSize = 0;
if (aTrakAtom->IsSingleSampleSize()) {
ChunkSize = aTrakAtom->getNoSamplesInChunk(ChunkNo) * aTrakAtom->getSizeForSample(SampleNo);
TotalSampleSize = NoSamples * aTrakAtom->getSizeForSample(SampleNo);
} else {
// Add up all sample sizes in chunk
for (uint32 i=0;i<aTrakAtom->getNoSamplesInChunk(ChunkNo);i++) {
ChunkSize += aTrakAtom->getSizeForSample(SampleNo);
for (uint32 i=0;i<NoSamples;i++) {
TotalSampleSize += aTrakAtom->getSizeForSample(SampleNo);
SampleNo++;
}
}
TotalSampleSize = TotalSampleSize * aTrakAtom->getBytesPerSample();
// printf("[%ld] start %lld, size %ld NoSamples %ld TotalSampleSize %ld\n",ChunkNo,Chunk_Start,ChunkSize,NoSamples,TotalSampleSize);
return ChunkSize;
}
@@ -568,7 +613,7 @@ bool MOVFileReader::GetNextChunkInfo(uint32 stream_index, uint32 pFrameNo, off_t
*keyframe = IsKeyFrame(stream_index, pFrameNo);
}
return ((*start > 0) && (*size > 0) && !(IsEndOfFile(*start)));
return ((*start > 0) && (*size > 0) && !(IsEndOfFile(*start + *size)) && !(IsEndOfData(*start + *size)));
}
bool MOVFileReader::IsActive(uint32 stream_index)
@@ -31,6 +31,7 @@
#include <SupportDefs.h>
#include "MOVParser.h"
#include "ChunkSuperIndex.h"
// QT MOV file reader (and maybe MP4 as well)
class MOVFileReader {
@@ -56,6 +57,10 @@ private:
MVHDAtom *theMVHDAtom;
ChunkSuperIndex theChunkSuperIndex;
void BuildSuperIndex();
public:
MOVFileReader(BPositionIO *pStream);
virtual ~MOVFileReader();
@@ -65,6 +70,7 @@ public:
bool IsEndOfFile(off_t pPosition);
bool IsEndOfFile();
bool IsEndOfData(off_t pPosition);
// Is this a quicktime file
static bool IsSupported(BPositionIO *source);
@@ -516,11 +516,12 @@ uint32 STSCAtom::getNoSamplesInChunk(uint32 pChunkID)
{
for (uint32 i=0;i<theHeader.NoEntries;i++) {
if (theSampleToChunkArray[i]->FirstChunk > pChunkID) {
// printf("Chunk %ld contains %ld samples\n",pChunkID, theSampleToChunkArray[i-1]->SamplesPerChunk);
return theSampleToChunkArray[i-1]->SamplesPerChunk;
}
}
return 0;
return theSampleToChunkArray[theHeader.NoEntries-1]->SamplesPerChunk;
}
uint32 STSCAtom::getFirstSampleInChunk(uint32 pChunkID)
@@ -646,6 +647,7 @@ SampleSize *aSampleSize;
char *STSZAtom::OnGetAtomName()
{
printf("%ld ",theHeader.SampleSize);
return "Sample Size Atom";
}
@@ -720,7 +722,7 @@ uint64 STCOAtom::getOffsetForChunk(uint32 pChunkID)
DEBUGGER(("Bad Chunk ID %ld / %ld\n",pChunkID,theHeader.NoEntries));
TRESPASS();
return theChunkToOffsetArray[0]->Offset;
return -1LL;
}
STSDAtom::STSDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize)
@@ -779,6 +781,7 @@ void STSDAtom::ReadSoundDescription()
aSoundDescriptionV1->desc.SampleRate = B_BENDIAN_TO_HOST_INT32(aSoundDescriptionV1->desc.SampleRate);
if (aSoundDescriptionV1->desc.Version == 1) {
printf("V1 Sound\n");
theStream->Read(&(aSoundDescriptionV1->samplesPerPacket),4);
theStream->Read(&(aSoundDescriptionV1->bytesPerPacket),4);
theStream->Read(&(aSoundDescriptionV1->bytesPerFrame),4);
@@ -791,11 +794,17 @@ void STSDAtom::ReadSoundDescription()
descBytesLeft = descBytesLeft - 16;
} else {
printf("V0 Sound\n");
// Calculate?
aSoundDescriptionV1->samplesPerPacket = 0;
aSoundDescriptionV1->bytesPerPacket = 0;
aSoundDescriptionV1->bytesPerFrame = 0;
aSoundDescriptionV1->bytesPerSample = 0;
aSoundDescriptionV1->bytesPerSample = aSoundDescriptionV1->desc.SampleSize / 8;
if (aSoundDescriptionV1->basefields.DataFormat == 'ima4') {
printf("IMA4\n");
aSoundDescriptionV1->bytesPerFrame = aSoundDescriptionV1->desc.NoOfChannels * 64 / 34;
} else {
aSoundDescriptionV1->bytesPerFrame = aSoundDescriptionV1->desc.NoOfChannels * aSoundDescriptionV1->bytesPerSample;
}
aSoundDescriptionV1->bytesPerPacket = aSoundDescriptionV1->desc.PacketSize;
aSoundDescriptionV1->samplesPerPacket = aSoundDescriptionV1->desc.PacketSize / aSoundDescriptionV1->bytesPerFrame;
}
// 0 means we dont have one
@@ -1043,6 +1052,11 @@ char *MDATAtom::OnGetAtomName()
return "Media Data Atom";
}
off_t MDATAtom::getEOF()
{
return getStreamOffset() + getAtomSize();
}
MINFAtom::MINFAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomContainer(pStream, pstreamOffset, patomType, patomSize)
{
}
@@ -194,6 +194,7 @@ public:
char *OnGetAtomName();
uint64 getOffsetForChunk(uint32 pChunkID);
uint32 getTotalChunks() {return theHeader.NoEntries;};
protected:
// Read a single chunk offset from Stream
@@ -250,7 +251,7 @@ public:
virtual ~MDATAtom();
void OnProcessMetaData();
char *OnGetAtomName();
off_t getEOF();
};
// Atom class for reading the sdst atom
@@ -361,10 +362,14 @@ public:
uint32 getFirstSampleInChunk(uint32 pChunkID);
uint32 getSizeForSample(uint32 pSample);
uint32 getNoSamplesInChunk(uint32 pChunkID);
uint32 getTotalChunks();
bool IsSyncSample(uint32 pSampleNo);
bool IsSingleSampleSize();
bool IsActive();
uint32 getBytesPerSample();
TKHDAtom *getTKHDAtom();
MDHDAtom *getMDHDAtom();
private:
@@ -372,6 +377,7 @@ private:
MDHDAtom *theMDHDAtom;
uint32 framecount;
uint32 bytespersample;
};
// Atom class for reading the media container atom
@@ -29,6 +29,7 @@ TRAKAtom::TRAKAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType,
theTKHDAtom = NULL;
theMDHDAtom = NULL;
framecount = 0;
bytespersample = 0;
}
TRAKAtom::~TRAKAtom()
@@ -226,5 +227,41 @@ bool TRAKAtom::IsActive()
return getTKHDAtom()->IsActive();
}
uint32 TRAKAtom::getBytesPerSample()
{
AtomBase *aAtomBase;
if (bytespersample > 0) {
return bytespersample;
}
// calculate bytes per frame and cache it
if (IsAudio()) {
// only used by Audio
aAtomBase = GetChildAtom(uint32('stsd'),0);
if (aAtomBase) {
STSDAtom *aSTSDAtom = dynamic_cast<STSDAtom *>(aAtomBase);
SoundDescriptionV1 aSoundDescription = aSTSDAtom->getAsAudio();
bytespersample = aSoundDescription.bytesPerSample;
}
}
return bytespersample;
}
uint32 TRAKAtom::getTotalChunks()
{
AtomBase *aAtomBase = GetChildAtom(uint32('stco'),0);
if (aAtomBase) {
return (dynamic_cast<STCOAtom *>(aAtomBase))->getTotalChunks();
}
return 0;
}
// GetAudioMetaData() // If this is a audio track get the audio meta data
// GetVideoMetaData() // If this is a video track get the video meta data