bring haiku mp4_reader up to date with BeOS mp4_extractor

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25340 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
David McPaul 2008-05-07 10:08:31 +00:00
parent a2e55dcc0c
commit 899c7641d4
9 changed files with 614 additions and 329 deletions

View File

@ -41,6 +41,17 @@ AtomBase::~AtomBase()
parentAtom = NULL; parentAtom = NULL;
} }
char *AtomBase::getAtomTypeAsFourcc()
{
fourcc[0] = (char)((atomType >> 24) & 0xff);
fourcc[1] = (char)((atomType >> 16) & 0xff);
fourcc[2] = (char)((atomType >> 8) & 0xff);
fourcc[3] = (char)((atomType >> 0) & 0xff);
fourcc[4] = '\0';
return fourcc;
}
char *AtomBase::getAtomName() char *AtomBase::getAtomName()
{ {
char *_result; char *_result;
@ -209,6 +220,36 @@ void AtomBase::Read(uint8 *value, uint32 maxread)
// Assert((bytes_read == maxread,"Read Error"); // Assert((bytes_read == maxread,"Read Error");
} }
uint64 AtomBase::GetBits(uint64 buffer, uint8 startBit, uint8 totalBits)
{
// startBit should range from 0-63, totalBits should range from 1-64
if ((startBit < 64) && (totalBits > 0) && (totalBits <= 64) && (startBit + totalBits <= 64)) {
// Ok pull from the buffer the bits wanted.
buffer = buffer << startBit;
buffer = buffer >> (64 - (totalBits + startBit) + startBit);
printf("buffer = %Ld\n",buffer);
return buffer;
}
return 0L;
}
uint32 AtomBase::GetBits(uint32 buffer, uint8 startBit, uint8 totalBits)
{
// startBit should range from 0-31, totalBits should range from 1-32
if ((startBit < 32) && (totalBits > 0) && (totalBits <= 32) && (startBit + totalBits <= 32)) {
// Ok pull from the buffer the bits wanted.
buffer = buffer << startBit;
buffer = buffer >> (32 - (startBit + totalBits) + startBit);
return buffer;
}
return 0;
}
FullAtom::FullAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) FullAtom::FullAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize)
{ {
} }

View File

@ -101,17 +101,18 @@ public:
uint64 getAtomSize() {return atomSize;}; uint64 getAtomSize() {return atomSize;};
uint32 getAtomType() {return atomType;}; uint32 getAtomType() {return atomType;};
off_t getAtomOffset() {return atomOffset;}; char *getAtomTypeAsFourcc();
off_t getStreamOffset() {return streamOffset;}; off_t getAtomOffset() { return atomOffset; };
off_t getStreamOffset() { return streamOffset; };
uint64 getDataSize() {return atomSize - 8;}; uint64 getDataSize() { return atomSize - 8;};
uint64 getBytesRemaining(); uint64 getBytesRemaining();
bool IsType(uint32 patomType) {return patomType == atomType;}; bool IsType(uint32 patomType) { return patomType == atomType; };
void setAtomOffset(off_t patomOffset) {atomOffset = patomOffset;}; void setAtomOffset(off_t patomOffset) { atomOffset = patomOffset; };
void setStreamOffset(off_t pstreamOffset) {streamOffset = pstreamOffset;}; void setStreamOffset(off_t pstreamOffset) { streamOffset = pstreamOffset; };
char *getAtomName(); char *getAtomName();
@ -142,6 +143,9 @@ public:
void Read(uint8 *value); void Read(uint8 *value);
void Read(char *value, uint32 maxread); void Read(char *value, uint32 maxread);
void Read(uint8 *value, uint32 maxread); void Read(uint8 *value, uint32 maxread);
uint64 GetBits(uint64 buffer, uint8 startBit, uint8 totalBits);
uint32 GetBits(uint32 buffer, uint8 startBit, uint8 totalBits);
}; };
class FullAtom : public AtomBase { class FullAtom : public AtomBase {

View File

@ -62,11 +62,15 @@ MP4FileReader::IsEndOfData(off_t pPosition)
{ {
AtomBase* aAtomBase; AtomBase* aAtomBase;
// check all mdat atoms to make sure pPosition is within one of them
for (uint32 index=0;index<CountChildAtoms('mdat');index++) { for (uint32 index=0;index<CountChildAtoms('mdat');index++) {
aAtomBase = GetChildAtom(uint32('mdat'),index); aAtomBase = GetChildAtom(uint32('mdat'),index);
if ((aAtomBase) && (aAtomBase->getAtomSize() > 8)) { if ((aAtomBase) && (aAtomBase->getAtomSize() > 8)) {
MDATAtom *aMdatAtom = dynamic_cast<MDATAtom *>(aAtomBase); MDATAtom *aMDATAtom = dynamic_cast<MDATAtom *>(aAtomBase);
return pPosition >= aMdatAtom->getEOF(); if (pPosition >= aMDATAtom->getAtomOffset() && pPosition <= aMDATAtom->getEOF()) {
return false;
}
} }
} }
@ -187,8 +191,7 @@ MP4FileReader::getMovieTimeScale()
bigtime_t bigtime_t
MP4FileReader::getMovieDuration() MP4FileReader::getMovieDuration()
{ {
return (bigtime_t(getMVHDAtom()->getDuration()) * 1000000L) return bigtime_t((getMVHDAtom()->getDuration() * 1000000.0) / getMovieTimeScale());
/ getMovieTimeScale();
} }
@ -261,24 +264,13 @@ MP4FileReader::getMaxDuration()
uint32 uint32
MP4FileReader::getVideoFrameCount(uint32 streamIndex) MP4FileReader::getFrameCount(uint32 streamIndex)
{ {
AtomBase *aAtomBase = GetChildAtom(uint32('trak'), streamIndex); AtomBase *aAtomBase = GetChildAtom(uint32('trak'), streamIndex);
if (aAtomBase && dynamic_cast<TRAKAtom *>(aAtomBase)->IsVideo()) if (aAtomBase) {
return dynamic_cast<TRAKAtom *>(aAtomBase)->FrameCount();
return 1;
}
uint32
MP4FileReader::getAudioFrameCount(uint32 streamIndex)
{
AtomBase *aAtomBase = GetChildAtom(uint32('trak'), streamIndex);
if (aAtomBase && dynamic_cast<TRAKAtom *>(aAtomBase)->IsAudio())
return dynamic_cast<TRAKAtom *>(aAtomBase)->FrameCount(); return dynamic_cast<TRAKAtom *>(aAtomBase)->FrameCount();
}
return 1; return 1;
} }
@ -368,8 +360,12 @@ MP4FileReader::getOffsetForFrame(uint32 streamIndex, uint32 pFrameNo)
TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase); TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase);
if (pFrameNo < aTrakAtom->FrameCount()) { if (pFrameNo < aTrakAtom->FrameCount()) {
// Get Sample for Frame // Get time for Frame
uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo); bigtime_t Time = aTrakAtom->getTimeForFrame(pFrameNo);
// Get Sample for Time
uint32 SampleNo = aTrakAtom->getSampleForTime(Time);
// Get Chunk For Sample and the offset for the frame within that chunk // Get Chunk For Sample and the offset for the frame within that chunk
uint32 OffsetInChunk; uint32 OffsetInChunk;
uint32 ChunkNo = aTrakAtom->getChunkForSample(SampleNo, &OffsetInChunk); uint32 ChunkNo = aTrakAtom->getChunkForSample(SampleNo, &OffsetInChunk);
@ -391,6 +387,8 @@ MP4FileReader::getOffsetForFrame(uint32 streamIndex, uint32 pFrameNo)
} }
} }
// printf("frame %ld, time %Ld, sample %ld, Chunk %ld, OffsetInChunk %ld, Offset %Ld\n",pFrameNo, Time, SampleNo, ChunkNo, OffsetInChunk, OffsetNo);
return OffsetNo; return OffsetNo;
} }
} }
@ -458,7 +456,7 @@ MP4FileReader::MovMainHeader()
} else { } else {
theMainHeader.width = VideoFormat(videoStream)->width; theMainHeader.width = VideoFormat(videoStream)->width;
theMainHeader.height = VideoFormat(videoStream)->height; theMainHeader.height = VideoFormat(videoStream)->height;
theMainHeader.total_frames = getVideoFrameCount(videoStream); theMainHeader.total_frames = getFrameCount(videoStream);
theMainHeader.suggested_buffer_size = theMainHeader.width * theMainHeader.height * VideoFormat(videoStream)->bit_count / 8; theMainHeader.suggested_buffer_size = theMainHeader.width * theMainHeader.height * VideoFormat(videoStream)->bit_count / 8;
theMainHeader.micro_sec_per_frame = uint32(1000000.0 / VideoFormat(videoStream)->FrameRate); theMainHeader.micro_sec_per_frame = uint32(1000000.0 / VideoFormat(videoStream)->FrameRate);
} }
@ -483,18 +481,33 @@ MP4FileReader::AudioFormat(uint32 streamIndex, size_t *size)
if (aAtomBase) { if (aAtomBase) {
STSDAtom *aSTSDAtom = dynamic_cast<STSDAtom *>(aAtomBase); STSDAtom *aSTSDAtom = dynamic_cast<STSDAtom *>(aAtomBase);
// Fill In a wave_format_ex structure // Fill in the AudioMetaData structure
AudioDescription aAudioDescription = aSTSDAtom->getAsAudio(); AudioDescription aAudioDescription = aSTSDAtom->getAsAudio();
theAudio.compression = aAudioDescription.codecid; theAudio.compression = aAudioDescription.codecid;
theAudio.codecSubType = aAudioDescription.codecSubType;
theAudio.NoOfChannels = aAudioDescription.theAudioSampleEntry.ChannelCount; theAudio.NoOfChannels = aAudioDescription.theAudioSampleEntry.ChannelCount;
theAudio.SampleSize = aAudioDescription.theAudioSampleEntry.SampleSize;
theAudio.SampleRate = aAudioDescription.theAudioSampleEntry.SampleRate / 65536; // Convert from fixed point decimal to float
theAudio.PacketSize = uint32((theAudio.SampleSize * theAudio.NoOfChannels * 1024) / 8);
theAudio.theVOL = aAudioDescription.theVOL; // Fix for broken mp4's with 0 SampleSize, default to 16 bits
theAudio.VOLSize = aAudioDescription.VOLSize; if (aAudioDescription.theAudioSampleEntry.SampleSize == 0) {
theAudio.SampleSize = 16;
} else {
theAudio.SampleSize = aAudioDescription.theAudioSampleEntry.SampleSize;
}
theAudio.SampleRate = aAudioDescription.theAudioSampleEntry.SampleRate / 65536; // Convert from fixed point decimal to float
theAudio.FrameSize = aAudioDescription.FrameSize;
if (aAudioDescription.BufferSize == 0) {
theAudio.BufferSize = uint32((theAudio.SampleSize * theAudio.NoOfChannels * theAudio.FrameSize) / 8);
} else {
theAudio.BufferSize = aAudioDescription.BufferSize;
}
theAudio.BitRate = aAudioDescription.BitRate;
theAudio.theDecoderConfig = aAudioDescription.theDecoderConfig;
theAudio.DecoderConfigSize = aAudioDescription.DecoderConfigSize;
return &theAudio; return &theAudio;
} }
@ -519,25 +532,26 @@ MP4FileReader::VideoFormat(uint32 streamIndex)
VideoDescription aVideoDescription = aSTSDAtom->getAsVideo(); VideoDescription aVideoDescription = aSTSDAtom->getAsVideo();
theVideo.compression = aVideoDescription.codecid; theVideo.compression = aVideoDescription.codecid;
theVideo.codecSubType = aVideoDescription.codecSubType;
theVideo.width = aVideoDescription.theVideoSampleEntry.Width; theVideo.width = aVideoDescription.theVideoSampleEntry.Width;
theVideo.height = aVideoDescription.theVideoSampleEntry.Height; theVideo.height = aVideoDescription.theVideoSampleEntry.Height;
theVideo.planes = aVideoDescription.theVideoSampleEntry.Depth; theVideo.planes = aVideoDescription.theVideoSampleEntry.Depth;
theVideo.size = aVideoDescription.theVideoSampleEntry.Width * aVideoDescription.theVideoSampleEntry.Height * aVideoDescription.theVideoSampleEntry.Depth / 8; theVideo.BufferSize = aVideoDescription.theVideoSampleEntry.Width * aVideoDescription.theVideoSampleEntry.Height * aVideoDescription.theVideoSampleEntry.Depth / 8;
theVideo.bit_count = aVideoDescription.theVideoSampleEntry.Depth; theVideo.bit_count = aVideoDescription.theVideoSampleEntry.Depth;
theVideo.image_size = aVideoDescription.theVideoSampleEntry.Height * aVideoDescription.theVideoSampleEntry.Width; theVideo.image_size = aVideoDescription.theVideoSampleEntry.Height * aVideoDescription.theVideoSampleEntry.Width;
theVideo.HorizontalResolution = aVideoDescription.theVideoSampleEntry.HorizontalResolution; theVideo.HorizontalResolution = aVideoDescription.theVideoSampleEntry.HorizontalResolution;
theVideo.VerticalResolution = aVideoDescription.theVideoSampleEntry.VerticalResolution; theVideo.VerticalResolution = aVideoDescription.theVideoSampleEntry.VerticalResolution;
theVideo.FrameCount = aVideoDescription.theVideoSampleEntry.FrameCount; theVideo.FrameCount = aVideoDescription.theVideoSampleEntry.FrameCount;
theVideo.theVOL = aVideoDescription.theVOL; theVideo.theDecoderConfig = aVideoDescription.theDecoderConfig;
theVideo.VOLSize = aVideoDescription.VOLSize; theVideo.DecoderConfigSize = aVideoDescription.DecoderConfigSize;
aAtomBase = aTrakAtom->GetChildAtom(uint32('stts'),0); aAtomBase = aTrakAtom->GetChildAtom(uint32('stts'),0);
if (aAtomBase) { if (aAtomBase) {
STTSAtom *aSTTSAtom = dynamic_cast<STTSAtom *>(aAtomBase); STTSAtom *aSTTSAtom = dynamic_cast<STTSAtom *>(aAtomBase);
theVideo.FrameRate = ((aSTTSAtom->getSUMCounts() * 1000000.0L) / aTrakAtom->Duration(1)); theVideo.FrameRate = ((aSTTSAtom->getSUMCounts() * 1000000.0) / aTrakAtom->Duration(1));
return &theVideo; return &theVideo;
} }
@ -552,25 +566,25 @@ MP4FileReader::VideoFormat(uint32 streamIndex)
const mp4_stream_header* const mp4_stream_header*
MP4FileReader::StreamFormat(uint32 streamIndex) MP4FileReader::StreamFormat(uint32 streamIndex)
{ {
// Fill In a Stream Header
theStreamHeader.length = 0;
if (IsActive(streamIndex) == false) { if (IsActive(streamIndex) == false) {
return NULL; return NULL;
} }
// Fill In a Stream Header
theStreamHeader.length = 0;
if (IsVideo(streamIndex)) { if (IsVideo(streamIndex)) {
theStreamHeader.rate = uint32(1000000L*VideoFormat(streamIndex)->FrameRate); theStreamHeader.rate = uint32(1000000.0*VideoFormat(streamIndex)->FrameRate);
theStreamHeader.scale = 1000000L; theStreamHeader.scale = 1000000L;
theStreamHeader.length = getVideoFrameCount(streamIndex); theStreamHeader.length = getFrameCount(streamIndex);
} }
if (IsAudio(streamIndex)) { if (IsAudio(streamIndex)) {
theStreamHeader.rate = uint32(AudioFormat(streamIndex)->SampleRate); theStreamHeader.rate = uint32(AudioFormat(streamIndex)->SampleRate);
theStreamHeader.scale = 1; theStreamHeader.scale = 1;
theStreamHeader.length = getAudioFrameCount(streamIndex); theStreamHeader.length = getFrameCount(streamIndex);
theStreamHeader.sample_size = AudioFormat(streamIndex)->SampleSize; theStreamHeader.sample_size = AudioFormat(streamIndex)->SampleSize;
theStreamHeader.suggested_buffer_size = theStreamHeader.rate * theStreamHeader.sample_size; theStreamHeader.suggested_buffer_size = AudioFormat(streamIndex)->BufferSize;
} }
return &theStreamHeader; return &theStreamHeader;
@ -601,8 +615,7 @@ MP4FileReader::IsKeyFrame(uint32 streamIndex, uint32 pFrameNo)
if (aAtomBase) { if (aAtomBase) {
TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase); TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase);
uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo); return aTrakAtom->IsSyncSample(pFrameNo);
return aTrakAtom->IsSyncSample(SampleNo);
} }
return false; return false;
@ -620,6 +633,8 @@ MP4FileReader::GetNextChunkInfo(uint32 streamIndex, uint32 pFrameNo,
*keyframe = IsKeyFrame(streamIndex, pFrameNo); *keyframe = IsKeyFrame(streamIndex, pFrameNo);
} }
// printf("start %Ld, size %ld, eof %s, eod %s\n",*start,*size, IsEndOfFile(*start + *size) ? "true" : "false", IsEndOfData(*start + *size) ? "true" : "false");
// TODO need a better method for detecting End of Data Note ChunkSize of 0 seems to be it. // TODO need a better method for detecting End of Data Note ChunkSize of 0 seems to be it.
return *start > 0 && *size > 0 && !IsEndOfFile(*start + *size) return *start > 0 && *size > 0 && !IsEndOfFile(*start + *size)
&& !IsEndOfData(*start + *size); && !IsEndOfData(*start + *size);
@ -647,11 +662,18 @@ MP4FileReader::IsSupported(BPositionIO *source)
if (aAtom) { if (aAtom) {
if (dynamic_cast<FTYPAtom *>(aAtom)) { if (dynamic_cast<FTYPAtom *>(aAtom)) {
aAtom->ProcessMetaData(); aAtom->ProcessMetaData();
printf("ftyp atom found checking brands\n"); printf("ftyp atom found checking brands...");
// MP4 files start with a ftyp atom that does not contain a qt brand // MP4 files start with a ftyp atom that does not contain a qt brand
return !(dynamic_cast<FTYPAtom *>(aAtom)->HasBrand(uint32('qt '))); if (!dynamic_cast<FTYPAtom *>(aAtom)->HasBrand(uint32('qt '))) {
printf("no quicktime brand found must be mp4\n");
return true;
} else {
printf("quicktime brand found\n");
}
} }
} }
printf("NO ftyp atom found, cannot be mp4\n");
return false; return false;
} }

View File

@ -89,10 +89,7 @@ public:
bigtime_t getMaxDuration(); bigtime_t getMaxDuration();
// The no of frames in the video track indexed by streamIndex // The no of frames in the video track indexed by streamIndex
// 1 frame = 1 chunk uint32 getFrameCount(uint32 streamIndex);
uint32 getVideoFrameCount(uint32 streamIndex);
// The no of frames in the audio track indexed by streamIndex
uint32 getAudioFrameCount(uint32 streamIndex);
// The no of chunks in the audio track indexed by streamIndex // The no of chunks in the audio track indexed by streamIndex
uint32 getAudioChunkCount(uint32 streamIndex); uint32 getAudioChunkCount(uint32 streamIndex);
// Is stream (track) a video track // Is stream (track) a video track

View File

@ -189,6 +189,14 @@ AtomBase *getAtom(BPositionIO *pStream)
return new ESDSAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); return new ESDSAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize);
} }
if (aAtomType == uint32('alac')) {
return new ALACAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize);
}
if (aAtomType == uint32('wave')) {
return new WAVEAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize);
}
return new AtomBase(pStream, aStreamOffset, aAtomType, aRealAtomSize); return new AtomBase(pStream, aStreamOffset, aAtomType, aRealAtomSize);
} }
@ -477,6 +485,8 @@ TimeToSample *aTimeToSample;
ReadArrayHeader(&theHeader); ReadArrayHeader(&theHeader);
// printf("STTS:: Entries %ld\n",theHeader.NoEntries);
for (uint32 i=0;i<theHeader.NoEntries;i++) { for (uint32 i=0;i<theHeader.NoEntries;i++) {
aTimeToSample = new TimeToSample; aTimeToSample = new TimeToSample;
@ -486,7 +496,11 @@ TimeToSample *aTimeToSample;
theTimeToSampleArray[i] = aTimeToSample; theTimeToSampleArray[i] = aTimeToSample;
SUMDurations += (theTimeToSampleArray[i]->Duration * theTimeToSampleArray[i]->Count); SUMDurations += (theTimeToSampleArray[i]->Duration * theTimeToSampleArray[i]->Count);
SUMCounts += theTimeToSampleArray[i]->Count; SUMCounts += theTimeToSampleArray[i]->Count;
// printf("(%ld,%ld)",aTimeToSample->Count,aTimeToSample->Duration);
} }
// printf("\n");
} }
char *STTSAtom::OnGetAtomName() char *STTSAtom::OnGetAtomName()
@ -494,24 +508,30 @@ char *STTSAtom::OnGetAtomName()
return "Time to Sample Atom"; return "Time to Sample Atom";
} }
uint32 STTSAtom::getSampleForTime(uint32 pTime) uint32 STTSAtom::getSampleForTime(bigtime_t pTime)
{ {
// Sample for time is this calc, how does STTS help us?
return uint32((pTime * FrameRate + 50) / 1000000.0);
// TODO this is too slow. PreCalc when loading this? // TODO this is too slow. PreCalc when loading this?
uint64 Duration = 0; /* bigtime_t TotalDuration = 0;
uint64 TotalCount = 0;
for (uint32 i=0;i<theHeader.NoEntries;i++) { for (uint32 i=0;i<theHeader.NoEntries;i++) {
Duration += (theTimeToSampleArray[i]->Duration * theTimeToSampleArray[i]->Count); TotalDuration += (theTimeToSampleArray[i]->Duration * theTimeToSampleArray[i]->Count);
if (Duration > pTime) { TotalCount += theTimeToSampleArray[i]->Count;
return i; if ((TotalDuration * 44100) > pTime) {
return uint32((pTime * FrameRate + 50) / 1000000.0);
} }
} }
return 0; return 0; */
} }
uint32 STTSAtom::getSampleForFrame(uint32 pFrame) uint32 STTSAtom::getSampleForFrame(uint32 pFrame)
{ {
// Hmm Sample is Frame really, this Atom is more usefull for time->sample calcs // Convert frame to time and call getSampleForTime()
return pFrame; return pFrame;
} }
@ -574,6 +594,8 @@ SampleToChunk *aSampleToChunk;
uint32 TotalPrevSamples = 0; uint32 TotalPrevSamples = 0;
// printf("STSC:: Entries %ld\n",theHeader.NoEntries);
for (uint32 i=0;i<theHeader.NoEntries;i++) { for (uint32 i=0;i<theHeader.NoEntries;i++) {
aSampleToChunk = new SampleToChunk; aSampleToChunk = new SampleToChunk;
@ -588,8 +610,11 @@ SampleToChunk *aSampleToChunk;
aSampleToChunk->TotalPrevSamples = 0; aSampleToChunk->TotalPrevSamples = 0;
} }
// printf("(%ld,%ld)",aSampleToChunk->SamplesPerChunk, aSampleToChunk->TotalPrevSamples);
theSampleToChunkArray[i] = aSampleToChunk; theSampleToChunkArray[i] = aSampleToChunk;
} }
// printf("\n");
} }
char *STSCAtom::OnGetAtomName() char *STSCAtom::OnGetAtomName()
@ -688,7 +713,7 @@ bool STSSAtom::IsSyncSample(uint32 pSampleNo)
return true; return true;
} }
if (pSampleNo > theSyncSampleArray[i]->SyncSampleNo) { if (pSampleNo < theSyncSampleArray[i]->SyncSampleNo) {
return false; return false;
} }
} }
@ -908,28 +933,42 @@ uint64 STCOAtom::getOffsetForChunk(uint32 pChunkID)
return 0LL; return 0LL;
} }
ESDSAtom::ESDSAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) DecoderConfigAtom::DecoderConfigAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize)
{ {
theVOL = NULL; theDecoderConfig = NULL;
DecoderConfigSize = 0;
} }
ESDSAtom::~ESDSAtom() DecoderConfigAtom::~DecoderConfigAtom()
{ {
if (theVOL) { if (theDecoderConfig) {
free(theVOL); free(theDecoderConfig);
} }
} }
bool ESDSAtom::SkipTag(uint8 Tag, uint32 *offset) { void DecoderConfigAtom::OnProcessMetaData()
{
DecoderConfigSize = getBytesRemaining();
theDecoderConfig = (uint8 *)(malloc(DecoderConfigSize));
Read(theDecoderConfig,DecoderConfigSize);
}
uint8 *DecoderConfigAtom::getDecoderConfig()
{
return theDecoderConfig;
}
bool DecoderConfigAtom::SkipTag(uint8 *ESDS, uint8 Tag, uint32 *offset) {
uint8 byte; uint8 byte;
byte = theVOL[(*offset)++]; byte = ESDS[(*offset)++];
if (byte == Tag) { if (byte == Tag) {
uint8 numBytes = 0; uint8 numBytes = 0;
unsigned int length = 0; unsigned int length = 0;
do { do {
byte = theVOL[(*offset)++]; byte = ESDS[(*offset)++];
numBytes++; numBytes++;
length = (length << 7) | (byte & 0x7F); length = (length << 7) | (byte & 0x7F);
} while ((byte & 0x80) && numBytes < 4); } while ((byte & 0x80) && numBytes < 4);
@ -937,99 +976,51 @@ bool ESDSAtom::SkipTag(uint8 Tag, uint32 *offset) {
} else { } else {
// go back Tag not found // go back Tag not found
(*offset)--; (*offset)--;
printf("No Tag %d\n",Tag);
return false; return false;
} }
return true; return true;
} }
char *DecoderConfigAtom::OnGetAtomName()
{
return "Decoder Config Atom - Unknown type";
}
void DecoderConfigAtom::OverrideAudioDescription(AudioDescription *pAudioDescription)
{
if (pAudioDescription) {
pAudioDescription->DecoderConfigSize = DecoderConfigSize;
pAudioDescription->theDecoderConfig = (uint8 *)(malloc(DecoderConfigSize));
memcpy(pAudioDescription->theDecoderConfig, theDecoderConfig, DecoderConfigSize);
OnOverrideAudioDescription(pAudioDescription);
}
}
void DecoderConfigAtom::OverrideVideoDescription(VideoDescription *pVideoDescription)
{
if (pVideoDescription) {
pVideoDescription->DecoderConfigSize = DecoderConfigSize;
pVideoDescription->theDecoderConfig = (uint8 *)(malloc(DecoderConfigSize));
memcpy(pVideoDescription->theDecoderConfig, theDecoderConfig, DecoderConfigSize);
OnOverrideVideoDescription(pVideoDescription);
}
}
ESDSAtom::ESDSAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : DecoderConfigAtom(pStream, pstreamOffset, patomType, patomSize)
{
}
ESDSAtom::~ESDSAtom()
{
}
void ESDSAtom::OnProcessMetaData() void ESDSAtom::OnProcessMetaData()
{ {
FullAtom::OnProcessMetaData(); // Read 4 bytes because this is really a FullAtom
uint32 version;
VOLSize = getBytesRemaining(); Read(&version);
uint32 offset = 0; DecoderConfigAtom::OnProcessMetaData();
theVOL = (uint8 *)(malloc(VOLSize));
Read(theVOL,VOLSize);
// Display the VOL
if (VOLSize > 0) {
if (SkipTag(0x03,&offset)) {
offset += 3;
} else {
offset += 2;
}
if (SkipTag(0x04,&offset)) {
printf("type id is %d", theVOL[offset]);
switch (theVOL[offset]) {
case 1: printf("system v1"); break;
case 2: printf("system v2"); break;
case 32: printf("MPEG-4 video"); break;
case 33: printf("MPEG-4 AVC SPS"); break;
case 34: printf("MPEG-4 AVC PPS"); break;
case 64: printf("MPEG-4 audio"); break;
}
offset+=2;
uint32 a,b,c;
uint32 buff;
a = theVOL[offset];
b = theVOL[offset+1];
c = theVOL[offset+2];
buff = (a << 16) | (b << 8) | c;
printf(" buf size %ld ",buff);
printf(" max bit rate %ld",uint32(theVOL[offset+4]));
printf(" avg bit rate %ld\n",uint32(theVOL[offset+8]));
}
} else {
printf("VOL size is 0\n");
}
}
size_t ESDSAtom::getVOLSize(bool forAudio)
{
uint32 offset = 0;
if (forAudio) {
if (SkipTag(0x03,&offset)) {
offset += 3;
} else {
offset += 2;
}
if (SkipTag(0x04,&offset)) {
offset += 13;
}
SkipTag(0x05,&offset);
}
return ( VOLSize - offset);
}
uint8 *ESDSAtom::getVOL(bool forAudio)
{
uint32 offset = 0;
if (forAudio) {
if (SkipTag(0x03,&offset)) {
offset += 3;
} else {
offset += 2;
}
if (SkipTag(0x04,&offset)) {
offset += 13;
}
SkipTag(0x05,&offset);
}
return &theVOL[offset];
} }
char *ESDSAtom::OnGetAtomName() char *ESDSAtom::OnGetAtomName()
@ -1037,23 +1028,191 @@ char *ESDSAtom::OnGetAtomName()
return "Extended Sample Description Atom"; return "Extended Sample Description Atom";
} }
char *obj_type_names[]=
{
"Unknown",
"Main-AAC",
"LC-AAC",
"SSR-AAC",
"LTP-AAC",
"HE-AAC",
"HE-AAC(disabled)"
};
int aac_sampling_rate[16] =
{
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000, 7350, 0, 0, 0
};
void ESDSAtom::OnOverrideAudioDescription(AudioDescription *pAudioDescription)
{
// decode for aac and check for HE-AAC which uses a framesize of 2048
// also check for MP3 which has an ESDS
uint32 offset = 0;
if (SkipTag(pAudioDescription->theDecoderConfig, 0x03, &offset)) {
offset += 3;
} else {
offset += 2;
}
if (SkipTag(pAudioDescription->theDecoderConfig, 0x04, &offset)) {
ESDSType = pAudioDescription->theDecoderConfig[offset];
StreamType = pAudioDescription->theDecoderConfig[offset+1]/4;
NeededBufferSize = uint32(pAudioDescription->theDecoderConfig[offset+2]) * 256 * 256 +
pAudioDescription->theDecoderConfig[offset+3] * 256 + pAudioDescription->theDecoderConfig[offset+4];
MaxBitRate = uint32(pAudioDescription->theDecoderConfig[offset+5]) * 256 * 256 * 256 +
pAudioDescription->theDecoderConfig[offset+6] * 256 * 256 +
pAudioDescription->theDecoderConfig[offset+7] * 256 +
pAudioDescription->theDecoderConfig[offset+8];
AvgBitRate = uint32(pAudioDescription->theDecoderConfig[offset+9]) * 256 * 256 * 256 +
pAudioDescription->theDecoderConfig[offset+10] * 256 * 256 +
pAudioDescription->theDecoderConfig[offset+11] * 256 +
pAudioDescription->theDecoderConfig[offset+12];
printf("obj type id is %d \n", ESDSType);
printf("stream type is %d\n", StreamType);
printf("Buffer Size is %ld\n", NeededBufferSize);
printf("max Bitrate is %ld\n", MaxBitRate);
printf("avg Bitrate is %ld\n", AvgBitRate);
pAudioDescription->BitRate = AvgBitRate;
offset+=13;
}
SkipTag(pAudioDescription->theDecoderConfig, 0x05, &offset);
uint32 buffer;
uint32 temp;
switch (ESDSType) {
case 64: // AAC
// Attempt to read AAC Header
buffer = pAudioDescription->theDecoderConfig[offset];
buffer = buffer * 256 + pAudioDescription->theDecoderConfig[offset+1] ;
buffer = buffer * 256 + pAudioDescription->theDecoderConfig[offset+2] ;
buffer = buffer * 256 + pAudioDescription->theDecoderConfig[offset+3] ;
// Bits 0-5 are decoder type
temp = GetBits(buffer,0,5);
theAACHeader.objTypeIndex = temp;
// Bits 6-9 are frequency index
temp = GetBits(buffer,6,3);
theAACHeader.sampleRateIndex = temp;
// Bits 10-12 are channels
temp = GetBits(buffer,10,3);
theAACHeader.totalChannels = temp;
if (theAACHeader.objTypeIndex < 3) {
pAudioDescription->codecSubType = 'mp4a';
pAudioDescription->FrameSize = 1024;
} else {
pAudioDescription->codecSubType = 'haac';
pAudioDescription->FrameSize = 2048;
}
// SampleRate is in 16.16 format
pAudioDescription->theAudioSampleEntry.SampleRate = aac_sampling_rate[theAACHeader.sampleRateIndex] * 65536 ;
pAudioDescription->theAudioSampleEntry.ChannelCount = theAACHeader.totalChannels;
break;
case 107: // MP3
pAudioDescription->codecSubType = 'mp3';
// Only set this for mp3, we calc it normally
if (NeededBufferSize > pAudioDescription->BufferSize) {
pAudioDescription->BufferSize = NeededBufferSize;
}
break;
}
}
void ESDSAtom::OnOverrideVideoDescription(VideoDescription *pVideoDescription)
{
// Nothing to override
}
ALACAtom::ALACAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : DecoderConfigAtom(pStream, pstreamOffset, patomType, patomSize)
{
}
ALACAtom::~ALACAtom()
{
}
void ALACAtom::OnProcessMetaData()
{
DecoderConfigAtom::OnProcessMetaData();
}
char *ALACAtom::OnGetAtomName()
{
return "ALAC Decoder Config Atom";
}
void ALACAtom::OnOverrideAudioDescription(AudioDescription *pAudioDescription)
{
pAudioDescription->codecSubType = 'alac';
pAudioDescription->FrameSize = 4096;
}
void ALACAtom::OnOverrideVideoDescription(VideoDescription *pVideoDescription)
{
// Nothing to override
}
WAVEAtom::WAVEAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : DecoderConfigAtom(pStream, pstreamOffset, patomType, patomSize)
{
}
WAVEAtom::~WAVEAtom()
{
}
void WAVEAtom::OnProcessMetaData()
{
}
char *WAVEAtom::OnGetAtomName()
{
return "WAVE Decoder Config Atom";
}
void WAVEAtom::OnOverrideAudioDescription(AudioDescription *pAudioDescription)
{
pAudioDescription->codecSubType = 'alac';
pAudioDescription->FrameSize = 4096;
}
void WAVEAtom::OnOverrideVideoDescription(VideoDescription *pVideoDescription)
{
// Nothing to override
}
STSDAtom::STSDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) STSDAtom::STSDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize)
{ {
theHeader.NoEntries = 0; theHeader.NoEntries = 0;
theAudioDescription.theVOL = NULL; theAudioDescription.codecid = 0;
theVideoDescription.theVOL = NULL; theAudioDescription.codecSubType = 0;
theAudioDescription.FrameSize = 0;
theAudioDescription.BufferSize = 0;
theAudioDescription.BitRate = 0;
theAudioDescription.theDecoderConfig = NULL;
theVideoDescription.theDecoderConfig = NULL;
} }
STSDAtom::~STSDAtom() STSDAtom::~STSDAtom()
{ {
if (theAudioDescription.theVOL) { if (theAudioDescription.theDecoderConfig) {
free(theAudioDescription.theVOL); free(theAudioDescription.theDecoderConfig);
theAudioDescription.theVOL = NULL; theAudioDescription.theDecoderConfig = NULL;
} }
if (theVideoDescription.theVOL) { if (theVideoDescription.theDecoderConfig) {
free(theVideoDescription.theVOL); free(theVideoDescription.theDecoderConfig);
theVideoDescription.theVOL = NULL; theVideoDescription.theDecoderConfig = NULL;
} }
} }
@ -1062,12 +1221,14 @@ uint32 STSDAtom::getMediaHandlerType()
return dynamic_cast<STBLAtom *>(getParent())->getMediaHandlerType(); return dynamic_cast<STBLAtom *>(getParent())->getMediaHandlerType();
} }
void STSDAtom::ReadESDS(uint8 **VOL, size_t *VOLSize, bool forAudio) void STSDAtom::ReadDecoderConfig(uint8 **pDecoderConfig, size_t *pDecoderConfigSize, AudioDescription *pAudioDescription, VideoDescription *pVideoDescription)
{ {
// Check for a esds and if it exists read it into the VOL buffer // Check for a Decoder Config and if it exists copy it back to the caller
// MPEG-4 video/audio use the esds to store the VOL (STUPID COMMITTEE IDEA) // MPEG-4 video/audio use the various decoder config structures to pass additional
// decoder information to the decoder. The extractor sometimes needs to decode the data
// to work out how to properly construct the decoder
// First make sure we have a esds // First make sure we have a something
if (getBytesRemaining() > 0) { if (getBytesRemaining() > 0) {
// Well something is there so read it as an atom // Well something is there so read it as an atom
AtomBase *aAtomBase = getAtom(theStream); AtomBase *aAtomBase = getAtom(theStream);
@ -1075,11 +1236,11 @@ void STSDAtom::ReadESDS(uint8 **VOL, size_t *VOLSize, bool forAudio)
aAtomBase->ProcessMetaData(); aAtomBase->ProcessMetaData();
printf("%s [%Ld]\n",aAtomBase->getAtomName(),aAtomBase->getAtomSize()); printf("%s [%Ld]\n",aAtomBase->getAtomName(),aAtomBase->getAtomSize());
if (dynamic_cast<ESDSAtom *>(aAtomBase)) { if (dynamic_cast<DecoderConfigAtom *>(aAtomBase)) {
// ESDS atom good // DecoderConfig atom good
*VOLSize = dynamic_cast<ESDSAtom *>(aAtomBase)->getVOLSize(forAudio); DecoderConfigAtom *aDecoderConfigAtom = dynamic_cast<DecoderConfigAtom *>(aAtomBase);
*VOL = (uint8 *)(malloc(*VOLSize)); aDecoderConfigAtom->OverrideAudioDescription(pAudioDescription);
memcpy(*VOL,dynamic_cast<ESDSAtom *>(aAtomBase)->getVOL(forAudio),*VOLSize); aDecoderConfigAtom->OverrideVideoDescription(pVideoDescription);
delete aAtomBase; delete aAtomBase;
} else { } else {
@ -1099,7 +1260,7 @@ void STSDAtom::ReadSoundDescription()
Read(&theAudioDescription.theAudioSampleEntry.reserved); Read(&theAudioDescription.theAudioSampleEntry.reserved);
Read(&theAudioDescription.theAudioSampleEntry.SampleRate); Read(&theAudioDescription.theAudioSampleEntry.SampleRate);
ReadESDS(&theAudioDescription.theVOL,&theAudioDescription.VOLSize,true); ReadDecoderConfig(&theAudioDescription.theDecoderConfig, &theAudioDescription.DecoderConfigSize, &theAudioDescription, NULL);
} }
void STSDAtom::ReadVideoDescription() void STSDAtom::ReadVideoDescription()
@ -1129,7 +1290,7 @@ void STSDAtom::ReadVideoDescription()
Read(&theVideoDescription.theVideoSampleEntry.Depth); Read(&theVideoDescription.theVideoSampleEntry.Depth);
Read(&theVideoDescription.theVideoSampleEntry.pre_defined3); Read(&theVideoDescription.theVideoSampleEntry.pre_defined3);
ReadESDS(&theVideoDescription.theVOL,&theVideoDescription.VOLSize, false); ReadDecoderConfig(&theVideoDescription.theDecoderConfig, &theVideoDescription.DecoderConfigSize, NULL, &theVideoDescription);
} }
void STSDAtom::OnProcessMetaData() void STSDAtom::OnProcessMetaData()
@ -1331,13 +1492,13 @@ void MDATAtom::OnProcessMetaData()
char *MDATAtom::OnGetAtomName() char *MDATAtom::OnGetAtomName()
{ {
printf("Offset %lld, Size %lld ",getStreamOffset(),getAtomSize()); printf("Offset %lld, Size %lld ",getAtomOffset(),getAtomSize() - 8);
return "Media Data Atom"; return "Media Data Atom";
} }
off_t MDATAtom::getEOF() off_t MDATAtom::getEOF()
{ {
return getStreamOffset() + getAtomSize(); return getAtomOffset() + getAtomSize() - 8;
} }
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)
@ -1549,7 +1710,7 @@ char *MDHDAtom::OnGetAtomName()
bigtime_t MDHDAtom::getDuration() bigtime_t MDHDAtom::getDuration()
{ {
return bigtime_t((uint64(theHeader.Duration) * 1000000L) / theHeader.TimeScale); return bigtime_t((theHeader.Duration * 1000000.0) / theHeader.TimeScale);
} }
uint32 MDHDAtom::getTimeScale() uint32 MDHDAtom::getTimeScale()

View File

@ -171,15 +171,18 @@ public:
void OnProcessMetaData(); void OnProcessMetaData();
char *OnGetAtomName(); char *OnGetAtomName();
uint64 getSUMCounts() {return SUMCounts;}; uint64 getSUMCounts() { return SUMCounts; };
uint64 getSUMDurations() {return SUMDurations;}; uint64 getSUMDurations() { return SUMDurations; };
uint32 getSampleForTime(uint32 pTime); uint32 getSampleForTime(bigtime_t pTime);
uint32 getSampleForFrame(uint32 pFrame); uint32 getSampleForFrame(uint32 pFrame);
void setFrameRate(float pFrameRate) { FrameRate = pFrameRate; };
private: private:
array_header theHeader; array_header theHeader;
TimeToSampleArray theTimeToSampleArray; TimeToSampleArray theTimeToSampleArray;
uint64 SUMDurations; bigtime_t SUMDurations;
uint64 SUMCounts; uint64 SUMCounts;
float FrameRate;
}; };
// Atom class for reading the composition time to sample atom // Atom class for reading the composition time to sample atom
@ -300,22 +303,72 @@ public:
off_t getEOF(); off_t getEOF();
}; };
class ESDSAtom : public FullAtom { // Subclass for handling special decoder config atoms like ESDS, ALAC, AVCC, AMR
// ESDS is actually a FullAtom but others are not :-(
class DecoderConfigAtom : public AtomBase {
public:
DecoderConfigAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize);
virtual ~DecoderConfigAtom();
virtual void OnProcessMetaData();
virtual char *OnGetAtomName();
// The decoder config is what the decoder uses for it's setup
// The values SHOULD mirror the Container values but often don't
// So we have to parse the config and use the values as container overrides
void OverrideAudioDescription(AudioDescription *pAudioDescription);
virtual void OnOverrideAudioDescription(AudioDescription *pAudioDescription) {} ;
void OverrideVideoDescription(VideoDescription *pVideoDescription);
virtual void OnOverrideVideoDescription(VideoDescription *pVideoDescription) {} ;
bool SkipTag(uint8 *ESDS, uint8 Tag, uint32 *offset);
uint8 *getDecoderConfig();
size_t getDecoderConfigSize() { return DecoderConfigSize; } ;
private:
uint8 *theDecoderConfig;
size_t DecoderConfigSize;
};
// Atom class for reading the ESDS decoder config atom
class ESDSAtom : public DecoderConfigAtom {
public: public:
ESDSAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); ESDSAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize);
virtual ~ESDSAtom(); virtual ~ESDSAtom();
void OnProcessMetaData(); void OnProcessMetaData();
char *OnGetAtomName(); char *OnGetAtomName();
void OnOverrideAudioDescription(AudioDescription *pAudioDescription);
uint8 *getVOL(bool forAudio); void OnOverrideVideoDescription(VideoDescription *pVideoDescription);
size_t getVOLSize(bool forAudio);
private: private:
bool SkipTag(uint8 Tag, uint32 *offset); uint8 ESDSType;
uint8 *theVOL; uint8 StreamType;
size_t VOLSize; uint32 NeededBufferSize;
uint32 MaxBitRate;
uint32 AvgBitRate;
AACHeader theAACHeader;
}; };
// Atom class for reading the sdst atom // Atom class for reading the ALAC decoder config atom
class ALACAtom : public DecoderConfigAtom {
public:
ALACAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize);
virtual ~ALACAtom();
void OnProcessMetaData();
char *OnGetAtomName();
void OnOverrideAudioDescription(AudioDescription *pAudioDescription);
void OnOverrideVideoDescription(VideoDescription *pVideoDescription);
};
// Atom class for reading the WAVE atom for mp3 decoder config
class WAVEAtom : public DecoderConfigAtom {
public:
WAVEAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize);
virtual ~WAVEAtom();
void OnProcessMetaData();
char *OnGetAtomName();
void OnOverrideAudioDescription(AudioDescription *pAudioDescription);
void OnOverrideVideoDescription(VideoDescription *pVideoDescription);
};
// Atom class for reading the Sample Description atom
class STSDAtom : public FullAtom { class STSDAtom : public FullAtom {
public: public:
STSDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); STSDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize);
@ -329,10 +382,10 @@ public:
private: private:
uint32 getMediaHandlerType(); uint32 getMediaHandlerType();
void ReadESDS(uint8 **VOL, size_t *VOLSize, bool forAudio); void ReadDecoderConfig(uint8 **pDecoderConfig, size_t *pDecoderConfigSize, AudioDescription *pAudioDescription, VideoDescription *pVideoDescription);
void ReadSoundDescription(); void ReadSoundDescription();
void ReadVideoDescription(); void ReadVideoDescription();
void ReadHintDescription();
uint32 codecid; uint32 codecid;
@ -416,14 +469,15 @@ public:
void OnChildProcessingComplete(); void OnChildProcessingComplete();
bigtime_t Duration(uint32 TimeScale); // Return duration of track bigtime_t Duration(uint32 TimeScale); // Return duration of track
uint32 FrameCount() {return framecount;}; bigtime_t Duration() { return Duration(timescale); }
uint32 FrameCount() { return framecount; };
bool IsVideo(); // Is this a video track bool IsVideo(); // Is this a video track
bool IsAudio(); // Is this a audio track bool IsAudio(); // Is this a audio track
// 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
uint32 getTimeForFrame(uint32 pFrame, uint32 pTimeScale); bigtime_t getTimeForFrame(uint32 pFrame);
uint32 getSampleForTime(uint32 pTime); uint32 getSampleForTime(bigtime_t pTime);
uint32 getSampleForFrame(uint32 pFrame); uint32 getSampleForFrame(uint32 pFrame);
uint32 getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk); uint32 getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk);
uint64 getOffsetForChunk(uint32 pChunkID); uint64 getOffsetForChunk(uint32 pChunkID);
@ -432,6 +486,9 @@ public:
uint32 getNoSamplesInChunk(uint32 pChunkID); uint32 getNoSamplesInChunk(uint32 pChunkID);
uint32 getTotalChunks(); uint32 getTotalChunks();
float getSampleRate();
float getFrameRate();
bool IsSyncSample(uint32 pSampleNo); bool IsSyncSample(uint32 pSampleNo);
bool IsSingleSampleSize(); bool IsSingleSampleSize();
bool IsActive(); bool IsActive();
@ -446,6 +503,7 @@ private:
uint32 framecount; uint32 framecount;
uint32 bytespersample; uint32 bytespersample;
uint32 timescale;
}; };
// Atom class for reading the media container atom // Atom class for reading the media container atom

View File

@ -76,7 +76,8 @@ struct mp4_stream_header
struct VideoMetaData struct VideoMetaData
{ {
uint32 compression; uint32 compression;
uint32 size; uint32 codecSubType;
uint32 BufferSize;
uint32 width; uint32 width;
uint32 height; uint32 height;
uint16 planes; uint16 planes;
@ -85,20 +86,23 @@ struct VideoMetaData
uint32 HorizontalResolution; uint32 HorizontalResolution;
uint32 VerticalResolution; uint32 VerticalResolution;
uint32 FrameCount; uint32 FrameCount;
float FrameRate; float FrameRate; // Frames per second
uint8 *theVOL; uint8 *theDecoderConfig;
size_t VOLSize; size_t DecoderConfigSize;
}; };
struct AudioMetaData struct AudioMetaData
{ {
uint32 compression; // compression used uint32 compression; // compression used (ie codecid)
uint32 codecSubType; // Additional codecid
uint16 NoOfChannels; // 1 = mono, 2 = stereo uint16 NoOfChannels; // 1 = mono, 2 = stereo
uint16 SampleSize; // bits per sample uint16 SampleSize; // bits per sample
float SampleRate; // Samples per second (OR Frames per second) float SampleRate; // Samples per second
uint32 PacketSize; // (Sample Rate * NoOfchannels * SampleSize)/8 = bytes per second uint32 BufferSize; // (Sample Rate * NoOfchannels * SampleSize * SamplesPerFrame) / 8 = bytes per second
uint8 *theVOL; uint32 FrameSize; // No Of Samples in 1 frame of audio (SamplesPerFrame)
size_t VOLSize; uint32 BitRate; // Average Bitrate
uint8 *theDecoderConfig;
size_t DecoderConfigSize;
}; };
struct TimeToSample { struct TimeToSample {
@ -125,7 +129,7 @@ struct ChunkToOffset {
}; };
struct SyncSample { struct SyncSample {
uint64 SyncSampleNo; uint32 SyncSampleNo;
}; };
struct SampleSizeEntry { struct SampleSizeEntry {
@ -235,8 +239,6 @@ struct SampleEntry {
uint16 DataReference; uint16 DataReference;
}; };
#define SampleEntrySize sizeof(SampleEntry)
struct AudioSampleEntry { struct AudioSampleEntry {
uint32 Reserved[2]; uint32 Reserved[2];
uint16 ChannelCount; uint16 ChannelCount;
@ -246,13 +248,15 @@ struct AudioSampleEntry {
uint32 SampleRate; // 16.16 uint32 SampleRate; // 16.16
}; };
#define AudioSampleEntrySize sizeof(AudioSampleEntry)
struct AudioDescription { struct AudioDescription {
AudioSampleEntry theAudioSampleEntry; AudioSampleEntry theAudioSampleEntry;
uint32 codecid; uint32 codecid;
uint8 *theVOL; uint32 codecSubType;
size_t VOLSize; uint32 FrameSize;
uint32 BufferSize;
uint32 BitRate;
uint8 *theDecoderConfig;
size_t DecoderConfigSize;
}; };
struct VideoSampleEntry { struct VideoSampleEntry {
@ -270,13 +274,22 @@ struct VideoSampleEntry {
uint16 pre_defined3; uint16 pre_defined3;
}; };
#define VideoSampleEntrySize 70
struct VideoDescription { struct VideoDescription {
VideoSampleEntry theVideoSampleEntry; VideoSampleEntry theVideoSampleEntry;
uint32 codecid; uint32 codecid;
uint8 *theVOL; uint32 codecSubType;
size_t VOLSize; uint8 *theDecoderConfig;
size_t DecoderConfigSize;
};
// The parts of the AAC ESDS we care about
struct AACHeader {
uint8 objTypeIndex;
uint8 sampleRateIndex;
uint8 totalChannels;
uint16 frameSize;
uint16 adtsBuffer;
uint8 totalDataBlocksInFrame;
}; };
#endif // MP4_STRUCTS_H #endif // MP4_STRUCTS_H

View File

@ -30,6 +30,7 @@ TRAKAtom::TRAKAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType,
theMDHDAtom = NULL; theMDHDAtom = NULL;
framecount = 0; framecount = 0;
bytespersample = 0; bytespersample = 0;
timescale = 1;
} }
TRAKAtom::~TRAKAtom() TRAKAtom::~TRAKAtom()
@ -78,16 +79,20 @@ bigtime_t TRAKAtom::Duration(uint32 TimeScale)
return getMDHDAtom()->getDuration(); return getMDHDAtom()->getDuration();
} }
return bigtime_t(getTKHDAtom()->getDuration() * 1000000L) / TimeScale; return bigtime_t(getTKHDAtom()->getDuration() * 1000000.0) / TimeScale;
} }
void TRAKAtom::OnChildProcessingComplete() void TRAKAtom::OnChildProcessingComplete()
{ {
timescale = getMDHDAtom()->getTimeScale();
STTSAtom *aSTTSAtom = dynamic_cast<STTSAtom *>(GetChildAtom(uint32('stts'),0)); STTSAtom *aSTTSAtom = dynamic_cast<STTSAtom *>(GetChildAtom(uint32('stts'),0));
if (aSTTSAtom) { if (aSTTSAtom) {
framecount = aSTTSAtom->getSUMCounts(); framecount = aSTTSAtom->getSUMCounts();
aSTTSAtom->setFrameRate(getFrameRate());
} }
} }
// Is this a video track // Is this a video track
@ -104,25 +109,46 @@ bool TRAKAtom::IsAudio()
return (GetChildAtom(uint32('smhd'),0) != NULL); return (GetChildAtom(uint32('smhd'),0) != NULL);
} }
uint32 TRAKAtom::getTimeForFrame(uint32 pFrame, uint32 pTimeScale) // frames per us
float TRAKAtom::getFrameRate()
{ {
AtomBase *aAtomBase = GetChildAtom(uint32('stts'),0); if (IsAudio()) {
AtomBase *aAtomBase = GetChildAtom(uint32('stsd'),0);
if (aAtomBase) {
STSDAtom *aSTSDAtom = dynamic_cast<STSDAtom *>(aAtomBase);
if (aAtomBase) { AudioDescription aAudioDescription = aSTSDAtom->getAsAudio();
STTSAtom *aSTTSAtom = dynamic_cast<STTSAtom *>(aAtomBase); return (aAudioDescription.theAudioSampleEntry.SampleRate / 65536.0) / aAudioDescription.FrameSize;
}
} else if (IsVideo()) {
return (framecount * 1000000.0) / Duration();
}
if (IsAudio()) { return 0.0;
// Frame * SampleRate = Time }
} else if (IsVideo()) {
// Frame * fps = Time float TRAKAtom::getSampleRate()
return pFrame * ((aSTTSAtom->getSUMCounts() * 1000000L) / Duration(pTimeScale)); {
// Only valid for Audio
if (IsAudio()) {
AtomBase *aAtomBase = GetChildAtom(uint32('stsd'),0);
if (aAtomBase) {
STSDAtom *aSTSDAtom = dynamic_cast<STSDAtom *>(aAtomBase);
AudioDescription aAudioDescription = aSTSDAtom->getAsAudio();
return aAudioDescription.theAudioSampleEntry.SampleRate / 65536;
} }
} }
return 0; return 0.0;
} }
uint32 TRAKAtom::getSampleForTime(uint32 pTime) bigtime_t TRAKAtom::getTimeForFrame(uint32 pFrame)
{
return bigtime_t((pFrame * 1000000.0) / getFrameRate());
}
uint32 TRAKAtom::getSampleForTime(bigtime_t pTime)
{ {
AtomBase *aAtomBase = GetChildAtom(uint32('stts'),0); AtomBase *aAtomBase = GetChildAtom(uint32('stts'),0);
@ -138,7 +164,7 @@ uint32 TRAKAtom::getSampleForFrame(uint32 pFrame)
AtomBase *aAtomBase = GetChildAtom(uint32('stts'),0); AtomBase *aAtomBase = GetChildAtom(uint32('stts'),0);
if (aAtomBase) { if (aAtomBase) {
return (dynamic_cast<STTSAtom *>(aAtomBase))->getSampleForFrame(pFrame); return (dynamic_cast<STTSAtom *>(aAtomBase))->getSampleForTime(getTimeForFrame(pFrame));
} }
return 0; return 0;
@ -262,6 +288,3 @@ uint32 TRAKAtom::getTotalChunks()
return 0; 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

View File

@ -38,7 +38,7 @@
#include <string.h> #include <string.h>
//#define TRACE_MP4_READER #define TRACE_MP4_READER
#ifdef TRACE_MP4_READER #ifdef TRACE_MP4_READER
# define TRACE printf # define TRACE printf
#else #else
@ -60,8 +60,6 @@ struct mp4_cookie {
bool audio; bool audio;
// audio only: // audio only:
off_t byte_pos;
uint32 chunk_pos;
uint32 bytes_per_sec_rate; uint32 bytes_per_sec_rate;
uint32 bytes_per_sec_scale; uint32 bytes_per_sec_scale;
@ -145,6 +143,9 @@ mp4Reader::GetFileFormatInfo(media_file_format *mff)
status_t status_t
mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie) mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie)
{ {
size_t size;
const void *data;
mp4_cookie *cookie = new mp4_cookie; mp4_cookie *cookie = new mp4_cookie;
*_cookie = cookie; *_cookie = cookie;
@ -181,12 +182,10 @@ mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie)
return B_ERROR; return B_ERROR;
} }
cookie->frame_count = theFileReader->getAudioFrameCount(cookie->stream); cookie->frame_count = theFileReader->getFrameCount(cookie->stream);
cookie->duration = theFileReader->getAudioDuration(cookie->stream); cookie->duration = theFileReader->getAudioDuration(cookie->stream);
cookie->audio = true; cookie->audio = true;
cookie->byte_pos = 0;
cookie->chunk_pos = 1;
if (stream_header->scale && stream_header->rate) { if (stream_header->scale && stream_header->rate) {
cookie->bytes_per_sec_rate = stream_header->rate * cookie->bytes_per_sec_rate = stream_header->rate *
@ -196,6 +195,8 @@ mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie)
cookie->frames_per_sec_scale = stream_header->scale; cookie->frames_per_sec_scale = stream_header->scale;
TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using both)\n", TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using both)\n",
cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale); cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using both)\n",
cookie->frames_per_sec_rate, cookie->frames_per_sec_scale);
} else if (stream_header->rate) { } else if (stream_header->rate) {
cookie->bytes_per_sec_rate = stream_header->rate * audio_format->SampleSize cookie->bytes_per_sec_rate = stream_header->rate * audio_format->SampleSize
* audio_format->NoOfChannels / 8; * audio_format->NoOfChannels / 8;
@ -204,14 +205,8 @@ mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie)
cookie->frames_per_sec_scale = 1; cookie->frames_per_sec_scale = 1;
TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using rate)\n", TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using rate)\n",
cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale); cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
} else if (audio_format->PacketSize) { TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using rate)\n",
cookie->bytes_per_sec_rate = audio_format->PacketSize; cookie->frames_per_sec_rate, cookie->frames_per_sec_scale);
cookie->bytes_per_sec_scale = 1;
cookie->frames_per_sec_rate = audio_format->PacketSize * 8
/ audio_format->SampleSize / audio_format->NoOfChannels;
cookie->frames_per_sec_scale = 1;
TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using PacketSize)\n",
cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
} else { } else {
cookie->bytes_per_sec_rate = 128000; cookie->bytes_per_sec_rate = 128000;
cookie->bytes_per_sec_scale = 8; cookie->bytes_per_sec_scale = 8;
@ -294,10 +289,32 @@ mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie)
format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels; format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels;
break; break;
case 'mp4a': case 'mp4a':
TRACE("AAC Audio (mp4a)\n"); case 'alac':
format->u.encoded_audio.bit_rate = 8 * cookie->frames_per_sec_rate / cookie->frames_per_sec_scale; TRACE("AAC audio (mp4a) or ALAC audio\n");
format->u.encoded_audio.output.frame_rate = cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;
format->u.encoded_audio.output.frame_rate = cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;;
format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels; format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels;
format->u.encoded_audio.output.format = media_raw_audio_format::B_AUDIO_SHORT;
format->u.encoded_audio.output.byte_order = B_HOST_IS_LENDIAN ? B_MEDIA_LITTLE_ENDIAN : B_MEDIA_BIG_ENDIAN;
// AAC is 1024 samples per frame
// HE-AAC is 2048 samples per frame
// ALAC is 4096 samples per frame
format->u.encoded_audio.frame_size = audio_format->FrameSize;
format->u.encoded_audio.output.buffer_size = audio_format->BufferSize;
// Average BitRate = (TotalBytes * 8 * (SampleRate / FrameSize)) / TotalFrames
// Setting a bitrate seems to cause more problems than it solves
format->u.encoded_audio.bit_rate = audio_format->BitRate; // usually 128000 for AAC
printf("Audio NoOfChannels %d, SampleSize %d, SampleRate %f, FrameSize %ld\n",audio_format->NoOfChannels, audio_format->SampleSize, audio_format->SampleRate, audio_format->FrameSize);
printf("Audio frame_rate %f, channel_count %ld, format %ld, buffer_size %ld, frame_size %ld, bit_rate %f\n",
format->u.encoded_audio.output.frame_rate, format->u.encoded_audio.output.channel_count, format->u.encoded_audio.output.format,format->u.encoded_audio.output.buffer_size, format->u.encoded_audio.frame_size, format->u.encoded_audio.bit_rate);
printf("Track %d MP4 Audio FrameCount %ld\n",cookie->stream,theFileReader->getFrameCount(cookie->stream));
break; break;
default: default:
char cc1,cc2,cc3,cc4; char cc1,cc2,cc3,cc4;
@ -315,49 +332,18 @@ mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie)
} }
} }
/* if (audio_format->compression == 0x0001) {
// a raw PCM format
description.family = B_BEOS_FORMAT_FAMILY;
description.u.beos.format = B_BEOS_FORMAT_RAW_AUDIO;
if (B_OK != formats.GetFormatFor(description, format))
format->type = B_MEDIA_RAW_AUDIO;
format->u.raw_audio.frame_rate = audio_format->SampleRate;
format->u.raw_audio.channel_count = audio_format->NoOfChannels;
if (audio_format->bits_per_sample <= 8)
format->u.raw_audio.format = B_AUDIO_FORMAT_UINT8;
else if (audio_format->bits_per_sample <= 16)
format->u.raw_audio.format = B_AUDIO_FORMAT_INT16;
else if (audio_format->bits_per_sample <= 24)
format->u.raw_audio.format = B_AUDIO_FORMAT_INT24;
else if (audio_format->bits_per_sample <= 32)
format->u.raw_audio.format = B_AUDIO_FORMAT_INT32;
else {
ERROR("movReader::AllocateCookie: unhandled bits per sample %d\n", audio_format->bits_per_sample);
return B_ERROR;
}
format->u.raw_audio.format |= B_AUDIO_FORMAT_CHANNEL_ORDER_WAVE;
format->u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN;
format->u.raw_audio.buffer_size = stream_header->suggested_buffer_size;
} else {
// some encoded format
description.family = B_WAV_FORMAT_FAMILY;
description.u.wav.codec = audio_format->compression;
if (B_OK != formats.GetFormatFor(description, format))
format->type = B_MEDIA_ENCODED_AUDIO;
format->u.encoded_audio.bit_rate = 8 * audio_format->PacketSize;
TRACE("bit_rate %.3f\n", format->u.encoded_audio.bit_rate);
format->u.encoded_audio.output.frame_rate = audio_format->SampleRate;
format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels;
}
*/
// this doesn't seem to work (it's not even a fourcc) // this doesn't seem to work (it's not even a fourcc)
format->user_data_type = B_CODEC_TYPE_INFO; format->user_data_type = B_CODEC_TYPE_INFO;
*(uint32 *)format->user_data = audio_format->compression; format->user_data[4] = 0; *(uint32 *)format->user_data = audio_format->compression; format->user_data[4] = 0;
// Set the VOL // Set the DecoderConfigSize
size_t size = audio_format->VOLSize; size = audio_format->DecoderConfigSize;
const void *data = audio_format->theVOL; data = audio_format->theDecoderConfig;
format->SetMetaData(data, size); if (size > 0) {
if (format->SetMetaData(data, size) != B_OK) {
printf("Failed to set Decoder Config\n");
}
}
#ifdef TRACE_MP4_READER #ifdef TRACE_MP4_READER
if (data) { if (data) {
@ -463,13 +449,13 @@ mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie)
TRACE("max_bit_rate %.3f\n", format->u.encoded_video.max_bit_rate); TRACE("max_bit_rate %.3f\n", format->u.encoded_video.max_bit_rate);
TRACE("field_rate %.3f\n", format->u.encoded_video.output.field_rate); TRACE("field_rate %.3f\n", format->u.encoded_video.output.field_rate);
// Set the VOL // Set the Decoder Config
if (video_format->VOLSize > 0) { size = video_format->DecoderConfigSize;
TRACE("VOL Found Size is %ld\n",video_format->VOLSize); data = video_format->theDecoderConfig;
size_t size = video_format->VOLSize; if (size > 0) {
const void *data = video_format->theVOL; TRACE("Decoder Config Found Size is %ld\n",size);
if (format->SetMetaData(data, size) != B_OK) { if (format->SetMetaData(data, size) != B_OK) {
ERROR("Failed to set VOL\n %d %d",(16 << 20),32000); printf("Failed to set Decoder Config\n");
} }
#ifdef TRACE_MP4_READER #ifdef TRACE_MP4_READER
@ -514,12 +500,12 @@ mp4Reader::GetStreamInfo(void *_cookie, int64 *frameCount, bigtime_t *duration,
// Copy metadata to infoBuffer // Copy metadata to infoBuffer
if (theFileReader->IsVideo(cookie->stream)) { if (theFileReader->IsVideo(cookie->stream)) {
const VideoMetaData *video_format = theFileReader->VideoFormat(cookie->stream); const VideoMetaData *video_format = theFileReader->VideoFormat(cookie->stream);
*infoBuffer = video_format->theVOL; *infoBuffer = video_format->theDecoderConfig;
*infoSize = video_format->VOLSize; *infoSize = video_format->DecoderConfigSize;
} else { } else {
const AudioMetaData *audio_format = theFileReader->AudioFormat(cookie->stream); const AudioMetaData *audio_format = theFileReader->AudioFormat(cookie->stream);
*infoBuffer = audio_format->theVOL; *infoBuffer = audio_format->theDecoderConfig;
*infoSize = audio_format->VOLSize; *infoSize = audio_format->DecoderConfigSize;
} }
return B_OK; return B_OK;
} }
@ -538,16 +524,16 @@ mp4Reader::Seek(void *cookie, uint32 seekTo, int64 *frame, bigtime_t *time)
// frame = (time * rate) / fps / 1000000LL // frame = (time * rate) / fps / 1000000LL
*frame = ((*time * mp4cookie->frames_per_sec_rate) / (int64)mp4cookie->frames_per_sec_scale) / 1000000LL; *frame = ((*time * mp4cookie->frames_per_sec_rate) / (int64)mp4cookie->frames_per_sec_scale) / 1000000LL;
TRACE("Time %Ld to Frame %Ld\n",*time, *frame); TRACE("Time %Ld to Frame %Ld\n",*time, *frame);
// movcookie->frame_pos = *frame; mp4cookie->frame_pos = *frame;
// return B_ERROR; return B_OK;
} }
if (seekTo & B_MEDIA_SEEK_TO_FRAME) { if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
// time = frame * 1000000LL * fps / rate // time = frame * 1000000LL * fps / rate
TRACE("Frame %Ld to Time %Ld\n", *frame, *time); TRACE("Frame %Ld to Time %Ld\n", *frame, *time);
*time = (*frame * 1000000LL * (int64)mp4cookie->frames_per_sec_scale) / mp4cookie->frames_per_sec_rate; *time = (*frame * 1000000LL * (int64)mp4cookie->frames_per_sec_scale) / mp4cookie->frames_per_sec_rate;
// movcookie->frame_pos = *frame; mp4cookie->frame_pos = *frame;
// return B_ERROR; return B_OK;
} }
TRACE("mp4Reader::Seek: seekTo%s%s%s%s, time %Ld, frame %Ld\n", TRACE("mp4Reader::Seek: seekTo%s%s%s%s, time %Ld, frame %Ld\n",
@ -570,16 +556,9 @@ mp4Reader::GetNextChunk(void *_cookie, const void **chunkBuffer,
uint32 size; uint32 size;
bool keyframe; bool keyframe;
if (cookie->audio) { if (theFileReader->GetNextChunkInfo(cookie->stream, cookie->frame_pos, &start, &size, &keyframe) == false) {
// use chunk position TRACE("LAST BUFFER ERROR\n");
if (!theFileReader->GetNextChunkInfo(cookie->stream, cookie->chunk_pos, return B_LAST_BUFFER_ERROR;
&start, &size, &keyframe))
return B_LAST_BUFFER_ERROR;
} else {
// use frame position
if (!theFileReader->GetNextChunkInfo(cookie->stream, cookie->frame_pos,
&start, &size, &keyframe))
return B_LAST_BUFFER_ERROR;
} }
if (cookie->buffer_size < size) { if (cookie->buffer_size < size) {
@ -589,36 +568,23 @@ mp4Reader::GetNextChunk(void *_cookie, const void **chunkBuffer,
} }
if (cookie->audio) { if (cookie->audio) {
TRACE("Audio stream %d: chunk %ld expected start time %lld output size %ld key %d\n",cookie->stream, cookie->chunk_pos, start, size, keyframe); // TRACE("Audio stream %d: frame %ld expected start time %lld output size %ld key %d\n",cookie->stream, cookie->frame_pos, start, size, keyframe);
mediaHeader->type = B_MEDIA_ENCODED_AUDIO; mediaHeader->type = B_MEDIA_ENCODED_AUDIO;
mediaHeader->u.encoded_audio.buffer_flags = keyframe ? B_MEDIA_KEY_FRAME : 0; mediaHeader->u.encoded_audio.buffer_flags = keyframe ? B_MEDIA_KEY_FRAME : 0;
// This will only work with raw audio I think.
mediaHeader->start_time = (cookie->byte_pos * 1000000LL * cookie->bytes_per_sec_scale) / cookie->bytes_per_sec_rate;
TRACE("Audio - Frames in Chunk %ld / Actual Start Time %Ld using byte_pos\n",theFileReader->getNoFramesInChunk(cookie->stream,cookie->chunk_pos),mediaHeader->start_time);
// We should find the current frame position (ie first frame in chunk) then compute using fps
cookie->frame_pos = theFileReader->getFirstFrameInChunk(cookie->stream,cookie->chunk_pos);
mediaHeader->start_time = (cookie->frame_pos * 1000000LL * (int64)cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate;
TRACE("Audio - Frames in Chunk %ld / Actual Start Time %Ld using frame_no %ld\n",theFileReader->getNoFramesInChunk(cookie->stream,cookie->chunk_pos),mediaHeader->start_time, cookie->frame_pos);
cookie->byte_pos += size;
// frame_pos is chunk No for audio
cookie->chunk_pos++;
} else { } else {
TRACE("Video stream %d: frame %ld start %lld Size %ld key %d\n",cookie->stream, cookie->frame_pos, start, size, keyframe); // TRACE("Video stream %d: frame %ld start %lld Size %ld key %d\n",cookie->stream, cookie->frame_pos, start, size, keyframe);
mediaHeader->start_time = (cookie->frame_pos * 1000000LL * (int64)cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate;
mediaHeader->type = B_MEDIA_ENCODED_VIDEO; mediaHeader->type = B_MEDIA_ENCODED_VIDEO;
mediaHeader->u.encoded_video.field_flags = keyframe ? B_MEDIA_KEY_FRAME : 0; mediaHeader->u.encoded_video.field_flags = keyframe ? B_MEDIA_KEY_FRAME : 0;
mediaHeader->u.encoded_video.first_active_line = 0; mediaHeader->u.encoded_video.first_active_line = 0;
mediaHeader->u.encoded_video.line_count = cookie->line_count; mediaHeader->u.encoded_video.line_count = cookie->line_count;
mediaHeader->u.encoded_video.field_number = 0; mediaHeader->u.encoded_video.field_number = 0;
mediaHeader->u.encoded_video.field_sequence = cookie->frame_pos; mediaHeader->u.encoded_video.field_sequence = cookie->frame_pos;
cookie->frame_pos++;
} }
TRACE("stream %d: start_time %.6f\n", cookie->stream, mediaHeader->start_time / 1000000.0); mediaHeader->start_time = bigtime_t(cookie->frame_pos * 1000000.0 * cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate;
cookie->frame_pos++;
// TRACE("stream %d: start_time %.6f\n", cookie->stream, mediaHeader->start_time / 1000000.0);
*chunkBuffer = cookie->buffer; *chunkBuffer = cookie->buffer;
*chunkSize = size; *chunkSize = size;