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;
}
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 *_result;
@ -209,6 +220,36 @@ void AtomBase::Read(uint8 *value, uint32 maxread)
// 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)
{
}

View File

@ -101,17 +101,18 @@ public:
uint64 getAtomSize() {return atomSize;};
uint32 getAtomType() {return atomType;};
off_t getAtomOffset() {return atomOffset;};
off_t getStreamOffset() {return streamOffset;};
char *getAtomTypeAsFourcc();
off_t getAtomOffset() { return atomOffset; };
off_t getStreamOffset() { return streamOffset; };
uint64 getDataSize() {return atomSize - 8;};
uint64 getDataSize() { return atomSize - 8;};
uint64 getBytesRemaining();
bool IsType(uint32 patomType) {return patomType == atomType;};
bool IsType(uint32 patomType) { return patomType == atomType; };
void setAtomOffset(off_t patomOffset) {atomOffset = patomOffset;};
void setStreamOffset(off_t pstreamOffset) {streamOffset = pstreamOffset;};
void setAtomOffset(off_t patomOffset) { atomOffset = patomOffset; };
void setStreamOffset(off_t pstreamOffset) { streamOffset = pstreamOffset; };
char *getAtomName();
@ -142,6 +143,9 @@ public:
void Read(uint8 *value);
void Read(char *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 {

View File

@ -62,11 +62,15 @@ MP4FileReader::IsEndOfData(off_t pPosition)
{
AtomBase* aAtomBase;
// check all mdat atoms to make sure pPosition is within one of them
for (uint32 index=0;index<CountChildAtoms('mdat');index++) {
aAtomBase = GetChildAtom(uint32('mdat'),index);
if ((aAtomBase) && (aAtomBase->getAtomSize() > 8)) {
MDATAtom *aMdatAtom = dynamic_cast<MDATAtom *>(aAtomBase);
return pPosition >= aMdatAtom->getEOF();
MDATAtom *aMDATAtom = dynamic_cast<MDATAtom *>(aAtomBase);
if (pPosition >= aMDATAtom->getAtomOffset() && pPosition <= aMDATAtom->getEOF()) {
return false;
}
}
}
@ -187,8 +191,7 @@ MP4FileReader::getMovieTimeScale()
bigtime_t
MP4FileReader::getMovieDuration()
{
return (bigtime_t(getMVHDAtom()->getDuration()) * 1000000L)
/ getMovieTimeScale();
return bigtime_t((getMVHDAtom()->getDuration() * 1000000.0) / getMovieTimeScale());
}
@ -261,24 +264,13 @@ MP4FileReader::getMaxDuration()
uint32
MP4FileReader::getVideoFrameCount(uint32 streamIndex)
MP4FileReader::getFrameCount(uint32 streamIndex)
{
AtomBase *aAtomBase = GetChildAtom(uint32('trak'), streamIndex);
if (aAtomBase && dynamic_cast<TRAKAtom *>(aAtomBase)->IsVideo())
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())
if (aAtomBase) {
return dynamic_cast<TRAKAtom *>(aAtomBase)->FrameCount();
}
return 1;
}
@ -368,8 +360,12 @@ MP4FileReader::getOffsetForFrame(uint32 streamIndex, uint32 pFrameNo)
TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase);
if (pFrameNo < aTrakAtom->FrameCount()) {
// Get Sample for Frame
uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo);
// Get time for Frame
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
uint32 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;
}
}
@ -458,7 +456,7 @@ MP4FileReader::MovMainHeader()
} else {
theMainHeader.width = VideoFormat(videoStream)->width;
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.micro_sec_per_frame = uint32(1000000.0 / VideoFormat(videoStream)->FrameRate);
}
@ -483,18 +481,33 @@ MP4FileReader::AudioFormat(uint32 streamIndex, size_t *size)
if (aAtomBase) {
STSDAtom *aSTSDAtom = dynamic_cast<STSDAtom *>(aAtomBase);
// Fill In a wave_format_ex structure
// Fill in the AudioMetaData structure
AudioDescription aAudioDescription = aSTSDAtom->getAsAudio();
theAudio.compression = aAudioDescription.codecid;
theAudio.codecSubType = aAudioDescription.codecSubType;
theAudio.NoOfChannels = aAudioDescription.theAudioSampleEntry.ChannelCount;
theAudio.SampleSize = aAudioDescription.theAudioSampleEntry.SampleSize;
// Fix for broken mp4's with 0 SampleSize, default to 16 bits
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.PacketSize = uint32((theAudio.SampleSize * theAudio.NoOfChannels * 1024) / 8);
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.theVOL = aAudioDescription.theVOL;
theAudio.VOLSize = aAudioDescription.VOLSize;
theAudio.theDecoderConfig = aAudioDescription.theDecoderConfig;
theAudio.DecoderConfigSize = aAudioDescription.DecoderConfigSize;
return &theAudio;
}
@ -519,25 +532,26 @@ MP4FileReader::VideoFormat(uint32 streamIndex)
VideoDescription aVideoDescription = aSTSDAtom->getAsVideo();
theVideo.compression = aVideoDescription.codecid;
theVideo.codecSubType = aVideoDescription.codecSubType;
theVideo.width = aVideoDescription.theVideoSampleEntry.Width;
theVideo.height = aVideoDescription.theVideoSampleEntry.Height;
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.image_size = aVideoDescription.theVideoSampleEntry.Height * aVideoDescription.theVideoSampleEntry.Width;
theVideo.HorizontalResolution = aVideoDescription.theVideoSampleEntry.HorizontalResolution;
theVideo.VerticalResolution = aVideoDescription.theVideoSampleEntry.VerticalResolution;
theVideo.FrameCount = aVideoDescription.theVideoSampleEntry.FrameCount;
theVideo.theVOL = aVideoDescription.theVOL;
theVideo.VOLSize = aVideoDescription.VOLSize;
theVideo.theDecoderConfig = aVideoDescription.theDecoderConfig;
theVideo.DecoderConfigSize = aVideoDescription.DecoderConfigSize;
aAtomBase = aTrakAtom->GetChildAtom(uint32('stts'),0);
if (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;
}
@ -552,25 +566,25 @@ MP4FileReader::VideoFormat(uint32 streamIndex)
const mp4_stream_header*
MP4FileReader::StreamFormat(uint32 streamIndex)
{
// Fill In a Stream Header
theStreamHeader.length = 0;
if (IsActive(streamIndex) == false) {
return NULL;
}
// Fill In a Stream Header
theStreamHeader.length = 0;
if (IsVideo(streamIndex)) {
theStreamHeader.rate = uint32(1000000L*VideoFormat(streamIndex)->FrameRate);
theStreamHeader.rate = uint32(1000000.0*VideoFormat(streamIndex)->FrameRate);
theStreamHeader.scale = 1000000L;
theStreamHeader.length = getVideoFrameCount(streamIndex);
theStreamHeader.length = getFrameCount(streamIndex);
}
if (IsAudio(streamIndex)) {
theStreamHeader.rate = uint32(AudioFormat(streamIndex)->SampleRate);
theStreamHeader.scale = 1;
theStreamHeader.length = getAudioFrameCount(streamIndex);
theStreamHeader.length = getFrameCount(streamIndex);
theStreamHeader.sample_size = AudioFormat(streamIndex)->SampleSize;
theStreamHeader.suggested_buffer_size = theStreamHeader.rate * theStreamHeader.sample_size;
theStreamHeader.suggested_buffer_size = AudioFormat(streamIndex)->BufferSize;
}
return &theStreamHeader;
@ -601,8 +615,7 @@ MP4FileReader::IsKeyFrame(uint32 streamIndex, uint32 pFrameNo)
if (aAtomBase) {
TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase);
uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo);
return aTrakAtom->IsSyncSample(SampleNo);
return aTrakAtom->IsSyncSample(pFrameNo);
}
return false;
@ -620,6 +633,8 @@ MP4FileReader::GetNextChunkInfo(uint32 streamIndex, uint32 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.
return *start > 0 && *size > 0 && !IsEndOfFile(*start + *size)
&& !IsEndOfData(*start + *size);
@ -647,11 +662,18 @@ MP4FileReader::IsSupported(BPositionIO *source)
if (aAtom) {
if (dynamic_cast<FTYPAtom *>(aAtom)) {
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
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;
}

View File

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

View File

@ -189,6 +189,14 @@ AtomBase *getAtom(BPositionIO *pStream)
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);
}
@ -477,6 +485,8 @@ TimeToSample *aTimeToSample;
ReadArrayHeader(&theHeader);
// printf("STTS:: Entries %ld\n",theHeader.NoEntries);
for (uint32 i=0;i<theHeader.NoEntries;i++) {
aTimeToSample = new TimeToSample;
@ -486,7 +496,11 @@ TimeToSample *aTimeToSample;
theTimeToSampleArray[i] = aTimeToSample;
SUMDurations += (theTimeToSampleArray[i]->Duration * theTimeToSampleArray[i]->Count);
SUMCounts += theTimeToSampleArray[i]->Count;
// printf("(%ld,%ld)",aTimeToSample->Count,aTimeToSample->Duration);
}
// printf("\n");
}
char *STTSAtom::OnGetAtomName()
@ -494,24 +508,30 @@ char *STTSAtom::OnGetAtomName()
return "Time to Sample Atom";
}
uint32 STTSAtom::getSampleForTime(uint32 pTime)
uint32 STTSAtom::getSampleForTime(bigtime_t pTime)
{
// TODO this is too slow. PreCalc when loading this?
uint64 Duration = 0;
// 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?
/* bigtime_t TotalDuration = 0;
uint64 TotalCount = 0;
for (uint32 i=0;i<theHeader.NoEntries;i++) {
Duration += (theTimeToSampleArray[i]->Duration * theTimeToSampleArray[i]->Count);
if (Duration > pTime) {
return i;
TotalDuration += (theTimeToSampleArray[i]->Duration * theTimeToSampleArray[i]->Count);
TotalCount += theTimeToSampleArray[i]->Count;
if ((TotalDuration * 44100) > pTime) {
return uint32((pTime * FrameRate + 50) / 1000000.0);
}
}
return 0;
return 0; */
}
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;
}
@ -573,6 +593,8 @@ SampleToChunk *aSampleToChunk;
ReadArrayHeader(&theHeader);
uint32 TotalPrevSamples = 0;
// printf("STSC:: Entries %ld\n",theHeader.NoEntries);
for (uint32 i=0;i<theHeader.NoEntries;i++) {
aSampleToChunk = new SampleToChunk;
@ -588,8 +610,11 @@ SampleToChunk *aSampleToChunk;
aSampleToChunk->TotalPrevSamples = 0;
}
// printf("(%ld,%ld)",aSampleToChunk->SamplesPerChunk, aSampleToChunk->TotalPrevSamples);
theSampleToChunkArray[i] = aSampleToChunk;
}
// printf("\n");
}
char *STSCAtom::OnGetAtomName()
@ -665,7 +690,7 @@ SyncSample *aSyncSample;
FullAtom::OnProcessMetaData();
ReadArrayHeader(&theHeader);
for (uint32 i=0;i<theHeader.NoEntries;i++) {
aSyncSample = new SyncSample;
@ -688,7 +713,7 @@ bool STSSAtom::IsSyncSample(uint32 pSampleNo)
return true;
}
if (pSampleNo > theSyncSampleArray[i]->SyncSampleNo) {
if (pSampleNo < theSyncSampleArray[i]->SyncSampleNo) {
return false;
}
}
@ -908,28 +933,42 @@ uint64 STCOAtom::getOffsetForChunk(uint32 pChunkID)
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) {
free(theVOL);
if (theDecoderConfig) {
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;
byte = theVOL[(*offset)++];
byte = ESDS[(*offset)++];
if (byte == Tag) {
uint8 numBytes = 0;
unsigned int length = 0;
do {
byte = theVOL[(*offset)++];
byte = ESDS[(*offset)++];
numBytes++;
length = (length << 7) | (byte & 0x7F);
} while ((byte & 0x80) && numBytes < 4);
@ -937,99 +976,51 @@ bool ESDSAtom::SkipTag(uint8 Tag, uint32 *offset) {
} else {
// go back Tag not found
(*offset)--;
printf("No Tag %d\n",Tag);
return false;
}
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()
{
FullAtom::OnProcessMetaData();
VOLSize = getBytesRemaining();
uint32 offset = 0;
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];
// Read 4 bytes because this is really a FullAtom
uint32 version;
Read(&version);
DecoderConfigAtom::OnProcessMetaData();
}
char *ESDSAtom::OnGetAtomName()
@ -1037,23 +1028,191 @@ char *ESDSAtom::OnGetAtomName()
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)
{
theHeader.NoEntries = 0;
theAudioDescription.theVOL = NULL;
theVideoDescription.theVOL = NULL;
theAudioDescription.codecid = 0;
theAudioDescription.codecSubType = 0;
theAudioDescription.FrameSize = 0;
theAudioDescription.BufferSize = 0;
theAudioDescription.BitRate = 0;
theAudioDescription.theDecoderConfig = NULL;
theVideoDescription.theDecoderConfig = NULL;
}
STSDAtom::~STSDAtom()
{
if (theAudioDescription.theVOL) {
free(theAudioDescription.theVOL);
theAudioDescription.theVOL = NULL;
if (theAudioDescription.theDecoderConfig) {
free(theAudioDescription.theDecoderConfig);
theAudioDescription.theDecoderConfig = NULL;
}
if (theVideoDescription.theVOL) {
free(theVideoDescription.theVOL);
theVideoDescription.theVOL = NULL;
if (theVideoDescription.theDecoderConfig) {
free(theVideoDescription.theDecoderConfig);
theVideoDescription.theDecoderConfig = NULL;
}
}
@ -1062,12 +1221,14 @@ uint32 STSDAtom::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
// MPEG-4 video/audio use the esds to store the VOL (STUPID COMMITTEE IDEA)
// Check for a Decoder Config and if it exists copy it back to the caller
// 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) {
// Well something is there so read it as an atom
AtomBase *aAtomBase = getAtom(theStream);
@ -1075,11 +1236,11 @@ void STSDAtom::ReadESDS(uint8 **VOL, size_t *VOLSize, bool forAudio)
aAtomBase->ProcessMetaData();
printf("%s [%Ld]\n",aAtomBase->getAtomName(),aAtomBase->getAtomSize());
if (dynamic_cast<ESDSAtom *>(aAtomBase)) {
// ESDS atom good
*VOLSize = dynamic_cast<ESDSAtom *>(aAtomBase)->getVOLSize(forAudio);
*VOL = (uint8 *)(malloc(*VOLSize));
memcpy(*VOL,dynamic_cast<ESDSAtom *>(aAtomBase)->getVOL(forAudio),*VOLSize);
if (dynamic_cast<DecoderConfigAtom *>(aAtomBase)) {
// DecoderConfig atom good
DecoderConfigAtom *aDecoderConfigAtom = dynamic_cast<DecoderConfigAtom *>(aAtomBase);
aDecoderConfigAtom->OverrideAudioDescription(pAudioDescription);
aDecoderConfigAtom->OverrideVideoDescription(pVideoDescription);
delete aAtomBase;
} else {
@ -1099,7 +1260,7 @@ void STSDAtom::ReadSoundDescription()
Read(&theAudioDescription.theAudioSampleEntry.reserved);
Read(&theAudioDescription.theAudioSampleEntry.SampleRate);
ReadESDS(&theAudioDescription.theVOL,&theAudioDescription.VOLSize,true);
ReadDecoderConfig(&theAudioDescription.theDecoderConfig, &theAudioDescription.DecoderConfigSize, &theAudioDescription, NULL);
}
void STSDAtom::ReadVideoDescription()
@ -1129,7 +1290,7 @@ void STSDAtom::ReadVideoDescription()
Read(&theVideoDescription.theVideoSampleEntry.Depth);
Read(&theVideoDescription.theVideoSampleEntry.pre_defined3);
ReadESDS(&theVideoDescription.theVOL,&theVideoDescription.VOLSize, false);
ReadDecoderConfig(&theVideoDescription.theDecoderConfig, &theVideoDescription.DecoderConfigSize, NULL, &theVideoDescription);
}
void STSDAtom::OnProcessMetaData()
@ -1331,13 +1492,13 @@ void MDATAtom::OnProcessMetaData()
char *MDATAtom::OnGetAtomName()
{
printf("Offset %lld, Size %lld ",getStreamOffset(),getAtomSize());
printf("Offset %lld, Size %lld ",getAtomOffset(),getAtomSize() - 8);
return "Media Data Atom";
}
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)
@ -1549,7 +1710,7 @@ char *MDHDAtom::OnGetAtomName()
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()

View File

@ -171,15 +171,18 @@ public:
void OnProcessMetaData();
char *OnGetAtomName();
uint64 getSUMCounts() {return SUMCounts;};
uint64 getSUMDurations() {return SUMDurations;};
uint32 getSampleForTime(uint32 pTime);
uint64 getSUMCounts() { return SUMCounts; };
uint64 getSUMDurations() { return SUMDurations; };
uint32 getSampleForTime(bigtime_t pTime);
uint32 getSampleForFrame(uint32 pFrame);
void setFrameRate(float pFrameRate) { FrameRate = pFrameRate; };
private:
array_header theHeader;
TimeToSampleArray theTimeToSampleArray;
uint64 SUMDurations;
bigtime_t SUMDurations;
uint64 SUMCounts;
float FrameRate;
};
// Atom class for reading the composition time to sample atom
@ -300,22 +303,72 @@ public:
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:
ESDSAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize);
virtual ~ESDSAtom();
void OnProcessMetaData();
char *OnGetAtomName();
uint8 *getVOL(bool forAudio);
size_t getVOLSize(bool forAudio);
void OnOverrideAudioDescription(AudioDescription *pAudioDescription);
void OnOverrideVideoDescription(VideoDescription *pVideoDescription);
private:
bool SkipTag(uint8 Tag, uint32 *offset);
uint8 *theVOL;
size_t VOLSize;
uint8 ESDSType;
uint8 StreamType;
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 {
public:
STSDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize);
@ -329,10 +382,10 @@ public:
private:
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 ReadVideoDescription();
void ReadHintDescription();
uint32 codecid;
@ -416,14 +469,15 @@ public:
void OnChildProcessingComplete();
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 IsAudio(); // Is this a audio track
// GetAudioMetaData() // If this is a audio track get the audio meta data
// GetVideoMetaData() // If this is a video track get the video meta data
uint32 getTimeForFrame(uint32 pFrame, uint32 pTimeScale);
uint32 getSampleForTime(uint32 pTime);
bigtime_t getTimeForFrame(uint32 pFrame);
uint32 getSampleForTime(bigtime_t pTime);
uint32 getSampleForFrame(uint32 pFrame);
uint32 getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk);
uint64 getOffsetForChunk(uint32 pChunkID);
@ -432,6 +486,9 @@ public:
uint32 getNoSamplesInChunk(uint32 pChunkID);
uint32 getTotalChunks();
float getSampleRate();
float getFrameRate();
bool IsSyncSample(uint32 pSampleNo);
bool IsSingleSampleSize();
bool IsActive();
@ -446,6 +503,7 @@ private:
uint32 framecount;
uint32 bytespersample;
uint32 timescale;
};
// Atom class for reading the media container atom

View File

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

View File

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

View File

@ -38,7 +38,7 @@
#include <string.h>
//#define TRACE_MP4_READER
#define TRACE_MP4_READER
#ifdef TRACE_MP4_READER
# define TRACE printf
#else
@ -60,8 +60,6 @@ struct mp4_cookie {
bool audio;
// audio only:
off_t byte_pos;
uint32 chunk_pos;
uint32 bytes_per_sec_rate;
uint32 bytes_per_sec_scale;
@ -145,6 +143,9 @@ mp4Reader::GetFileFormatInfo(media_file_format *mff)
status_t
mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie)
{
size_t size;
const void *data;
mp4_cookie *cookie = new mp4_cookie;
*_cookie = cookie;
@ -181,12 +182,10 @@ mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie)
return B_ERROR;
}
cookie->frame_count = theFileReader->getAudioFrameCount(cookie->stream);
cookie->frame_count = theFileReader->getFrameCount(cookie->stream);
cookie->duration = theFileReader->getAudioDuration(cookie->stream);
cookie->audio = true;
cookie->byte_pos = 0;
cookie->chunk_pos = 1;
if (stream_header->scale && 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;
TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using both)\n",
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) {
cookie->bytes_per_sec_rate = stream_header->rate * audio_format->SampleSize
* audio_format->NoOfChannels / 8;
@ -204,14 +205,8 @@ mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie)
cookie->frames_per_sec_scale = 1;
TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using rate)\n",
cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
} else if (audio_format->PacketSize) {
cookie->bytes_per_sec_rate = audio_format->PacketSize;
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);
TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using rate)\n",
cookie->frames_per_sec_rate, cookie->frames_per_sec_scale);
} else {
cookie->bytes_per_sec_rate = 128000;
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;
break;
case 'mp4a':
TRACE("AAC Audio (mp4a)\n");
format->u.encoded_audio.bit_rate = 8 * 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;
case 'alac':
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.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;
default:
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)
format->user_data_type = B_CODEC_TYPE_INFO;
*(uint32 *)format->user_data = audio_format->compression; format->user_data[4] = 0;
// Set the VOL
size_t size = audio_format->VOLSize;
const void *data = audio_format->theVOL;
format->SetMetaData(data, size);
// Set the DecoderConfigSize
size = audio_format->DecoderConfigSize;
data = audio_format->theDecoderConfig;
if (size > 0) {
if (format->SetMetaData(data, size) != B_OK) {
printf("Failed to set Decoder Config\n");
}
}
#ifdef TRACE_MP4_READER
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("field_rate %.3f\n", format->u.encoded_video.output.field_rate);
// Set the VOL
if (video_format->VOLSize > 0) {
TRACE("VOL Found Size is %ld\n",video_format->VOLSize);
size_t size = video_format->VOLSize;
const void *data = video_format->theVOL;
// Set the Decoder Config
size = video_format->DecoderConfigSize;
data = video_format->theDecoderConfig;
if (size > 0) {
TRACE("Decoder Config Found Size is %ld\n",size);
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
@ -514,12 +500,12 @@ mp4Reader::GetStreamInfo(void *_cookie, int64 *frameCount, bigtime_t *duration,
// Copy metadata to infoBuffer
if (theFileReader->IsVideo(cookie->stream)) {
const VideoMetaData *video_format = theFileReader->VideoFormat(cookie->stream);
*infoBuffer = video_format->theVOL;
*infoSize = video_format->VOLSize;
*infoBuffer = video_format->theDecoderConfig;
*infoSize = video_format->DecoderConfigSize;
} else {
const AudioMetaData *audio_format = theFileReader->AudioFormat(cookie->stream);
*infoBuffer = audio_format->theVOL;
*infoSize = audio_format->VOLSize;
*infoBuffer = audio_format->theDecoderConfig;
*infoSize = audio_format->DecoderConfigSize;
}
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 * mp4cookie->frames_per_sec_rate) / (int64)mp4cookie->frames_per_sec_scale) / 1000000LL;
TRACE("Time %Ld to Frame %Ld\n",*time, *frame);
// movcookie->frame_pos = *frame;
// return B_ERROR;
mp4cookie->frame_pos = *frame;
return B_OK;
}
if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
// time = frame * 1000000LL * fps / rate
TRACE("Frame %Ld to Time %Ld\n", *frame, *time);
*time = (*frame * 1000000LL * (int64)mp4cookie->frames_per_sec_scale) / mp4cookie->frames_per_sec_rate;
// movcookie->frame_pos = *frame;
// return B_ERROR;
mp4cookie->frame_pos = *frame;
return B_OK;
}
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;
bool keyframe;
if (cookie->audio) {
// use chunk position
if (!theFileReader->GetNextChunkInfo(cookie->stream, cookie->chunk_pos,
&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 (theFileReader->GetNextChunkInfo(cookie->stream, cookie->frame_pos, &start, &size, &keyframe) == false) {
TRACE("LAST BUFFER ERROR\n");
return B_LAST_BUFFER_ERROR;
}
if (cookie->buffer_size < size) {
@ -589,36 +568,23 @@ mp4Reader::GetNextChunk(void *_cookie, const void **chunkBuffer,
}
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->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 {
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;
// TRACE("Video stream %d: frame %ld start %lld Size %ld key %d\n",cookie->stream, cookie->frame_pos, start, size, keyframe);
mediaHeader->type = B_MEDIA_ENCODED_VIDEO;
mediaHeader->u.encoded_video.field_flags = keyframe ? B_MEDIA_KEY_FRAME : 0;
mediaHeader->u.encoded_video.first_active_line = 0;
mediaHeader->u.encoded_video.line_count = cookie->line_count;
mediaHeader->u.encoded_video.field_number = 0;
mediaHeader->u.encoded_video.field_sequence = cookie->frame_pos;
cookie->frame_pos++;
}
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);
// TRACE("stream %d: start_time %.6f\n", cookie->stream, mediaHeader->start_time / 1000000.0);
*chunkBuffer = cookie->buffer;
*chunkSize = size;