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:
parent
9e4b755834
commit
32661d2e83
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user