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 KNOWN ISSUES
Edits lists are ignored the track is played start to finish regardless of edit lists 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 stop too early
IMA4 sound tracks repeat (no idea) 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 MOVFileReader.cpp
MOVAtom.cpp MOVAtom.cpp
MOVTrakAtom.cpp MOVTrakAtom.cpp
ChunkSuperIndex.cpp
; ;
@@ -101,6 +101,8 @@ public:
uint64 getAtomSize() {return atomSize;}; uint64 getAtomSize() {return atomSize;};
uint32 getAtomType() {return atomType;}; uint32 getAtomType() {return atomType;};
off_t getAtomOffset() {return atomOffset;};
off_t getStreamOffset() {return streamOffset;};
uint64 getBytesRemaining(); uint64 getBytesRemaining();
@@ -39,6 +39,7 @@ MOVFileReader::MOVFileReader(BPositionIO *pStream)
// Find Size of Stream, need to rethink this for non seekable streams // Find Size of Stream, need to rethink this for non seekable streams
theStream->Seek(0,SEEK_END); theStream->Seek(0,SEEK_END);
StreamSize = theStream->Position(); StreamSize = theStream->Position();
theStream->Seek(0,SEEK_SET); theStream->Seek(0,SEEK_SET);
TotalChildren = 0; TotalChildren = 0;
@@ -51,6 +52,19 @@ MOVFileReader::~MOVFileReader()
theMVHDAtom = NULL; 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) bool MOVFileReader::IsEndOfFile(off_t pPosition)
{ {
return (pPosition >= StreamSize); return (pPosition >= StreamSize);
@@ -332,6 +346,28 @@ uint64 MOVFileReader::getOffsetForFrame(uint32 stream_index, uint32 pFrameNo)
return 0; 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() status_t MOVFileReader::ParseFile()
{ {
AtomBase *aChild; AtomBase *aChild;
@@ -347,6 +383,8 @@ status_t MOVFileReader::ParseFile()
atomChildren[i]->DisplayAtoms(); atomChildren[i]->DisplayAtoms();
} }
BuildSuperIndex();
return B_OK; return B_OK;
} }
@@ -514,22 +552,29 @@ uint32 MOVFileReader::getChunkSize(uint32 stream_index, uint32 pFrameNo)
if (IsAudio(stream_index)) { if (IsAudio(stream_index)) {
// We read audio in chunk by chunk so chunk size is chunk size // 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 // Get first sample in chunk
uint32 SampleNo = aTrakAtom->getFirstSampleInChunk(ChunkNo); uint32 SampleNo = aTrakAtom->getFirstSampleInChunk(ChunkNo);
uint32 ChunkSize = 0;
if (aTrakAtom->IsSingleSampleSize()) { if (aTrakAtom->IsSingleSampleSize()) {
ChunkSize = aTrakAtom->getNoSamplesInChunk(ChunkNo) * aTrakAtom->getSizeForSample(SampleNo); TotalSampleSize = NoSamples * aTrakAtom->getSizeForSample(SampleNo);
} else { } else {
// Add up all sample sizes in chunk // Add up all sample sizes in chunk
for (uint32 i=0;i<aTrakAtom->getNoSamplesInChunk(ChunkNo);i++) { for (uint32 i=0;i<NoSamples;i++) {
ChunkSize += aTrakAtom->getSizeForSample(SampleNo); TotalSampleSize += aTrakAtom->getSizeForSample(SampleNo);
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; return ChunkSize;
} }
@@ -568,7 +613,7 @@ bool MOVFileReader::GetNextChunkInfo(uint32 stream_index, uint32 pFrameNo, off_t
*keyframe = IsKeyFrame(stream_index, pFrameNo); *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) bool MOVFileReader::IsActive(uint32 stream_index)
@@ -31,6 +31,7 @@
#include <SupportDefs.h> #include <SupportDefs.h>
#include "MOVParser.h" #include "MOVParser.h"
#include "ChunkSuperIndex.h"
// QT MOV file reader (and maybe MP4 as well) // QT MOV file reader (and maybe MP4 as well)
class MOVFileReader { class MOVFileReader {
@@ -56,6 +57,10 @@ private:
MVHDAtom *theMVHDAtom; MVHDAtom *theMVHDAtom;
ChunkSuperIndex theChunkSuperIndex;
void BuildSuperIndex();
public: public:
MOVFileReader(BPositionIO *pStream); MOVFileReader(BPositionIO *pStream);
virtual ~MOVFileReader(); virtual ~MOVFileReader();
@@ -65,6 +70,7 @@ public:
bool IsEndOfFile(off_t pPosition); bool IsEndOfFile(off_t pPosition);
bool IsEndOfFile(); bool IsEndOfFile();
bool IsEndOfData(off_t pPosition);
// Is this a quicktime file // Is this a quicktime file
static bool IsSupported(BPositionIO *source); static bool IsSupported(BPositionIO *source);
@@ -516,11 +516,12 @@ uint32 STSCAtom::getNoSamplesInChunk(uint32 pChunkID)
{ {
for (uint32 i=0;i<theHeader.NoEntries;i++) { for (uint32 i=0;i<theHeader.NoEntries;i++) {
if (theSampleToChunkArray[i]->FirstChunk > pChunkID) { if (theSampleToChunkArray[i]->FirstChunk > pChunkID) {
// printf("Chunk %ld contains %ld samples\n",pChunkID, theSampleToChunkArray[i-1]->SamplesPerChunk);
return theSampleToChunkArray[i-1]->SamplesPerChunk; return theSampleToChunkArray[i-1]->SamplesPerChunk;
} }
} }
return 0; return theSampleToChunkArray[theHeader.NoEntries-1]->SamplesPerChunk;
} }
uint32 STSCAtom::getFirstSampleInChunk(uint32 pChunkID) uint32 STSCAtom::getFirstSampleInChunk(uint32 pChunkID)
@@ -646,6 +647,7 @@ SampleSize *aSampleSize;
char *STSZAtom::OnGetAtomName() char *STSZAtom::OnGetAtomName()
{ {
printf("%ld ",theHeader.SampleSize);
return "Sample Size Atom"; return "Sample Size Atom";
} }
@@ -720,7 +722,7 @@ uint64 STCOAtom::getOffsetForChunk(uint32 pChunkID)
DEBUGGER(("Bad Chunk ID %ld / %ld\n",pChunkID,theHeader.NoEntries)); DEBUGGER(("Bad Chunk ID %ld / %ld\n",pChunkID,theHeader.NoEntries));
TRESPASS(); TRESPASS();
return theChunkToOffsetArray[0]->Offset; return -1LL;
} }
STSDAtom::STSDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) 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); aSoundDescriptionV1->desc.SampleRate = B_BENDIAN_TO_HOST_INT32(aSoundDescriptionV1->desc.SampleRate);
if (aSoundDescriptionV1->desc.Version == 1) { if (aSoundDescriptionV1->desc.Version == 1) {
printf("V1 Sound\n");
theStream->Read(&(aSoundDescriptionV1->samplesPerPacket),4); theStream->Read(&(aSoundDescriptionV1->samplesPerPacket),4);
theStream->Read(&(aSoundDescriptionV1->bytesPerPacket),4); theStream->Read(&(aSoundDescriptionV1->bytesPerPacket),4);
theStream->Read(&(aSoundDescriptionV1->bytesPerFrame),4); theStream->Read(&(aSoundDescriptionV1->bytesPerFrame),4);
@@ -791,11 +794,17 @@ void STSDAtom::ReadSoundDescription()
descBytesLeft = descBytesLeft - 16; descBytesLeft = descBytesLeft - 16;
} else { } else {
printf("V0 Sound\n");
// Calculate? // Calculate?
aSoundDescriptionV1->samplesPerPacket = 0; aSoundDescriptionV1->bytesPerSample = aSoundDescriptionV1->desc.SampleSize / 8;
aSoundDescriptionV1->bytesPerPacket = 0; if (aSoundDescriptionV1->basefields.DataFormat == 'ima4') {
aSoundDescriptionV1->bytesPerFrame = 0; printf("IMA4\n");
aSoundDescriptionV1->bytesPerSample = 0; 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 // 0 means we dont have one
@@ -1043,6 +1052,11 @@ char *MDATAtom::OnGetAtomName()
return "Media Data Atom"; 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) MINFAtom::MINFAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomContainer(pStream, pstreamOffset, patomType, patomSize)
{ {
} }
@@ -194,6 +194,7 @@ public:
char *OnGetAtomName(); char *OnGetAtomName();
uint64 getOffsetForChunk(uint32 pChunkID); uint64 getOffsetForChunk(uint32 pChunkID);
uint32 getTotalChunks() {return theHeader.NoEntries;};
protected: protected:
// Read a single chunk offset from Stream // Read a single chunk offset from Stream
@@ -250,7 +251,7 @@ public:
virtual ~MDATAtom(); virtual ~MDATAtom();
void OnProcessMetaData(); void OnProcessMetaData();
char *OnGetAtomName(); char *OnGetAtomName();
off_t getEOF();
}; };
// Atom class for reading the sdst atom // Atom class for reading the sdst atom
@@ -361,10 +362,14 @@ public:
uint32 getFirstSampleInChunk(uint32 pChunkID); uint32 getFirstSampleInChunk(uint32 pChunkID);
uint32 getSizeForSample(uint32 pSample); uint32 getSizeForSample(uint32 pSample);
uint32 getNoSamplesInChunk(uint32 pChunkID); uint32 getNoSamplesInChunk(uint32 pChunkID);
uint32 getTotalChunks();
bool IsSyncSample(uint32 pSampleNo); bool IsSyncSample(uint32 pSampleNo);
bool IsSingleSampleSize(); bool IsSingleSampleSize();
bool IsActive(); bool IsActive();
uint32 getBytesPerSample();
TKHDAtom *getTKHDAtom(); TKHDAtom *getTKHDAtom();
MDHDAtom *getMDHDAtom(); MDHDAtom *getMDHDAtom();
private: private:
@@ -372,6 +377,7 @@ private:
MDHDAtom *theMDHDAtom; MDHDAtom *theMDHDAtom;
uint32 framecount; uint32 framecount;
uint32 bytespersample;
}; };
// Atom class for reading the media container atom // Atom class for reading the media container atom
@@ -29,6 +29,7 @@ TRAKAtom::TRAKAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType,
theTKHDAtom = NULL; theTKHDAtom = NULL;
theMDHDAtom = NULL; theMDHDAtom = NULL;
framecount = 0; framecount = 0;
bytespersample = 0;
} }
TRAKAtom::~TRAKAtom() TRAKAtom::~TRAKAtom()
@@ -226,5 +227,41 @@ bool TRAKAtom::IsActive()
return getTKHDAtom()->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 // GetAudioMetaData() // If this is a audio track get the audio meta data
// GetVideoMetaData() // If this is a video track get the video meta data // GetVideoMetaData() // If this is a video track get the video meta data