diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.cpp b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.cpp index 2d260b0096..a68a9dc9ed 100644 --- a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.cpp +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.cpp @@ -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) { } diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.h b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.h index 607ec4d1bd..2858d7a8f4 100644 --- a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.h +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.h @@ -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 { diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.cpp b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.cpp index 7d9723414b..1b73d69ea9 100644 --- a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.cpp +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.cpp @@ -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;indexgetAtomSize() > 8)) { - MDATAtom *aMdatAtom = dynamic_cast(aAtomBase); - return pPosition >= aMdatAtom->getEOF(); + MDATAtom *aMDATAtom = dynamic_cast(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(aAtomBase)->IsVideo()) - return dynamic_cast(aAtomBase)->FrameCount(); - - return 1; -} - - -uint32 -MP4FileReader::getAudioFrameCount(uint32 streamIndex) -{ - AtomBase *aAtomBase = GetChildAtom(uint32('trak'), streamIndex); - - if (aAtomBase && dynamic_cast(aAtomBase)->IsAudio()) + if (aAtomBase) { return dynamic_cast(aAtomBase)->FrameCount(); + } return 1; } @@ -368,8 +360,12 @@ MP4FileReader::getOffsetForFrame(uint32 streamIndex, uint32 pFrameNo) TRAKAtom *aTrakAtom = dynamic_cast(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(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(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(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(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(aAtom)->HasBrand(uint32('qt '))); + if (!dynamic_cast(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; } diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.h b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.h index d51175c1c2..a79823c51b 100644 --- a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.h +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.h @@ -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 diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.cpp b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.cpp index 593bc9b178..99d568de16 100644 --- a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.cpp +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.cpp @@ -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;iDuration * 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;iDuration * 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;iTotalPrevSamples = 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 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(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(aAtomBase)) { - // ESDS atom good - *VOLSize = dynamic_cast(aAtomBase)->getVOLSize(forAudio); - *VOL = (uint8 *)(malloc(*VOLSize)); - memcpy(*VOL,dynamic_cast(aAtomBase)->getVOL(forAudio),*VOLSize); + if (dynamic_cast(aAtomBase)) { + // DecoderConfig atom good + DecoderConfigAtom *aDecoderConfigAtom = dynamic_cast(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() diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.h b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.h index 5c3b3bb2c5..70b6c67bf6 100644 --- a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.h +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.h @@ -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 diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Structs.h b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Structs.h index 18e42d7ce2..b863f0f614 100644 --- a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Structs.h +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Structs.h @@ -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 diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4TrakAtom.cpp b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4TrakAtom.cpp index 4172fec72b..4e9a3ba9b6 100644 --- a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4TrakAtom.cpp +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4TrakAtom.cpp @@ -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(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(aAtomBase); + if (IsAudio()) { + AtomBase *aAtomBase = GetChildAtom(uint32('stsd'),0); + if (aAtomBase) { + STSDAtom *aSTSDAtom = dynamic_cast(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(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(aAtomBase))->getSampleForFrame(pFrame); + return (dynamic_cast(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 diff --git a/src/add-ons/media/plugins/mp4_reader/mp4_reader.cpp b/src/add-ons/media/plugins/mp4_reader/mp4_reader.cpp index e845d41dd5..5ebb46e74b 100644 --- a/src/add-ons/media/plugins/mp4_reader/mp4_reader.cpp +++ b/src/add-ons/media/plugins/mp4_reader/mp4_reader.cpp @@ -38,7 +38,7 @@ #include -//#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;