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

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

View File

@ -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;
}

View File

@ -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

View File

@ -5,4 +5,5 @@ StaticLibrary movreader :
MOVFileReader.cpp
MOVAtom.cpp
MOVTrakAtom.cpp
ChunkSuperIndex.cpp
;

View File

@ -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();

View File

@ -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)

View File

@ -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);

View File

@ -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)
{
}

View File

@ -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

View File

@ -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