diff --git a/src/add-ons/media/plugins/Jamfile b/src/add-ons/media/plugins/Jamfile index 9162fb52f2..81ba03db72 100644 --- a/src/add-ons/media/plugins/Jamfile +++ b/src/add-ons/media/plugins/Jamfile @@ -17,6 +17,7 @@ SubInclude OBOS_TOP src add-ons media plugins avcodec ; SubInclude OBOS_TOP src add-ons media plugins vorbis ; SubInclude OBOS_TOP src add-ons media plugins speex ; SubInclude OBOS_TOP src add-ons media plugins mov_reader ; +SubInclude OBOS_TOP src add-ons media plugins mp4_reader ; # The following add-ons are GPL licensed, and can only be used with # software whose license is GPL compatible. To include these GPL diff --git a/src/add-ons/media/plugins/avcodec/codectbl.cpp b/src/add-ons/media/plugins/avcodec/codectbl.cpp index afb0c3f57e..56e4bf51dd 100644 --- a/src/add-ons/media/plugins/avcodec/codectbl.cpp +++ b/src/add-ons/media/plugins/avcodec/codectbl.cpp @@ -48,6 +48,9 @@ const struct codec_table gCodecTable[] = { {CODEC_ID_ADPCM_MS, B_MEDIA_ENCODED_AUDIO, B_QUICKTIME_FORMAT_FAMILY, 0x6D730002, "MS ADPCM"}, {CODEC_ID_MP2, B_MEDIA_ENCODED_AUDIO, B_QUICKTIME_FORMAT_FAMILY, 0x6D730050, "MP Layer2"}, {CODEC_ID_MP2, B_MEDIA_ENCODED_AUDIO, B_QUICKTIME_FORMAT_FAMILY, 0x6D730055, "MP Layer3"}, + {CODEC_ID_ADPCM_IMA_QT, B_MEDIA_ENCODED_AUDIO, B_QUICKTIME_FORMAT_FAMILY, 'ima4', "Quicktime IMA4"}, + {CODEC_ID_MPEG4AAC, B_MEDIA_ENCODED_AUDIO, B_QUICKTIME_FORMAT_FAMILY, 'mp4a', "MPEG4 AAC"}, + {CODEC_ID_MPEG4, B_MEDIA_ENCODED_AUDIO, B_QUICKTIME_FORMAT_FAMILY, 'mp4v', "MPEG4 Video"}, #ifdef HAS_MACE_AUDIO {CODEC_ID_MACE3, B_MEDIA_ENCODED_AUDIO, B_QUICKTIME_FORMAT_FAMILY, 'MAC3', "MACE 3:1"}, @@ -63,8 +66,9 @@ const struct codec_table gCodecTable[] = { {CODEC_ID_WMAV2, B_MEDIA_ENCODED_AUDIO, B_WAV_FORMAT_FAMILY, 0x161, "MS WMA v2"}, {CODEC_ID_CINEPAK, B_MEDIA_ENCODED_VIDEO, B_AVI_FORMAT_FAMILY, FOURCC('cvid'), "Cinepak Video"}, - {CODEC_ID_CINEPAK, B_MEDIA_ENCODED_VIDEO, B_QUICKTIME_FORMAT_FAMILY, FOURCC('divc'), "Cinepak Video"}, - {CODEC_ID_CINEPAK, B_MEDIA_ENCODED_VIDEO, B_QUICKTIME_FORMAT_FAMILY, FOURCC('cvid'), "Cinepak Video"}, + {CODEC_ID_CINEPAK, B_MEDIA_ENCODED_VIDEO, B_QUICKTIME_FORMAT_FAMILY, 'cvid', "Cinepak Video"}, + + {CODEC_ID_MSRLE, B_MEDIA_ENCODED_VIDEO, B_QUICKTIME_FORMAT_FAMILY, FOURCC(' elr'), "MS RLE"}, // ??? {CODEC_ID_MSRLE, B_MEDIA_ENCODED_VIDEO, B_AVI_FORMAT_FAMILY, FOURCC('RLE '), "MS RLE"}, // ??? {CODEC_ID_MSRLE, B_MEDIA_ENCODED_VIDEO, B_AVI_FORMAT_FAMILY, FOURCC('mrle'), "MS RLE"}, // ??? diff --git a/src/add-ons/media/plugins/mov_reader/TODO b/src/add-ons/media/plugins/mov_reader/TODO new file mode 100644 index 0000000000..c40123dfb3 --- /dev/null +++ b/src/add-ons/media/plugins/mov_reader/TODO @@ -0,0 +1,8 @@ +Quicktime Reader TODO +1. Correct known issues +2. Performance improvements by precalculating some meta data + +KNOWN ISSUES +Edits lists are ignored the track is played start to finish regardless of edit lists +MP3 sound tracks create a noise at irregular intervals (too little data supplied to decoder?) +IMA4 sound tracks repeat (no idea) \ No newline at end of file diff --git a/src/add-ons/media/plugins/mov_reader/libMOV/MOVParser.cpp b/src/add-ons/media/plugins/mov_reader/libMOV/MOVParser.cpp index 924364ccad..72fc8e877c 100644 --- a/src/add-ons/media/plugins/mov_reader/libMOV/MOVParser.cpp +++ b/src/add-ons/media/plugins/mov_reader/libMOV/MOVParser.cpp @@ -158,10 +158,6 @@ AtomBase *getAtom(BPositionIO *pStream) return new STSZAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); } - if (aAtomType == uint32('ftyp')) { - return new FTYPAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); - } - if (aAtomType == uint32('cmov')) { return new CMOVAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); } @@ -979,23 +975,6 @@ char *WIDEAtom::OnGetAtomName() return "WIDE Atom"; } -FTYPAtom::FTYPAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) -{ -} - -FTYPAtom::~FTYPAtom() -{ -} - -void FTYPAtom::OnProcessMetaData() -{ -} - -char *FTYPAtom::OnGetAtomName() -{ - return "Graphics export file type Atom"; -} - FREEAtom::FREEAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) { } diff --git a/src/add-ons/media/plugins/mov_reader/libMOV/MOVParser.h b/src/add-ons/media/plugins/mov_reader/libMOV/MOVParser.h index e0101ca063..56a6f24f30 100644 --- a/src/add-ons/media/plugins/mov_reader/libMOV/MOVParser.h +++ b/src/add-ons/media/plugins/mov_reader/libMOV/MOVParser.h @@ -120,16 +120,6 @@ private: uint8 *Buffer; }; -// Atom class for reading the ftyp atom -class FTYPAtom : public AtomBase { -public: - FTYPAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); - virtual ~FTYPAtom(); - void OnProcessMetaData(); - char *OnGetAtomName(); - -}; - // Atom class for reading the wide atom class WIDEAtom : public AtomBase { public: diff --git a/src/add-ons/media/plugins/mp4_reader/Jamfile b/src/add-ons/media/plugins/mp4_reader/Jamfile new file mode 100644 index 0000000000..c389b56ee0 --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/Jamfile @@ -0,0 +1,16 @@ +SubDir OBOS_TOP src add-ons media plugins mp4_reader ; + +UsePrivateHeaders media ; + +Addon mp4_reader : media plugins : + mp4_reader.cpp + : + false + : + libmp4reader.a + libz.a +; + +LinkSharedOSLibs mp4_reader : be libmedia.so ; + +SubInclude OBOS_TOP src add-ons media plugins mp4_reader libMP4 ; diff --git a/src/add-ons/media/plugins/mp4_reader/TODO b/src/add-ons/media/plugins/mp4_reader/TODO new file mode 100644 index 0000000000..8a7a252d52 --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/TODO @@ -0,0 +1,7 @@ +MP4 Reader TODO +1. Correct known issues +2. Performance improvements by precalculating some meta data + +KNOWN ISSUES +Edits lists are ignored the track is played start to finish regardless of edit lists +Get some output :-) \ No newline at end of file diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/Jamfile b/src/add-ons/media/plugins/mp4_reader/libMP4/Jamfile new file mode 100644 index 0000000000..01a717c085 --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/Jamfile @@ -0,0 +1,8 @@ +SubDir OBOS_TOP src add-ons media plugins mp4_reader libMP4 ; + +StaticLibrary mp4reader : + MP4Parser.cpp + MP4FileReader.cpp + MP4Atom.cpp + MP4TrakAtom.cpp +; diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.cpp b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.cpp new file mode 100644 index 0000000000..8ab9522ded --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2005, David McPaul + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#include "MP4Atom.h" + +AtomBase::AtomBase(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) +{ + theStream = pStream; + streamOffset = pstreamOffset; + atomType = patomType; + atomSize = patomSize; + parentAtom = NULL; +} + +AtomBase::~AtomBase() +{ + theStream = NULL; + parentAtom = NULL; +} + +char *AtomBase::getAtomName() +{ + char *_result; + _result = OnGetAtomName(); + if (_result) { + return _result; + } + + 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::OnGetAtomName() +{ + return NULL; +} + +void AtomBase::ProcessMetaData() +// ProcessMetaData() - Reads in the basic Atom Meta Data +// - Calls OnProcessMetaData() +// - Calls ProcessMetaData on each child atom +// (ensures stream is correct for child via offset) +{ + setAtomOffset(getStream()->Position()); + + OnProcessMetaData(); + + MoveToEnd(); +} + +void AtomBase::OnProcessMetaData() +{ + MoveToEnd(); +} + +bool AtomBase::MoveToEnd() +{ + off_t NewPosition = streamOffset + atomSize; + + if (getStream()->Position() != NewPosition) { + return (getStream()->Seek(NewPosition,0) > 0); + } + return true; +} + +uint64 AtomBase::getBytesRemaining() +{ + off_t EndPosition = streamOffset + atomSize; + + return (EndPosition - getStream()->Position()); +} + +void AtomBase::DisplayAtoms() +{ + uint32 aindent = 0; + DisplayAtoms(aindent); +} + +void AtomBase::DisplayAtoms(uint32 pindent) +{ + Indent(pindent); + printf("%s\n",getAtomName()); +} + +void AtomBase::Indent(uint32 pindent) +{ + for (uint32 i=0;iRead(pHeader,sizeof(array_header)); + + pHeader->NoEntries = B_BENDIAN_TO_HOST_INT32(pHeader->NoEntries); +} + +BPositionIO *AtomBase::OnGetStream() +{ + // default implementation + return theStream; +} + +BPositionIO *AtomBase::getStream() +{ + return OnGetStream(); +} + +FullAtom::FullAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) +{ +} + +FullAtom::~FullAtom() +{ +} + +void FullAtom::OnProcessMetaData() +{ + getStream()->Read(&Version,sizeof(uint8)); + getStream()->Read(&Flags1,sizeof(uint8)); + getStream()->Read(&Flags2,sizeof(uint8)); + getStream()->Read(&Flags3,sizeof(uint8)); +} + +AtomContainer::AtomContainer(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) +{ + TotalChildren = 0; +} + +AtomContainer::~AtomContainer() +{ +} + +void AtomContainer::DisplayAtoms(uint32 pindent) +{ + Indent(pindent); + printf("%ld:%s\n",TotalChildren,getAtomName()); + pindent++; + // for each child + for (uint32 i = 0;i < TotalChildren;i++) { + atomChildren[i]->DisplayAtoms(pindent); + } + +} + +void AtomContainer::ProcessMetaData() +{ + setAtomOffset(getStream()->Position()); + + OnProcessMetaData(); + + AtomBase *aChild; + while (IsEndOfAtom() == false) { + aChild = getAtom(getStream()); + if (AddChild(aChild)) { + aChild->ProcessMetaData(); + } + } + + OnChildProcessingComplete(); + + MoveToEnd(); +} + +bool AtomContainer::AddChild(AtomBase *pChildAtom) +{ + if (pChildAtom) { + pChildAtom->setParent(this); + atomChildren[TotalChildren++] = pChildAtom; + return true; + } + return false; +} + +AtomBase *AtomContainer::GetChildAtom(uint32 patomType, uint32 offset) +{ + for (uint32 i=0;iIsType(patomType)) { + // found match, skip if offset non zero. + if (offset == 0) { + return atomChildren[i]; + } else { + offset--; + } + } else { + if (atomChildren[i]->IsContainer()) { + // search container + AtomBase *aAtomBase = (dynamic_cast(atomChildren[i])->GetChildAtom(patomType, offset)); + if (aAtomBase) { + // found in container + return aAtomBase; + } + // not found + } + } + } + return NULL; +} + +void AtomContainer::OnProcessMetaData() +{ +} diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.h b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.h new file mode 100644 index 0000000000..31d457682a --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Atom.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2005, David McPaul + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _MP4_ATOM_H +#define _MP4_ATOM_H + +#include +#include +#include +#include + +// Std Headers +#include + +#include "MP4Structs.h" + +/* +AtomBase + theStream : Stream; + streamOffset : FilePtr; + atomOffset : FilePtr; + atomSize : int64; + atomType : int32; // Can be a number or a 4 char FOURCC + atomChildren : Array of AtomBase; + +public + ProcessMetaData() - Reads in the basic Atom Meta Data + - Calls OnProcessMetaData() + - Calls ProcessMetaData on each child atom +(ensures stream is correct for child via offset) + OnProcessMetaData() - Subclass override to read/set meta data + + AddChild(AtomBase) - Adds a child atom to the children array + + MoveToEnd() - Moves stream ptr to end of atom + + GetTypeAsString() - returns the type as something the user can read*/ + +class AtomBase; + +typedef AtomBase* AtomBasePtr; +typedef std::map > AtomArray; + +class AtomBase { + +/* + + This is the basic or standard atom. It contains data describing some aspect of the file/stream + +*/ + +private: + off_t streamOffset; + off_t atomOffset; + uint32 atomType; + uint64 atomSize; + char fourcc[5]; // make this an alias to atomType + AtomBase *parentAtom; + +protected: + BPositionIO *theStream; + void Indent(uint32 pindent); + +public: + AtomBase(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~AtomBase(); + + virtual bool IsContainer() {return false;}; + + virtual BPositionIO *OnGetStream(); + BPositionIO *getStream(); + + bool IsExtended() {return false;}; + bool IsEndOfAtom() {return (getStream()->Position() >= off_t(streamOffset + atomSize));}; + + // Is this a known atom type + bool IsKnown(); + + virtual void DisplayAtoms(uint32 pindent); + + uint64 getAtomSize() {return atomSize;}; + uint32 getAtomType() {return atomType;}; + + uint64 getBytesRemaining(); + + bool IsType(uint32 patomType) {return patomType == atomType;}; + + void setAtomOffset(off_t patomOffset) {atomOffset = patomOffset;}; + void setStreamOffset(off_t pstreamOffset) {streamOffset = pstreamOffset;}; + + char *getAtomName(); + + virtual char *OnGetAtomName(); + + // ProcessMetaData() - Reads in the basic Atom Meta Data + // - Calls OnProcessMetaData() + virtual void ProcessMetaData(); + + // OnProcessMetaData() - Subclass override to read/set meta data + virtual void OnProcessMetaData(); + + // Move stream ptr to where atom ends in stream (return false on failure) + bool MoveToEnd(); + + void DisplayAtoms(); + + // Many atoms use an array header + void ReadArrayHeader(array_header *pHeader); + + void setParent(AtomBase *pParent) {parentAtom = pParent;}; + AtomBase *getParent() { return parentAtom;}; +}; + +class FullAtom : public AtomBase { +public: + FullAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~FullAtom(); + + virtual void OnProcessMetaData(); + uint8 getVersion() {return Version;}; + uint8 getFlags1() {return Flags1;}; + uint8 getFlags2() {return Flags2;}; + uint8 getFlags3() {return Flags3;}; + +private: + uint8 Version; + uint8 Flags1; + uint8 Flags2; + uint8 Flags3; +}; + +class AtomContainer : public AtomBase { + +/* + + This is an Atom that contains other atoms. It has children that may be Container Atoms or Standard Atoms + +*/ + +private: + AtomArray atomChildren; + uint32 TotalChildren; + + virtual void DisplayAtoms(uint32 pindent); + +public: + AtomContainer(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~AtomContainer(); + + virtual bool IsContainer() {return true;}; + AtomBase *GetChildAtom(uint32 patomType, uint32 offset=0); + uint32 CountChildAtoms(uint32 patomType); + + // ProcessMetaData() - Reads in the basic Atom Meta Data + // - Calls OnProcessMetaData() + // - Calls ProcessMetaData on each child atom + // (ensures stream is correct for child via offset) + virtual void ProcessMetaData(); + + // Add a atom to the children array (return false on failure) + bool AddChild(AtomBase *pChildAtom); + + // OnProcessMetaData() - Subclass override to read/set meta data + virtual void OnProcessMetaData(); + virtual void OnChildProcessingComplete() {}; +}; + +extern AtomBase *getAtom(BPositionIO *pStream); + +#endif // _MP4_ATOM_H diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.cpp b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.cpp new file mode 100644 index 0000000000..8596dbe273 --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.cpp @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2005, David McPaul + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#include +#include + +#include "MP4Parser.h" +#include "MP4FileReader.h" + +extern AtomBase *getAtom(BPositionIO *pStream); + +MP4FileReader::MP4FileReader(BPositionIO *pStream) +{ + theStream = pStream; + + // Find Size of Stream, need to rethink this for non seekable streams + theStream->Seek(0,SEEK_END); + StreamSize = theStream->Position(); + theStream->Seek(0,SEEK_SET); + TotalChildren = 0; + + theMVHDAtom = NULL; +} + +MP4FileReader::~MP4FileReader() +{ + theStream = NULL; + theMVHDAtom = NULL; +} + +bool MP4FileReader::IsEndOfFile(off_t pPosition) +{ + return (pPosition >= StreamSize); +} + +bool MP4FileReader::IsEndOfFile() +{ + return (theStream->Position() >= StreamSize); +} + +bool MP4FileReader::AddChild(AtomBase *pChildAtom) +{ + if (pChildAtom) { + atomChildren[TotalChildren++] = pChildAtom; + return true; + } + return false; +} + +AtomBase *MP4FileReader::GetChildAtom(uint32 patomType, uint32 offset) +{ + for (uint32 i=0;iIsType(patomType)) { + // found match, skip if offset non zero. + if (offset == 0) { + return atomChildren[i]; + } else { + offset--; + } + } else { + if (atomChildren[i]->IsContainer()) { + // search container + AtomBase *aAtomBase = (dynamic_cast(atomChildren[i])->GetChildAtom(patomType, offset)); + if (aAtomBase) { + // found in container + return aAtomBase; + } + // not found + } + } + } + return NULL; +} + +uint32 MP4FileReader::CountChildAtoms(uint32 patomType) +{ + uint32 count = 0; + + while (GetChildAtom(patomType, count) != NULL) { + count++; + } + return count; +} + +MVHDAtom *MP4FileReader::getMVHDAtom() +{ +AtomBase *aAtomBase; + + if (theMVHDAtom == NULL) { + aAtomBase = GetChildAtom(uint32('mvhd')); + theMVHDAtom = dynamic_cast(aAtomBase); + } + + // Assert(theMVHDAtom != NULL,"Movie has no movie header atom"); + return theMVHDAtom; +} + +uint32 MP4FileReader::getMovieTimeScale() +{ + return getMVHDAtom()->getTimeScale(); +} + +bigtime_t MP4FileReader::getMovieDuration() +{ + return ((bigtime_t(getMVHDAtom()->getDuration()) * 1000000L) / getMovieTimeScale()); +} + +uint32 MP4FileReader::getStreamCount() +{ + // count the number of tracks in the file + return (CountChildAtoms(uint32('trak'))); +} + +bigtime_t MP4FileReader::getVideoDuration(uint32 stream_index) +{ +AtomBase *aAtomBase; + + aAtomBase = GetChildAtom(uint32('trak'),stream_index); + + if ((aAtomBase) && (dynamic_cast(aAtomBase)->IsVideo())) { + return (dynamic_cast(aAtomBase)->Duration(1)); + } + + return 0; +} + +bigtime_t MP4FileReader::getAudioDuration(uint32 stream_index) +{ +AtomBase *aAtomBase; + + aAtomBase = GetChildAtom(uint32('trak'),stream_index); + + if ((aAtomBase) && (dynamic_cast(aAtomBase)->IsAudio())) { + return (dynamic_cast(aAtomBase)->Duration(1)); + } + + return 0; +} + +bigtime_t MP4FileReader::getMaxDuration() +{ +AtomBase *aAtomBase; +int32 video_index,audio_index; + video_index = -1; + audio_index = -1; + + // find the active video and audio tracks + for (uint32 i=0;i(aAtomBase)->IsActive())) { + if (dynamic_cast(aAtomBase)->IsAudio()) { + audio_index = int32(i); + } + if (dynamic_cast(aAtomBase)->IsVideo()) { + video_index = int32(i); + } + } + } + + if ((video_index >= 0) && (audio_index >= 0)) { + return MAX(getVideoDuration(video_index),getAudioDuration(audio_index)); + } + if ((video_index < 0) && (audio_index >= 0)) { + return getAudioDuration(audio_index); + } + if ((video_index >= 0) && (audio_index < 0)) { + return getVideoDuration(video_index); + } + + return 0; +} + +uint32 MP4FileReader::getVideoFrameCount(uint32 stream_index) +{ +AtomBase *aAtomBase; + + aAtomBase = GetChildAtom(uint32('trak'),stream_index); + + if ((aAtomBase) && (dynamic_cast(aAtomBase)->IsVideo())) { + + return dynamic_cast(aAtomBase)->FrameCount(); + } + + return 1; +} + +uint32 MP4FileReader::getAudioFrameCount(uint32 stream_index) +{ + if (IsAudio(stream_index)) { + return uint32(((getAudioDuration(stream_index) * AudioFormat(stream_index)->SampleRate) / 1000000L) + 0.5); + } + + return 0; +} + +bool MP4FileReader::IsVideo(uint32 stream_index) +{ + // Look for a trak with a vmhd atom + + AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index); + + if (aAtomBase) { + return (dynamic_cast(aAtomBase)->IsVideo()); + } + + // No trak + return false; +} + +bool MP4FileReader::IsAudio(uint32 stream_index) +{ + // Look for a trak with a smhd atom + + AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index); + + if (aAtomBase) { + return (dynamic_cast(aAtomBase)->IsAudio()); + } + + // No trak + return false; +} + +uint32 MP4FileReader::getFirstFrameInChunk(uint32 stream_index, uint32 pChunkID) +{ + // Find Track + AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index); + if (aAtomBase) { + TRAKAtom *aTrakAtom = dynamic_cast(aAtomBase); + + return aTrakAtom->getFirstSampleInChunk(pChunkID); + } + + return 0; +} + +uint32 MP4FileReader::getNoFramesInChunk(uint32 stream_index, uint32 pFrameNo) +{ + // Find Track + AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index); + if (aAtomBase) { + TRAKAtom *aTrakAtom = dynamic_cast(aAtomBase); + uint32 ChunkNo = 1; + + if (IsAudio(stream_index)) { + ChunkNo = pFrameNo; + } + + if (IsVideo(stream_index)) { + uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo); + + uint32 OffsetInChunk; + ChunkNo = aTrakAtom->getChunkForSample(SampleNo, &OffsetInChunk); + } + + return aTrakAtom->getNoSamplesInChunk(ChunkNo); + } + + return 0; +} + +uint64 MP4FileReader::getOffsetForFrame(uint32 stream_index, uint32 pFrameNo) +{ + // Find Track + AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index); + if (aAtomBase) { + TRAKAtom *aTrakAtom = dynamic_cast(aAtomBase); + + if (IsAudio(stream_index)) { + // FrameNo is really chunk No for audio + uint32 ChunkNo = pFrameNo; + + // Get Offset For Chunk + return aTrakAtom->getOffsetForChunk(ChunkNo); + } + + if (IsVideo(stream_index)) { + + if (pFrameNo < aTrakAtom->FrameCount()) { + // Get Sample for Frame + uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo); + // Get Chunk For Sample and the offset for the frame within that chunk + uint32 OffsetInChunk; + uint32 ChunkNo = aTrakAtom->getChunkForSample(SampleNo, &OffsetInChunk); + // Get Offset For Chunk + uint64 OffsetNo = aTrakAtom->getOffsetForChunk(ChunkNo); + + if (ChunkNo != 0) { + uint32 SizeForSample; + // Adjust the Offset for the Offset in the chunk + if (aTrakAtom->IsSingleSampleSize()) { + SizeForSample = aTrakAtom->getSizeForSample(SampleNo); + OffsetNo = OffsetNo + (OffsetInChunk * SizeForSample); + } else { + // This is bad news performance wise + for (uint32 i=1;i<=OffsetInChunk;i++) { + SizeForSample = aTrakAtom->getSizeForSample(SampleNo-i); + OffsetNo = OffsetNo + SizeForSample; + } + } + } + + return OffsetNo; + } + } + } + + return 0; +} + +status_t MP4FileReader::ParseFile() +{ + AtomBase *aChild; + while (IsEndOfFile() == false) { + aChild = getAtom(theStream); + if (AddChild(aChild)) { + aChild->ProcessMetaData(); + } + } + + // Debug info + for (uint32 i=0;iDisplayAtoms(); + } + + return B_OK; +} + +const mp4_main_header *MP4FileReader::MovMainHeader() +{ + // Fill In theMainHeader +// uint32 micro_sec_per_frame; +// uint32 max_bytes_per_sec; +// uint32 padding_granularity; +// uint32 flags; +// uint32 total_frames; +// uint32 initial_frames; +// uint32 streams; +// uint32 suggested_buffer_size; +// uint32 width; +// uint32 height; + + uint32 videoStream = 0; + + theMainHeader.streams = getStreamCount(); + theMainHeader.flags = 0; + theMainHeader.initial_frames = 0; + + while ( videoStream < theMainHeader.streams ) { + if (IsVideo(videoStream) && IsActive(videoStream)) { + break; + } + videoStream++; + } + + if (videoStream >= theMainHeader.streams) { + theMainHeader.width = 0; + theMainHeader.height = 0; + theMainHeader.total_frames = 0; + theMainHeader.suggested_buffer_size = 0; + theMainHeader.micro_sec_per_frame = 0; + } else { + theMainHeader.width = VideoFormat(videoStream)->width; + theMainHeader.height = VideoFormat(videoStream)->height; + theMainHeader.total_frames = getVideoFrameCount(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); + } + + theMainHeader.padding_granularity = 0; + theMainHeader.max_bytes_per_sec = 0; + + return &theMainHeader; +} + +const AudioMetaData *MP4FileReader::AudioFormat(uint32 stream_index, size_t *size = 0) +{ + if (IsAudio(stream_index)) { + + AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index); + + if (aAtomBase) { + TRAKAtom *aTrakAtom = dynamic_cast(aAtomBase); + + aAtomBase = aTrakAtom->GetChildAtom(uint32('stsd'),0); + if (aAtomBase) { + STSDAtom *aSTSDAtom = dynamic_cast(aAtomBase); + + // Fill In a wave_format_ex structure + AudioDescription aAudioDescription = aSTSDAtom->getAsAudio(); + + theAudio.compression = aAudioDescription.codecid; + + theAudio.NoOfChannels = aAudioDescription.theAudioSampleEntry.ChannelCount; + theAudio.SampleSize = aAudioDescription.theAudioSampleEntry.SampleSize; + theAudio.SampleRate = aAudioDescription.theAudioSampleEntry.SampleRate / 65536; // Convert from fixed point decimal to float + theAudio.PacketSize = uint32((theAudio.SampleRate * theAudio.NoOfChannels * theAudio.SampleSize) / 8); + + return &theAudio; + } + } + } + + return NULL; +} + +const VideoMetaData *MP4FileReader::VideoFormat(uint32 stream_index) +{ + if (IsVideo(stream_index)) { + + AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index); + + if (aAtomBase) { + TRAKAtom *aTrakAtom = dynamic_cast(aAtomBase); + + aAtomBase = aTrakAtom->GetChildAtom(uint32('stsd'),0); + + if (aAtomBase) { + STSDAtom *aSTSDAtom = dynamic_cast(aAtomBase); + + VideoDescription aVideoDescription = aSTSDAtom->getAsVideo(); + + theVideo.compression = aVideoDescription.codecid; + + 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.bit_count = aVideoDescription.theVideoSampleEntry.Depth; + theVideo.image_size = aVideoDescription.theVideoSampleEntry.Height * aVideoDescription.theVideoSampleEntry.Width; + theVideo.HorizontalResolution = aVideoDescription.theVideoSampleEntry.HorizontalResolution; + theVideo.VerticalResolution = aVideoDescription.theVideoSampleEntry.VerticalResolution; + + aAtomBase = aTrakAtom->GetChildAtom(uint32('stts'),0); + if (aAtomBase) { + STTSAtom *aSTTSAtom = dynamic_cast(aAtomBase); + + theVideo.FrameRate = ((aSTTSAtom->getSUMCounts() * 1000000.0L) / aTrakAtom->Duration(1)); + + return &theVideo; + } + } + } + } + + return NULL; +} + +const mp4_stream_header *MP4FileReader::StreamFormat(uint32 stream_index) +{ + // Fill In a Stream Header + theStreamHeader.length = 0; + + if (IsActive(stream_index) == false) { + return NULL; + } + + if (IsVideo(stream_index)) { + theStreamHeader.rate = uint32(1000000L*VideoFormat(stream_index)->FrameRate); + theStreamHeader.scale = 1000000L; + theStreamHeader.length = getVideoFrameCount(stream_index); + } + + if (IsAudio(stream_index)) { + theStreamHeader.rate = uint32(AudioFormat(stream_index)->SampleRate); + theStreamHeader.scale = 1; + theStreamHeader.length = getAudioFrameCount(stream_index); + theStreamHeader.sample_size = AudioFormat(stream_index)->SampleSize; + theStreamHeader.suggested_buffer_size = theStreamHeader.rate * theStreamHeader.sample_size; + } + + return &theStreamHeader; +} + +uint32 MP4FileReader::getChunkSize(uint32 stream_index, uint32 pFrameNo) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index); + if (aAtomBase) { + TRAKAtom *aTrakAtom = dynamic_cast(aAtomBase); + + if (IsAudio(stream_index)) { + + // We read audio in chunk by chunk so chunk size is chunk size + uint32 ChunkNo = pFrameNo; + + // Get first sample in chunk + uint32 SampleNo = aTrakAtom->getFirstSampleInChunk(ChunkNo); + + uint32 ChunkSize = 0; + if (aTrakAtom->IsSingleSampleSize()) { + ChunkSize = aTrakAtom->getNoSamplesInChunk(ChunkNo) * aTrakAtom->getSizeForSample(SampleNo); + } else { + // Add up all sample sizes in chunk + for (uint32 i=0;igetNoSamplesInChunk(ChunkNo);i++) { + ChunkSize += aTrakAtom->getSizeForSample(SampleNo); + SampleNo++; + } + } + + return ChunkSize; + } + + if (IsVideo(stream_index)) { + if (pFrameNo < aTrakAtom->FrameCount()) { + // We read video in Sample by Sample so chunk size is Sample Size + uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo); + return aTrakAtom->getSizeForSample(SampleNo); + } + } + + } + + return 0; +} + +bool MP4FileReader::IsKeyFrame(uint32 stream_index, uint32 pFrameNo) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index); + if (aAtomBase) { + TRAKAtom *aTrakAtom = dynamic_cast(aAtomBase); + + uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo); + return aTrakAtom->IsSyncSample(SampleNo); + } + + return false; +} + +bool MP4FileReader::GetNextChunkInfo(uint32 stream_index, uint32 pFrameNo, off_t *start, uint32 *size, bool *keyframe) +{ + *start = getOffsetForFrame(stream_index, pFrameNo); + *size = getChunkSize(stream_index, pFrameNo); + + if ((*start > 0) && (*size > 0)) { + *keyframe = IsKeyFrame(stream_index, pFrameNo); + } + + return ((*start > 0) && (*size > 0) && !(IsEndOfFile(*start))); +} + +bool MP4FileReader::IsActive(uint32 stream_index) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index); + if (aAtomBase) { + TRAKAtom *aTrakAtom = dynamic_cast(aAtomBase); + return aTrakAtom->IsActive(); + } + + return false; +} + +/* static */ +bool MP4FileReader::IsSupported(BPositionIO *source) +{ + AtomBase *aAtom; + + aAtom = getAtom(source); + if (aAtom) { + return (aAtom->IsKnown()); + } + 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 new file mode 100644 index 0000000000..f1bd2656d5 --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4FileReader.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2005, David McPaul + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _MP4_FILE_READER_H +#define _MP4_FILE_READER_H + +#include +#include +#include +#include + +#include "MP4Parser.h" + +// MP4 file reader +class MP4FileReader { +private: + // Atom List + AtomArray atomChildren; + uint32 TotalChildren; + off_t StreamSize; + BPositionIO *theStream; + + AtomBase *GetChildAtom(uint32 patomType, uint32 offset=0); + uint32 CountChildAtoms(uint32 patomType); + + uint32 getMovieTimeScale(); + + // Add a atom to the children array (return false on failure) + bool AddChild(AtomBase *pChildAtom); + + AudioMetaData theAudio; + VideoMetaData theVideo; + mp4_stream_header theStreamHeader; + mp4_main_header theMainHeader; + + MVHDAtom *theMVHDAtom; + +public: + MP4FileReader(BPositionIO *pStream); + virtual ~MP4FileReader(); + + // getter for MVHDAtom + MVHDAtom *getMVHDAtom(); + + bool IsEndOfFile(off_t pPosition); + bool IsEndOfFile(); + + // Is this a quicktime file + static bool IsSupported(BPositionIO *source); + + // How many tracks in file + uint32 getStreamCount(); + + // Duration defined by the movie header + bigtime_t getMovieDuration(); + + // The first video track duration indexed by stream_index + bigtime_t getVideoDuration(uint32 stream_index); + // the first audio track duration indexed by stream_index + bigtime_t getAudioDuration(uint32 stream_index); + // the max of all active audio or video durations + bigtime_t getMaxDuration(); + + // The no of frames in the video track indexed by stream_index + uint32 getVideoFrameCount(uint32 stream_index); + // The no of frames in the audio track indexed by stream_index + uint32 getAudioFrameCount(uint32 stream_index); + + // Is stream (track) a video track + bool IsVideo(uint32 stream_index); + // Is stream (track) a audio track + bool IsAudio(uint32 stream_index); + + uint32 getNoFramesInChunk(uint32 stream_index, uint32 pFrameNo); + uint32 getFirstFrameInChunk(uint32 stream_index, uint32 pChunkID); + + uint64 getOffsetForFrame(uint32 stream_index, uint32 pFrameNo); + uint32 getChunkSize(uint32 stream_index, uint32 pFrameNo); + bool IsKeyFrame(uint32 stream_index, uint32 pFrameNo); + + status_t ParseFile(); + BPositionIO *Source() {return theStream;}; + + bool IsActive(uint32 stream_index); + + bool GetNextChunkInfo(uint32 stream_index, uint32 pFrameNo, off_t *start, uint32 *size, bool *keyframe); + + // Return all Audio Meta Data + const AudioMetaData *AudioFormat(uint32 stream_index, size_t *size = 0); + // Return all Video Meta Data + const VideoMetaData *VideoFormat(uint32 stream_index); + + // XXX these need work + const mp4_main_header *MovMainHeader(); + const mp4_stream_header *StreamFormat(uint32 stream_index); +}; + +#endif diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.cpp b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.cpp new file mode 100644 index 0000000000..dc4537e301 --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.cpp @@ -0,0 +1,1409 @@ +/* + * Copyright (c) 2005, David McPaul + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#include +#include +#include + +#include +//#include + +#include "MP4Parser.h" + +//static +AtomBase *getAtom(BPositionIO *pStream) +{ + uint32 aAtomType; + uint32 aAtomSize; + uint64 aRealAtomSize; + off_t aStreamOffset; + + aStreamOffset = pStream->Position(); + + // Get Size uint32 + pStream->Read(&aAtomSize,4); + aAtomSize = B_BENDIAN_TO_HOST_INT32(aAtomSize); + // Get Type uint32 + pStream->Read(&aAtomType,4); + aAtomType = B_BENDIAN_TO_HOST_INT32(aAtomType); + // Check for extended size + if (aAtomSize == 1) { + // Handle extended size + pStream->Read(&aRealAtomSize,4); + aRealAtomSize = B_BENDIAN_TO_HOST_INT64(aRealAtomSize); + } else if (aAtomSize == 0) { + // aAtomSize extends to end of file. + // TODO this is broken + aRealAtomSize = 0; + } else { + aRealAtomSize = aAtomSize; + } + + if (aAtomType == uint32('moov')) { + return new MOOVAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('mvhd')) { + return new MVHDAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('trak')) { + return new TRAKAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('tkhd')) { + return new TKHDAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('free')) { + return new FREEAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('skip')) { + return new SKIPAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('wide')) { + return new WIDEAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('mdat')) { + return new MDATAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('mdia')) { + return new MDIAAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('mdhd')) { + return new MDHDAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('hdlr')) { + return new HDLRAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('minf')) { + return new MINFAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('vmhd')) { + return new VMHDAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('smhd')) { + return new SMHDAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('dinf')) { + return new DINFAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('stbl')) { + return new STBLAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('stsd')) { + return new STSDAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('tmcd')) { + return new TMCDAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('stts')) { + return new STTSAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('pnot')) { + return new PNOTAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('stsc')) { + return new STSCAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('stco')) { + return new STCOAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('stss')) { + return new STSSAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('ctts')) { + return new CTTSAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('stsz')) { + return new STSZAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('stz2')) { + return new STZ2Atom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('ftyp')) { + return new FTYPAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('cmov')) { + return new CMOVAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('dcom')) { + return new DCOMAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + if (aAtomType == uint32('cmvd')) { + return new CMVDAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize); + } + + return new AtomBase(pStream, aStreamOffset, aAtomType, aRealAtomSize); + +} + +MOOVAtom::MOOVAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomContainer(pStream, pstreamOffset, patomType, patomSize) +{ + theMVHDAtom = NULL; +} + +MOOVAtom::~MOOVAtom() +{ + theMVHDAtom = NULL; +} + +void MOOVAtom::OnProcessMetaData() +{ +} + +char *MOOVAtom::OnGetAtomName() +{ + return "Quicktime Movie"; +} + +CMOVAtom::CMOVAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomContainer(pStream, pstreamOffset, patomType, patomSize) +{ + theUncompressedStream = NULL; +} + +CMOVAtom::~CMOVAtom() +{ +} + +BPositionIO *CMOVAtom::OnGetStream() +{ + // Use the decompressed stream instead of file stream + if (theUncompressedStream) { + return theUncompressedStream; + } + + return theStream; +} + +void CMOVAtom::OnProcessMetaData() +{ + BMallocIO *theUncompressedData; + uint8 *outBuffer; + CMVDAtom *aCMVDAtom = NULL; + uint32 compressionID = 0; + uint64 descBytesLeft; + uint32 Size; + + descBytesLeft = getAtomSize(); + + // Check for Compression Type + while (descBytesLeft > 0) { + AtomBase *aAtomBase = getAtom(theStream); + + aAtomBase->OnProcessMetaData(); + printf("%s [%Ld]\n",aAtomBase->getAtomName(),aAtomBase->getAtomSize()); + + if (aAtomBase->getAtomSize() > 0) { + descBytesLeft = descBytesLeft - aAtomBase->getAtomSize(); + } else { + printf("Invalid Atom found when reading Compressed Headers\n"); + descBytesLeft = 0; + } + + if (dynamic_cast(aAtomBase)) { + // DCOM atom + compressionID = dynamic_cast(aAtomBase)->getCompressionID(); + delete aAtomBase; + } else { + if (dynamic_cast(aAtomBase)) { + // CMVD atom + aCMVDAtom = dynamic_cast(aAtomBase); + descBytesLeft = 0; + } + } + } + + // Decompress data + if (compressionID == 'zlib') { + Size = aCMVDAtom->getUncompressedSize(); + + outBuffer = (uint8 *)(malloc(Size)); + + printf("Decompressing %ld bytes to %ld bytes\n",aCMVDAtom->getBufferSize(),Size); + int result = uncompress(outBuffer, &Size, aCMVDAtom->getCompressedData(), aCMVDAtom->getBufferSize()); + + if (result != Z_OK) { + printf("Failed to decompress headers uncompress returned "); + switch (result) { + case Z_MEM_ERROR: + DEBUGGER("Lack of Memory Error\n"); + break; + case Z_BUF_ERROR: + DEBUGGER("Lack of Output buffer space Error\n"); + break; + case Z_DATA_ERROR: + DEBUGGER("Input Data is corrupt or not a compressed set Error\n"); + break; + } + } + + // Copy uncompressed data into BMAllocIO + theUncompressedData = new BMallocIO(); + theUncompressedData->SetSize(Size); + theUncompressedData->WriteAt(0L,outBuffer,Size); + + free(outBuffer); + delete aCMVDAtom; + + // reset position on BMAllocIO + theUncompressedData->Seek(SEEK_SET,0L); + // Assign to Stream + theUncompressedStream = theUncompressedData; + + // All subsequent reads should use theUncompressedStream + } + +} + +void CMOVAtom::OnChildProcessingComplete() +{ + // revert back to file stream once all children have finished + if (theUncompressedStream) { + delete theUncompressedStream; + } + theUncompressedStream = NULL; +} + +char *CMOVAtom::OnGetAtomName() +{ + return "Compressed Quicktime Movie"; +} + +DCOMAtom::DCOMAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) +{ +} + +DCOMAtom::~DCOMAtom() +{ +} + +void DCOMAtom::OnProcessMetaData() +{ + theStream->Read(&compressionID,sizeof(uint32)); + + compressionID = B_BENDIAN_TO_HOST_INT32(compressionID); +} + +char *DCOMAtom::OnGetAtomName() +{ + return "Decompression Atom"; +} + +CMVDAtom::CMVDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) +{ + Buffer = NULL; + UncompressedSize = 0; + BufferSize = 0; +} + +CMVDAtom::~CMVDAtom() +{ + if (Buffer) { + free(Buffer); + } +} + +void CMVDAtom::OnProcessMetaData() +{ + theStream->Read(&UncompressedSize,sizeof(uint32)); + + UncompressedSize = B_BENDIAN_TO_HOST_INT32(UncompressedSize); + + if (UncompressedSize > 0) { + BufferSize = getBytesRemaining(); + Buffer = (uint8 *)(malloc(BufferSize)); + theStream->Read(Buffer,BufferSize); + } +} + +char *CMVDAtom::OnGetAtomName() +{ + return "Compressed Movie Data"; +} + +MVHDAtom::MVHDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ +} + +MVHDAtom::~MVHDAtom() +{ +} + +void MVHDAtom::OnProcessMetaData() +{ + FullAtom::OnProcessMetaData(); + + if (getVersion() == 0) { + mvhdV0 aHeader; + theStream->Read(&aHeader,sizeof(mvhdV0)); + + theHeader.CreationTime = (uint64)(B_BENDIAN_TO_HOST_INT32(aHeader.CreationTime)); + theHeader.ModificationTime = (uint64)(B_BENDIAN_TO_HOST_INT32(aHeader.ModificationTime)); + theHeader.TimeScale = B_BENDIAN_TO_HOST_INT32(aHeader.TimeScale); + theHeader.Duration = (uint64)(B_BENDIAN_TO_HOST_INT32(aHeader.Duration)); + theHeader.PreferredRate = B_BENDIAN_TO_HOST_INT32(aHeader.PreferredRate); + theHeader.PreferredVolume = B_BENDIAN_TO_HOST_INT16(aHeader.PreferredVolume); + theHeader.NextTrackID = B_BENDIAN_TO_HOST_INT32(aHeader.NextTrackID); + } + + if (getVersion() == 1) { + theStream->Read(&theHeader,sizeof(mvhdV1)); + + theHeader.CreationTime = B_BENDIAN_TO_HOST_INT64(theHeader.CreationTime); + theHeader.ModificationTime = B_BENDIAN_TO_HOST_INT64(theHeader.ModificationTime); + theHeader.TimeScale = B_BENDIAN_TO_HOST_INT32(theHeader.TimeScale); + theHeader.Duration = B_BENDIAN_TO_HOST_INT64(theHeader.Duration); + theHeader.PreferredRate = B_BENDIAN_TO_HOST_INT32(theHeader.PreferredRate); + theHeader.PreferredVolume = B_BENDIAN_TO_HOST_INT16(theHeader.PreferredVolume); + theHeader.NextTrackID = B_BENDIAN_TO_HOST_INT32(theHeader.NextTrackID); + } +} + +char *MVHDAtom::OnGetAtomName() +{ + return "Quicktime Movie Header"; +} + +MVHDAtom *MOOVAtom::getMVHDAtom() +{ +AtomBase *aAtomBase; + + if (theMVHDAtom == NULL) { + aAtomBase = GetChildAtom(uint32('mvhd')); + theMVHDAtom = dynamic_cast(aAtomBase); + } + + // Assert(theMVHDAtom != NULL,"Movie has no movie header atom"); + return theMVHDAtom; +} + +STTSAtom::STTSAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ + theHeader.NoEntries = 0; + SUMDurations = 0; + SUMCounts = 0; +} + +STTSAtom::~STTSAtom() +{ + for (uint32 i=0;iRead(aTimeToSample,sizeof(TimeToSample)); + aTimeToSample->Count = B_BENDIAN_TO_HOST_INT32(aTimeToSample->Count); + aTimeToSample->Duration = B_BENDIAN_TO_HOST_INT32(aTimeToSample->Duration); + + theTimeToSampleArray[i] = aTimeToSample; + SUMDurations += (theTimeToSampleArray[i]->Duration * theTimeToSampleArray[i]->Count); + SUMCounts += theTimeToSampleArray[i]->Count; + } +} + +char *STTSAtom::OnGetAtomName() +{ + return "Time to Sample Atom"; +} + +uint32 STTSAtom::getSampleForTime(uint32 pTime) +{ +// TODO this is too slow. PreCalc when loading this? + uint64 Duration = 0; + + for (uint32 i=0;iDuration * theTimeToSampleArray[i]->Count); + if (Duration > pTime) { + return i; + } + } + + return 0; +} + +uint32 STTSAtom::getSampleForFrame(uint32 pFrame) +{ +// Hmm Sample is Frame really, this Atom is more usefull for time->sample calcs + return pFrame; +} + +CTTSAtom::CTTSAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ + theHeader.NoEntries = 0; +} + +CTTSAtom::~CTTSAtom() +{ + for (uint32 i=0;iRead(aCompTimeToSample,sizeof(CompTimeToSample)); + aCompTimeToSample->Count = B_BENDIAN_TO_HOST_INT32(aCompTimeToSample->Count); + aCompTimeToSample->Offset = B_BENDIAN_TO_HOST_INT32(aCompTimeToSample->Offset); + + theCompTimeToSampleArray[i] = aCompTimeToSample; + } +} + +char *CTTSAtom::OnGetAtomName() +{ + return "Composition Time to Sample Atom"; +} + +STSCAtom::STSCAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ + theHeader.NoEntries = 0; +} + +STSCAtom::~STSCAtom() +{ + for (uint32 i=0;iRead(&aSampleToChunk->FirstChunk,sizeof(uint32)); + theStream->Read(&aSampleToChunk->SamplesPerChunk,sizeof(uint32)); + theStream->Read(&aSampleToChunk->SampleDescriptionID,sizeof(uint32)); + + aSampleToChunk->FirstChunk = B_BENDIAN_TO_HOST_INT32(aSampleToChunk->FirstChunk); + aSampleToChunk->SamplesPerChunk = B_BENDIAN_TO_HOST_INT32(aSampleToChunk->SamplesPerChunk); + aSampleToChunk->SampleDescriptionID = B_BENDIAN_TO_HOST_INT32(aSampleToChunk->SampleDescriptionID); + + if (i > 0) { + TotalPrevFrames = TotalPrevFrames + (aSampleToChunk->FirstChunk - theSampleToChunkArray[i-1]->FirstChunk) * theSampleToChunkArray[i-1]->SamplesPerChunk; + aSampleToChunk->TotalPrevFrames = TotalPrevFrames; + } else { + aSampleToChunk->TotalPrevFrames = 0; + } + + theSampleToChunkArray[i] = aSampleToChunk; + } +} + +char *STSCAtom::OnGetAtomName() +{ + return "Sample to Chunk Atom"; +} + +uint32 STSCAtom::getNoSamplesInChunk(uint32 pChunkID) +{ + for (uint32 i=0;iFirstChunk > pChunkID) { + return theSampleToChunkArray[i-1]->SamplesPerChunk; + } + } + + return 0; +} + +uint32 STSCAtom::getFirstSampleInChunk(uint32 pChunkID) +{ + for (uint32 i=0;iFirstChunk > pChunkID) { + uint32 Diff = pChunkID - theSampleToChunkArray[i-1]->FirstChunk; + uint32 pSampleNo = (Diff * theSampleToChunkArray[i-1]->SamplesPerChunk) + theSampleToChunkArray[i-1]->TotalPrevFrames; + return pSampleNo; + } + } + + return 0; +} + +uint32 STSCAtom::getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk) +{ + uint32 ChunkID = 0; + uint32 OffsetInChunk = 0; + + for (int32 i=theHeader.NoEntries-1;i>=0;i--) { + if (pSample >= theSampleToChunkArray[i]->TotalPrevFrames) { + // Found chunk now calculate offset + ChunkID = (pSample - theSampleToChunkArray[i]->TotalPrevFrames) / theSampleToChunkArray[i]->SamplesPerChunk; + ChunkID += theSampleToChunkArray[i]->FirstChunk; + + OffsetInChunk = (pSample - theSampleToChunkArray[i]->TotalPrevFrames) % theSampleToChunkArray[i]->SamplesPerChunk; + + *pOffsetInChunk = OffsetInChunk; + return ChunkID; + } + } + + *pOffsetInChunk = 0; + return theSampleToChunkArray[theHeader.NoEntries-1]->FirstChunk; +} + +STSSAtom::STSSAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ + theHeader.NoEntries = 0; +} + +STSSAtom::~STSSAtom() +{ + for (uint32 i=0;iRead(aSyncSample,sizeof(SyncSample)); + aSyncSample->SyncSampleNo = B_BENDIAN_TO_HOST_INT32(aSyncSample->SyncSampleNo); + + theSyncSampleArray[i] = aSyncSample; + } +} + +char *STSSAtom::OnGetAtomName() +{ + return "Sync Sample Atom"; +} + +bool STSSAtom::IsSyncSample(uint32 pSampleNo) +{ + + for (uint32 i=0;iSyncSampleNo == pSampleNo) { + return true; + } + + if (pSampleNo > theSyncSampleArray[i]->SyncSampleNo) { + return false; + } + } + + return false; +} + +STSZAtom::STSZAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ + SampleCount = 0; +} + +STSZAtom::~STSZAtom() +{ + for (uint32 i=0;iRead(&SampleSize,sizeof(uint32)); + theStream->Read(&SampleCount,sizeof(uint32)); + + SampleSize = B_BENDIAN_TO_HOST_INT32(SampleCount); + SampleCount = B_BENDIAN_TO_HOST_INT32(SampleCount); + + // If the sample size is constant there is no array and NoEntries seems to contain bad values + if (SampleSize == 0) { + SampleSizePtr aSampleSizePtr; + + for (uint32 i=0;iRead(aSampleSizePtr,sizeof(SampleSizeEntry)); + aSampleSizePtr->EntrySize = B_BENDIAN_TO_HOST_INT32(aSampleSizePtr->EntrySize); + + theSampleSizeArray[i] = aSampleSizePtr; + } + } +} + +char *STSZAtom::OnGetAtomName() +{ + return "Sample Size Atom"; +} + +uint32 STSZAtom::getSizeForSample(uint32 pSampleNo) +{ + if (SampleSize > 0) { + // All samples are the same size + return SampleSize; + } + + // Sample Array indexed by SampleNo + return theSampleSizeArray[pSampleNo]->EntrySize; +} + +bool STSZAtom::IsSingleSampleSize() +{ + return (SampleSize > 0); +} + +STZ2Atom::STZ2Atom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ + SampleCount = 0; +} + +STZ2Atom::~STZ2Atom() +{ + for (uint32 i=0;iRead(&reserved,sizeof(uint8)); + theStream->Read(&reserved,sizeof(uint8)); + theStream->Read(&reserved,sizeof(uint8)); + + theStream->Read(&FieldSize,sizeof(uint8)); + theStream->Read(&SampleCount,sizeof(uint32)); + + SampleCount = B_BENDIAN_TO_HOST_INT32(SampleCount); + + SampleSizePtr aSampleSizePtr; + uint8 EntrySize8; + uint16 EntrySize16; + + for (uint32 i=0;iRead(&EntrySize8,sizeof(uint8)); + // 2 values per byte + aSampleSizePtr->EntrySize = (uint32)(EntrySize8); + break; + case 8: + theStream->Read(&EntrySize8,sizeof(uint8)); + // 1 value per byte + aSampleSizePtr->EntrySize = (uint32)(EntrySize8); + break; + case 16: + theStream->Read(&EntrySize16,sizeof(uint16)); + // 1 value per 2 bytes + aSampleSizePtr->EntrySize = (uint32)(EntrySize16); + break; + } + + theSampleSizeArray[i] = aSampleSizePtr; + } +} + +char *STZ2Atom::OnGetAtomName() +{ + return "Compressed Sample Size Atom"; +} + +uint32 STZ2Atom::getSizeForSample(uint32 pSampleNo) +{ +// THIS CODE NEEDS SOME TESTING, never seen a STZ2 atom + +uint32 index; + + switch (FieldSize) { + case 4: + // 2 entries per array entry so Divide by 2 + index = pSampleNo / 2; + if (index * 2 == pSampleNo) { + // even so return low nibble + return theSampleSizeArray[pSampleNo]->EntrySize && 0x0000000f; + } else { + // odd so return high nibble + return theSampleSizeArray[pSampleNo]->EntrySize && 0x000000f0; + } + break; + case 8: + return theSampleSizeArray[pSampleNo]->EntrySize; + break; + case 16: + return theSampleSizeArray[pSampleNo]->EntrySize; + break; + } + + // Sample Array indexed by SampleNo + return theSampleSizeArray[pSampleNo]->EntrySize; +} + +bool STZ2Atom::IsSingleSampleSize() +{ + return false; +} + +STCOAtom::STCOAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ + theHeader.NoEntries = 0; +} + +STCOAtom::~STCOAtom() +{ + for (uint32 i=0;iRead(&Offset,sizeof(uint32)); + + // Upconvert to uint64 + return uint64(B_BENDIAN_TO_HOST_INT32(Offset)); +} + +void STCOAtom::OnProcessMetaData() +{ +ChunkToOffset *aChunkToOffset; + + FullAtom::OnProcessMetaData(); + + ReadArrayHeader(&theHeader); + + for (uint32 i=0;iOffset = OnGetChunkOffset(); + + theChunkToOffsetArray[i] = aChunkToOffset; + } + + PRINT(("Chunk to Offset Array has %ld entries\n",theHeader.NoEntries)); +} + +char *STCOAtom::OnGetAtomName() +{ + return "Chunk to Offset Atom"; +} + +uint64 STCOAtom::getOffsetForChunk(uint32 pChunkID) +{ + // First chunk is first array entry + if ((pChunkID > 0) && (pChunkID <= theHeader.NoEntries)) { + return theChunkToOffsetArray[pChunkID - 1]->Offset; + } + + DEBUGGER(("Bad Chunk ID %ld / %ld\n",pChunkID,theHeader.NoEntries)); + + TRESPASS(); + return theChunkToOffsetArray[0]->Offset; +} + +STSDAtom::STSDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ + theHeader.NoEntries = 0; +} + +STSDAtom::~STSDAtom() +{ +} + +uint32 STSDAtom::getMediaHandlerType() +{ + return dynamic_cast(getParent())->getMediaHandlerType(); +} + +void STSDAtom::ReadSoundDescription() +{ + theStream->Read(&theAudioDescription.theAudioSampleEntry,sizeof(AudioSampleEntry)); + + theAudioDescription.theAudioSampleEntry.ChannelCount = B_BENDIAN_TO_HOST_INT16(theAudioDescription.theAudioSampleEntry.ChannelCount); + theAudioDescription.theAudioSampleEntry.SampleSize = B_BENDIAN_TO_HOST_INT16(theAudioDescription.theAudioSampleEntry.SampleSize); + theAudioDescription.theAudioSampleEntry.SampleRate = B_BENDIAN_TO_HOST_INT32(theAudioDescription.theAudioSampleEntry.SampleRate); +} + +void STSDAtom::ReadVideoDescription() +{ + theStream->Read(&theVideoDescription.theVideoSampleEntry,sizeof(VideoSampleEntry)); + + theVideoDescription.theVideoSampleEntry.Width = B_BENDIAN_TO_HOST_INT16(theVideoDescription.theVideoSampleEntry.Width); + theVideoDescription.theVideoSampleEntry.Height = B_BENDIAN_TO_HOST_INT16(theVideoDescription.theVideoSampleEntry.Height); + theVideoDescription.theVideoSampleEntry.HorizontalResolution = B_BENDIAN_TO_HOST_INT32(theVideoDescription.theVideoSampleEntry.HorizontalResolution); + theVideoDescription.theVideoSampleEntry.VerticalResolution = B_BENDIAN_TO_HOST_INT32(theVideoDescription.theVideoSampleEntry.VerticalResolution); + theVideoDescription.theVideoSampleEntry.FrameCount = B_BENDIAN_TO_HOST_INT16(theVideoDescription.theVideoSampleEntry.FrameCount); + theVideoDescription.theVideoSampleEntry.Depth = B_BENDIAN_TO_HOST_INT16(theVideoDescription.theVideoSampleEntry.Depth); + + // convert from pascal string (first bytes size) to C string (null terminated) + uint8 size = (uint8)(theVideoDescription.theVideoSampleEntry.CompressorName[0]); + memcpy(&theVideoDescription.theVideoSampleEntry.CompressorName[0],&theVideoDescription.theVideoSampleEntry.CompressorName[1],size); + theVideoDescription.theVideoSampleEntry.CompressorName[size] = '\0'; + + printf("Compressor Name %s\n",theVideoDescription.theVideoSampleEntry.CompressorName); +} + +void STSDAtom::OnProcessMetaData() +{ + + FullAtom::OnProcessMetaData(); + + ReadArrayHeader(&theHeader); + + // This Atom allows for multiple Desc Entries, we don't + + // Ok the committee was drunk when they designed this. + // We are actually reading a Atom/Box where the Atom Type is the codec id. + uint32 Size; + + theStream->Read(&Size,sizeof(uint32)); + theStream->Read(&codecid,sizeof(uint32)); + + Size = B_BENDIAN_TO_HOST_INT32(Size); + codecid = B_BENDIAN_TO_HOST_INT32(codecid); + + theStream->Read(&theSampleEntry,sizeof(SampleEntry)); + theSampleEntry.DataReference = B_BENDIAN_TO_HOST_INT16(theSampleEntry.DataReference); + + switch (getMediaHandlerType()) { + case 'soun': + ReadSoundDescription(); + theAudioDescription.codecid = codecid; + break; + case 'vide': + ReadVideoDescription(); + theVideoDescription.codecid = codecid; + break; + case 'hint': + break; + default: + break; + } +} + +VideoDescription STSDAtom::getAsVideo() +{ + // Assert IsVideo + return theVideoDescription; +} + +AudioDescription STSDAtom::getAsAudio() +{ + // Assert IsAudio + return theAudioDescription; +} + +char *STSDAtom::OnGetAtomName() +{ + return "Sample Description Atom"; +} + +TMCDAtom::TMCDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) +{ +} + +TMCDAtom::~TMCDAtom() +{ +} + +void TMCDAtom::OnProcessMetaData() +{ +} + +char *TMCDAtom::OnGetAtomName() +{ + return "TimeCode Atom"; +} + +WIDEAtom::WIDEAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) +{ +} + +WIDEAtom::~WIDEAtom() +{ +} + +void WIDEAtom::OnProcessMetaData() +{ +} + +char *WIDEAtom::OnGetAtomName() +{ + return "WIDE Atom"; +} + +FTYPAtom::FTYPAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) +{ + total_brands = 0; +} + +FTYPAtom::~FTYPAtom() +{ +} + +void FTYPAtom::OnProcessMetaData() +{ + theStream->Read(&major_brand,sizeof(uint32)); + theStream->Read(&minor_version,sizeof(uint32)); + + major_brand = B_BENDIAN_TO_HOST_INT32(major_brand); + minor_version = B_BENDIAN_TO_HOST_INT32(minor_version); + + total_brands = getBytesRemaining() / sizeof(uint32); + + if (total_brands > 32) { + total_brands = 32; // restrict to 32 + } + + for (uint32 i=0;iRead(&compatable_brands[i],sizeof(uint32)); + compatable_brands[i] = B_BENDIAN_TO_HOST_INT32(compatable_brands[i]); + } + +} + +char *FTYPAtom::OnGetAtomName() +{ + return "File type Atom"; +} + +FREEAtom::FREEAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) +{ +} + +FREEAtom::~FREEAtom() +{ +} + +void FREEAtom::OnProcessMetaData() +{ +} + +char *FREEAtom::OnGetAtomName() +{ + return "Free Atom"; +} + +PNOTAtom::PNOTAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) +{ +} + +PNOTAtom::~PNOTAtom() +{ +} + +void PNOTAtom::OnProcessMetaData() +{ +} + +char *PNOTAtom::OnGetAtomName() +{ + return "Preview Atom"; +} + +SKIPAtom::SKIPAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) +{ +} + +SKIPAtom::~SKIPAtom() +{ +} + +void SKIPAtom::OnProcessMetaData() +{ +} + +char *SKIPAtom::OnGetAtomName() +{ + return "Skip Atom"; +} + +MDATAtom::MDATAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize) +{ +} + +MDATAtom::~MDATAtom() +{ +} + +void MDATAtom::OnProcessMetaData() +{ +} + +char *MDATAtom::OnGetAtomName() +{ + return "Media Data Atom"; +} + +MINFAtom::MINFAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomContainer(pStream, pstreamOffset, patomType, patomSize) +{ +} + +MINFAtom::~MINFAtom() +{ +} + +void MINFAtom::OnProcessMetaData() +{ +} + +char *MINFAtom::OnGetAtomName() +{ + return "Quicktime Media Information Atom"; +} + +uint32 MINFAtom::getMediaHandlerType() +{ + return dynamic_cast(getParent())->getMediaHandlerType(); +} + +STBLAtom::STBLAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomContainer(pStream, pstreamOffset, patomType, patomSize) +{ +} + +STBLAtom::~STBLAtom() +{ +} + +void STBLAtom::OnProcessMetaData() +{ +} + +char *STBLAtom::OnGetAtomName() +{ + return "Quicktime Sample Table Atom"; +} + +uint32 STBLAtom::getMediaHandlerType() +{ + return dynamic_cast(getParent())->getMediaHandlerType(); +} + +DINFAtom::DINFAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomContainer(pStream, pstreamOffset, patomType, patomSize) +{ +} + +DINFAtom::~DINFAtom() +{ +} + +void DINFAtom::OnProcessMetaData() +{ +} + +char *DINFAtom::OnGetAtomName() +{ + return "Quicktime Data Information Atom"; +} + +TKHDAtom::TKHDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ +} + +TKHDAtom::~TKHDAtom() +{ +} + +void TKHDAtom::OnProcessMetaData() +{ + + FullAtom::OnProcessMetaData(); + + if (getVersion() == 0) { + tkhdV0 aHeaderV0; + theStream->Read(&aHeaderV0,sizeof(tkhdV0)); + + for (uint32 i=0;i<9;i++) { + theHeader.MatrixStructure[i] = B_BENDIAN_TO_HOST_INT32(aHeaderV0.MatrixStructure[i]); + } + + // upconvert to V1 header + theHeader.CreationTime = (uint64)(B_BENDIAN_TO_HOST_INT32(aHeaderV0.CreationTime)); + theHeader.ModificationTime = (uint64)(B_BENDIAN_TO_HOST_INT32(aHeaderV0.ModificationTime)); + theHeader.TrackID = B_BENDIAN_TO_HOST_INT32(aHeaderV0.TrackID); + theHeader.Duration = (uint64)(B_BENDIAN_TO_HOST_INT32(aHeaderV0.Duration)); + theHeader.Layer = B_BENDIAN_TO_HOST_INT16(aHeaderV0.Layer); + theHeader.AlternateGroup = B_BENDIAN_TO_HOST_INT16(aHeaderV0.AlternateGroup); + theHeader.Volume = B_BENDIAN_TO_HOST_INT16(aHeaderV0.Volume); + theHeader.TrackWidth = B_BENDIAN_TO_HOST_INT32(aHeaderV0.TrackWidth); + theHeader.TrackHeight = B_BENDIAN_TO_HOST_INT32(aHeaderV0.TrackHeight); + + } else { + theStream->Read(&theHeader,sizeof(tkhdV1)); + + for (uint32 i=0;i<9;i++) { + theHeader.MatrixStructure[i] = B_BENDIAN_TO_HOST_INT32(theHeader.MatrixStructure[i]); + } + + theHeader.CreationTime = B_BENDIAN_TO_HOST_INT64(theHeader.CreationTime); + theHeader.ModificationTime = B_BENDIAN_TO_HOST_INT64(theHeader.ModificationTime); + theHeader.TrackID = B_BENDIAN_TO_HOST_INT32(theHeader.TrackID); + theHeader.Duration = B_BENDIAN_TO_HOST_INT64(theHeader.Duration); + theHeader.Layer = B_BENDIAN_TO_HOST_INT16(theHeader.Layer); + theHeader.AlternateGroup = B_BENDIAN_TO_HOST_INT16(theHeader.AlternateGroup); + theHeader.Volume = B_BENDIAN_TO_HOST_INT16(theHeader.Volume); + theHeader.TrackWidth = B_BENDIAN_TO_HOST_INT32(theHeader.TrackWidth); + theHeader.TrackHeight = B_BENDIAN_TO_HOST_INT32(theHeader.TrackHeight); + } + +} + +char *TKHDAtom::OnGetAtomName() +{ + return "Quicktime Track Header"; +} + +MDIAAtom::MDIAAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomContainer(pStream, pstreamOffset, patomType, patomSize) +{ +} + +MDIAAtom::~MDIAAtom() +{ +} + +void MDIAAtom::OnProcessMetaData() +{ +} + +char *MDIAAtom::OnGetAtomName() +{ + return "Quicktime Media Atom"; +} + +uint32 MDIAAtom::getMediaHandlerType() +{ + // get child atom hdlr + HDLRAtom *aHDLRAtom; + aHDLRAtom = dynamic_cast(GetChildAtom(uint32('hdlr'))); + + if (aHDLRAtom) { + return aHDLRAtom->getMediaHandlerType(); + } + + return 0; +} + +MDHDAtom::MDHDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ +} + +MDHDAtom::~MDHDAtom() +{ +} + +void MDHDAtom::OnProcessMetaData() +{ + FullAtom::OnProcessMetaData(); + + if (getVersion() == 0) { + mdhdV0 aHeader; + + theStream->Read(&aHeader,sizeof(mdhdV0)); + + theHeader.CreationTime = (uint64)(B_BENDIAN_TO_HOST_INT32(aHeader.CreationTime)); + theHeader.ModificationTime = (uint64)(B_BENDIAN_TO_HOST_INT32(aHeader.ModificationTime)); + theHeader.TimeScale = B_BENDIAN_TO_HOST_INT32(aHeader.TimeScale); + theHeader.Duration = (uint64)(B_BENDIAN_TO_HOST_INT32(aHeader.Duration)); + theHeader.Language = B_BENDIAN_TO_HOST_INT16(aHeader.Language); + } else { + theStream->Read(&theHeader,sizeof(mdhdV1)); + + theHeader.CreationTime = B_BENDIAN_TO_HOST_INT64(theHeader.CreationTime); + theHeader.ModificationTime = B_BENDIAN_TO_HOST_INT64(theHeader.ModificationTime); + theHeader.TimeScale = B_BENDIAN_TO_HOST_INT32(theHeader.TimeScale); + theHeader.Duration = B_BENDIAN_TO_HOST_INT64(theHeader.Duration); + theHeader.Language = B_BENDIAN_TO_HOST_INT16(theHeader.Language); + } +} + +char *MDHDAtom::OnGetAtomName() +{ + return "Quicktime Media Header"; +} + +bigtime_t MDHDAtom::getDuration() +{ + return bigtime_t((uint64(theHeader.Duration) * 1000000L) / theHeader.TimeScale); +} + +uint32 MDHDAtom::getTimeScale() +{ + return theHeader.TimeScale; +} + +HDLRAtom::HDLRAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ + name = NULL; +} + +HDLRAtom::~HDLRAtom() +{ + if (name) { + free(name); + } +} + +void HDLRAtom::OnProcessMetaData() +{ + FullAtom::OnProcessMetaData(); + + theStream->Read(&theHeader,sizeof(hdlr)); + theHeader.handler_type = B_BENDIAN_TO_HOST_INT32(theHeader.handler_type); + + name = (char *)(malloc(getBytesRemaining())); + + theStream->Read(name,getBytesRemaining()); +} + +char *HDLRAtom::OnGetAtomName() +{ + printf("%s %s:",(char *)&(theHeader.handler_type),name); + return "Quicktime Handler Reference Atom "; +} + +bool HDLRAtom::IsVideoHandler() +{ + return (theHeader.handler_type == 'vide'); +} + +bool HDLRAtom::IsAudioHandler() +{ + return (theHeader.handler_type == 'soun'); +} + +uint32 HDLRAtom::getMediaHandlerType() +{ + return theHeader.handler_type; +} + +VMHDAtom::VMHDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ +} + +VMHDAtom::~VMHDAtom() +{ +} + +void VMHDAtom::OnProcessMetaData() +{ + FullAtom::OnProcessMetaData(); + + theStream->Read(&theHeader,sizeof(vmhd)); + theHeader.GraphicsMode = B_BENDIAN_TO_HOST_INT16(theHeader.GraphicsMode); + theHeader.OpColour[0] = B_BENDIAN_TO_HOST_INT16(theHeader.OpColour[0]); + theHeader.OpColour[1] = B_BENDIAN_TO_HOST_INT16(theHeader.OpColour[1]); + theHeader.OpColour[2] = B_BENDIAN_TO_HOST_INT16(theHeader.OpColour[2]); +} + +char *VMHDAtom::OnGetAtomName() +{ + return "Quicktime Video Media Header"; +} + +SMHDAtom::SMHDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : FullAtom(pStream, pstreamOffset, patomType, patomSize) +{ +} + +SMHDAtom::~SMHDAtom() +{ +} + +void SMHDAtom::OnProcessMetaData() +{ + FullAtom::OnProcessMetaData(); + + theStream->Read(&theHeader,sizeof(smhd)); + theHeader.Balance = B_BENDIAN_TO_HOST_INT16(theHeader.Balance); +} + +char *SMHDAtom::OnGetAtomName() +{ + return "Quicktime Sound Media Header"; +} diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.h b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.h new file mode 100644 index 0000000000..0af530d2d4 --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Parser.h @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2005, David McPaul + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _MP4_PARSER_H +#define _MP4_PARSER_H + +#include +#include +#include +#include + +// Std Headers +#include + +#include "MP4Atom.h" + +typedef CompTimeToSample* CompTimeToSamplePtr; +typedef std::map > CompTimeToSampleArray; +typedef TimeToSample* TimeToSamplePtr; +typedef std::map > TimeToSampleArray; +typedef SampleToChunk* SampleToChunkPtr; +typedef std::map > SampleToChunkArray; +typedef ChunkToOffset* ChunkToOffsetPtr; +typedef std::map > ChunkToOffsetArray; +typedef SyncSample* SyncSamplePtr; +typedef std::map > SyncSampleArray; +typedef SampleSizeEntry* SampleSizePtr; +typedef std::map > SampleSizeArray; + +// Atom class for reading the movie header atom +class MVHDAtom : public FullAtom { +public: + MVHDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~MVHDAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + uint32 getTimeScale() {return theHeader.TimeScale;}; + uint32 getDuration() {return theHeader.Duration;}; +private: + mvhdV1 theHeader; +}; + +// Atom class for reading the moov atom +class MOOVAtom : public AtomContainer { +public: + MOOVAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~MOOVAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + MVHDAtom *getMVHDAtom(); +private: + MVHDAtom *theMVHDAtom; +}; + +// Atom class for reading the cmov atom +class CMOVAtom : public AtomContainer { +public: + CMOVAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~CMOVAtom(); + + BPositionIO *OnGetStream(); + void OnProcessMetaData(); + void OnChildProcessingComplete(); + + char *OnGetAtomName(); +private: + BMallocIO *theUncompressedStream; +}; + +// Atom class for reading the dcom atom +class DCOMAtom : public AtomBase { +public: + DCOMAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~DCOMAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + uint32 getCompressionID() {return compressionID;}; +private: + uint32 compressionID; +}; + +// Atom class for reading the cmvd atom +class CMVDAtom : public AtomBase { +public: + CMVDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~CMVDAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + uint32 getUncompressedSize() {return UncompressedSize;}; + uint8 *getCompressedData() {return Buffer;}; + uint32 getBufferSize() {return BufferSize;}; +private: + uint32 UncompressedSize; + uint32 BufferSize; + uint8 *Buffer; +}; + +// Atom class for reading the ftyp atom +class FTYPAtom : public AtomBase { +public: + FTYPAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~FTYPAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); +private: + uint32 major_brand; + uint32 minor_version; + uint32 compatable_brands[32]; // Should be infinite but we will settle for max 32 + uint32 total_brands; +}; + +// Atom class for reading the wide atom +class WIDEAtom : public AtomBase { +public: + WIDEAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~WIDEAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + +}; + +// Atom class for reading the free atom +class FREEAtom : public AtomBase { +public: + FREEAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~FREEAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + +}; + +// Atom class for reading the preview atom +class PNOTAtom : public AtomBase { +public: + PNOTAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~PNOTAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + +}; + +// Atom class for reading the time to sample atom +class STTSAtom : public FullAtom { +public: + STTSAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~STTSAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + uint64 getSUMCounts() {return SUMCounts;}; + uint64 getSUMDurations() {return SUMDurations;}; + uint32 getSampleForTime(uint32 pTime); + uint32 getSampleForFrame(uint32 pFrame); +private: + array_header theHeader; + TimeToSampleArray theTimeToSampleArray; + uint64 SUMDurations; + uint64 SUMCounts; +}; + +// Atom class for reading the composition time to sample atom +class CTTSAtom : public FullAtom { +public: + CTTSAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~CTTSAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + +private: + array_header theHeader; + CompTimeToSampleArray theCompTimeToSampleArray; +}; + +// Atom class for reading the sample to chunk atom +class STSCAtom : public FullAtom { +public: + STSCAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~STSCAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + uint32 getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk); + uint32 getFirstSampleInChunk(uint32 pChunkID); + uint32 getNoSamplesInChunk(uint32 pChunkID); +private: + array_header theHeader; + SampleToChunkArray theSampleToChunkArray; +}; + +// Atom class for reading the chunk to offset atom +class STCOAtom : public FullAtom { +public: + STCOAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~STCOAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + uint64 getOffsetForChunk(uint32 pChunkID); + +protected: + // Read a single chunk offset from Stream + virtual uint64 OnGetChunkOffset(); + +private: + array_header theHeader; + ChunkToOffsetArray theChunkToOffsetArray; +}; + +// Atom class for reading the sync sample atom +class STSSAtom : public FullAtom { +public: + STSSAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~STSSAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + bool IsSyncSample(uint32 pSampleNo); +private: + array_header theHeader; + SyncSampleArray theSyncSampleArray; +}; + +// Atom class for reading the sample size atom +class STSZAtom : public FullAtom { +public: + STSZAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~STSZAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + uint32 getSizeForSample(uint32 pSampleNo); + bool IsSingleSampleSize(); +private: + uint32 SampleSize; + uint32 SampleCount; + + SampleSizeArray theSampleSizeArray; +}; + +// Atom class for reading the sample size 2 atom +class STZ2Atom : public FullAtom { +public: + STZ2Atom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~STZ2Atom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + uint32 getSizeForSample(uint32 pSampleNo); + bool IsSingleSampleSize(); +private: + uint32 SampleSize; + uint32 SampleCount; + uint8 FieldSize; + + SampleSizeArray theSampleSizeArray; +}; + +// Atom class for reading the skip atom +class SKIPAtom : public AtomBase { +public: + SKIPAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~SKIPAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + +}; + +// Atom class for reading the media data atom +class MDATAtom : public AtomBase { +public: + MDATAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~MDATAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + +}; + +// Atom class for reading the sdst atom +class STSDAtom : public FullAtom { +public: + STSDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~STSDAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + VideoDescription getAsVideo(); + AudioDescription getAsAudio(); + +private: + uint32 getMediaHandlerType(); + + void ReadSoundDescription(); + void ReadVideoDescription(); + + uint32 codecid; + + array_header theHeader; + SampleEntry theSampleEntry; + AudioDescription theAudioDescription; + VideoDescription theVideoDescription; +}; + +// Atom class for reading the track header atom +class TKHDAtom : public FullAtom { +public: + TKHDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~TKHDAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + uint32 getDuration() {return theHeader.Duration;}; // duration is in the scale of the media + + // Flags3 should contain the following + // 0x001 Track enabled + // 0x002 Track in Movie + // 0x004 Track in Preview + // 0x008 Track in Poster + + bool IsActive() {return ((getFlags3() && 0x01) || (getFlags3() && 0x0f));}; + +private: + tkhdV1 theHeader; + +}; + +// Atom class for reading the media header atom +class MDHDAtom : public FullAtom { +public: + MDHDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~MDHDAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + // return duration in ms + bigtime_t getDuration(); + uint32 getTimeScale(); + +private: + mdhdV1 theHeader; + +}; + +// Atom class for reading the video media header atom +class VMHDAtom : public FullAtom { +public: + VMHDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~VMHDAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + bool IsCopyMode() {return theHeader.GraphicsMode == 0;}; +private: + vmhd theHeader ; +}; + +// Atom class for reading the sound media header atom +class SMHDAtom : public FullAtom { +public: + SMHDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~SMHDAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); +private: + smhd theHeader; +}; + +// Atom class for reading the track atom +class TRAKAtom : public AtomContainer { +public: + TRAKAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~TRAKAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + void OnChildProcessingComplete(); + + bigtime_t Duration(uint32 TimeScale); // Return duration of track + 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); + uint32 getSampleForFrame(uint32 pFrame); + uint32 getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk); + uint64 getOffsetForChunk(uint32 pChunkID); + uint32 getFirstSampleInChunk(uint32 pChunkID); + uint32 getSizeForSample(uint32 pSample); + uint32 getNoSamplesInChunk(uint32 pChunkID); + bool IsSyncSample(uint32 pSampleNo); + bool IsSingleSampleSize(); + bool IsActive(); + + TKHDAtom *getTKHDAtom(); + MDHDAtom *getMDHDAtom(); +private: + TKHDAtom *theTKHDAtom; + MDHDAtom *theMDHDAtom; + + uint32 framecount; +}; + +// Atom class for reading the media container atom +class MDIAAtom : public AtomContainer { +public: + MDIAAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~MDIAAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + uint32 getMediaHandlerType(); +}; + +// Atom class for reading the media information atom +class MINFAtom : public AtomContainer { +public: + MINFAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~MINFAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + uint32 getMediaHandlerType(); +}; + +// Atom class for reading the stbl atom +class STBLAtom : public AtomContainer { +public: + STBLAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~STBLAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + uint32 getMediaHandlerType(); +}; + +// Atom class for reading the dinf atom +class DINFAtom : public AtomContainer { +public: + DINFAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~DINFAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); +}; + +// Atom class for reading the tmcd atom +class TMCDAtom : public AtomBase { +public: + TMCDAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~TMCDAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + +}; + +// Atom class for reading the Media Handler atom +class HDLRAtom : public FullAtom { +public: + HDLRAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize); + virtual ~HDLRAtom(); + void OnProcessMetaData(); + char *OnGetAtomName(); + + bool IsVideoHandler(); + bool IsAudioHandler(); + uint32 getMediaHandlerType(); + +private: + hdlr theHeader; + char *name; +}; + +#endif diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Structs.h b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Structs.h new file mode 100644 index 0000000000..77f697792e --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4Structs.h @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2005, David McPaul + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define AUDIO_NONE 'NONE' +#define AUDIO_RAW 'raw ' +#define AUDIO_TWOS1 'twos' +#define AUDIO_TWOS2 'sowt' +#define AUDIO_IMA4 'ima4' +#define AUDIO_MS_PCM02 0x6D730002 +#define AUDIO_INTEL_PCM17 0x6D730011 +#define AUDIO_MPEG3_CBR 0x6D730055 + +// this is all from the avi reader. we rework it for mp4 +struct mp4_main_header +{ + uint32 micro_sec_per_frame; + uint32 max_bytes_per_sec; + uint32 padding_granularity; + uint32 flags; + uint32 total_frames; + uint32 initial_frames; + uint32 streams; + uint32 suggested_buffer_size; + uint32 width; + uint32 height; +}; + +struct mp4_stream_header +{ + uint32 fourcc_type; + uint32 fourcc_handler; + uint32 flags; + uint16 priority; + uint16 language; + uint32 initial_frames; + uint32 scale; + uint32 rate; + uint32 start; + uint32 length; + uint32 suggested_buffer_size; + uint32 quality; + uint32 sample_size; + int16 rect_left; + int16 rect_top; + int16 rect_right; + int16 rect_bottom; +}; + +struct VideoMetaData +{ + uint32 compression; + uint32 size; + uint32 width; + uint32 height; + uint16 planes; + uint16 bit_count; + uint32 image_size; + uint32 HorizontalResolution; + uint32 VerticalResolution; + float FrameRate; +}; + +struct AudioMetaData +{ + uint32 compression; // compression used + 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 +}; + +struct TimeToSample { + uint32 Count; + uint32 Duration; +}; + +struct CompTimeToSample { + uint32 Count; + uint32 Offset; +}; + +struct SampleToChunk { + uint32 FirstChunk; + uint32 SamplesPerChunk; + uint32 SampleDescriptionID; + uint32 TotalPrevFrames; +}; + +// Note standard is 32bits offsets but later is 64 +// We standardise on 64 and convert older formats upwards +struct ChunkToOffset { + uint64 Offset; +}; + +struct SyncSample { + uint64 SyncSampleNo; +}; + +struct SampleSizeEntry { + uint32 EntrySize; +}; + +struct mvhdV0 { + uint32 CreationTime; + uint32 ModificationTime; + uint32 TimeScale; + uint32 Duration; + uint32 PreferredRate; // Fixed point 16.16 + uint16 PreferredVolume; // Fixed point 8.8 + uint16 Reserved1; + uint32 Reserved2[2]; + uint32 Matrix[9]; + uint32 pre_defined[6]; + uint32 NextTrackID; +}; + +struct mvhdV1 { + uint64 CreationTime; + uint64 ModificationTime; + uint32 TimeScale; + uint64 Duration; + uint32 PreferredRate; // Fixed point 16.16 + uint16 PreferredVolume; // Fixed point 8.8 + uint16 Reserved1; + uint32 Reserved2[2]; + uint32 Matrix[9]; + uint32 pre_defined[6]; + uint32 NextTrackID; +}; + +struct tkhdV0 { + uint32 CreationTime; + uint32 ModificationTime; + uint32 TrackID; + uint32 Reserved1; + uint32 Duration; + uint32 Reserved2[2]; + uint16 Layer; + uint16 AlternateGroup; + uint16 Volume; // Fixed 8.8 + uint16 Reserved3; + int32 MatrixStructure[9]; + uint32 TrackWidth; // Fixed 16.16 + uint32 TrackHeight; // Fixed 16.16 +}; + +struct tkhdV1 { + uint64 CreationTime; + uint64 ModificationTime; + uint32 TrackID; + uint32 Reserved1; + uint64 Duration; + uint32 Reserved2[2]; + uint16 Layer; + uint16 AlternateGroup; + uint16 Volume; // Fixed 8.8 + uint16 Reserved3; + int32 MatrixStructure[9]; + uint32 TrackWidth; // Fixed 16.16 + uint32 TrackHeight; // Fixed 16.16 +}; + +struct mdhdV0 { + uint32 CreationTime; + uint32 ModificationTime; + uint32 TimeScale; + uint32 Duration; + uint16 Language; // Actually should be 1 bit followed by int(5)[3] + uint16 Reserved; +}; + +struct mdhdV1 { + uint64 CreationTime; + uint64 ModificationTime; + uint32 TimeScale; + uint64 Duration; + uint16 Language; // Actually should be 1 bit followed by int(5)[3] + uint16 Reserved; +}; + +struct hdlr { + uint32 pre_defined; + uint32 handler_type; + uint32 Reserved[3]; +}; + +struct vmhd { + uint16 GraphicsMode; + uint16 OpColour[3]; +}; + +struct smhd { + uint16 Balance; + uint16 Reserved; +}; + +struct array_header { + uint32 NoEntries; +}; + +struct SampleEntry { + uint8 Reserved[6]; + uint16 DataReference; +}; + +struct AudioSampleEntry { + uint32 Reserved[2]; + uint16 ChannelCount; + uint16 SampleSize; + uint16 pre_defined; + uint16 reserved; + uint32 SampleRate; // 16.16 +}; + +struct AudioDescription { + AudioSampleEntry theAudioSampleEntry; + uint32 codecid; +}; + +struct VideoSampleEntry { + uint16 pre_defined1; + uint16 reserved1; + uint32 pre_defined2[3]; + uint16 Width; + uint16 Height; + uint32 HorizontalResolution; + uint32 VerticalResolution; + uint32 reserved2; + uint16 FrameCount; + char CompressorName[32]; + uint16 Depth; + uint16 pre_defined3; +}; + +struct VideoDescription { + VideoSampleEntry theVideoSampleEntry; + uint32 codecid; +}; diff --git a/src/add-ons/media/plugins/mp4_reader/libMP4/MP4TrakAtom.cpp b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4TrakAtom.cpp new file mode 100644 index 0000000000..6954b06629 --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/libMP4/MP4TrakAtom.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2005, David McPaul + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "MP4Parser.h" + +TRAKAtom::TRAKAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomContainer(pStream, pstreamOffset, patomType, patomSize) +{ + theTKHDAtom = NULL; + theMDHDAtom = NULL; + framecount = 0; +} + +TRAKAtom::~TRAKAtom() +{ +} + +void TRAKAtom::OnProcessMetaData() +{ +} + +char *TRAKAtom::OnGetAtomName() +{ + return "Quicktime Track Atom"; +} + +TKHDAtom *TRAKAtom::getTKHDAtom() +{ + if (theTKHDAtom) { + return theTKHDAtom; + } + + theTKHDAtom = dynamic_cast(GetChildAtom(uint32('tkhd'),0)); + + // Assert(theTKHDAtom != NULL,"Track has no Track Header"); + + return theTKHDAtom; +} + +MDHDAtom *TRAKAtom::getMDHDAtom() +{ + if (theMDHDAtom) { + return theMDHDAtom; + } + + theMDHDAtom = dynamic_cast(GetChildAtom(uint32('mdhd'),0)); + + // Assert(theMDHDAtom != NULL,"Track has no Media Header"); + + return theMDHDAtom; +} + +// Return duration of track +bigtime_t TRAKAtom::Duration(uint32 TimeScale) +{ + if (IsAudio() || IsVideo()) { + return getMDHDAtom()->getDuration(); + } + + return bigtime_t(getTKHDAtom()->getDuration() * 1000000L) / TimeScale; +} + +void TRAKAtom::OnChildProcessingComplete() +{ + STTSAtom *aSTTSAtom = dynamic_cast(GetChildAtom(uint32('stts'),0)); + + if (aSTTSAtom) { + framecount = aSTTSAtom->getSUMCounts(); + } +} + +// Is this a video track +bool TRAKAtom::IsVideo() +{ + // video tracks have a vmhd atom + return (GetChildAtom(uint32('vmhd'),0) != NULL); +} + +// Is this a audio track +bool TRAKAtom::IsAudio() +{ + // audio tracks have a smhd atom + return (GetChildAtom(uint32('smhd'),0) != NULL); +} + +uint32 TRAKAtom::getTimeForFrame(uint32 pFrame, uint32 pTimeScale) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('stts'),0); + + if (aAtomBase) { + STTSAtom *aSTTSAtom = dynamic_cast(aAtomBase); + + if (IsAudio()) { + // Frame * SampleRate = Time + } else if (IsVideo()) { + // Frame * fps = Time + return pFrame * ((aSTTSAtom->getSUMCounts() * 1000000L) / Duration(pTimeScale)); + } + } + + return 0; +} + +uint32 TRAKAtom::getSampleForTime(uint32 pTime) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('stts'),0); + + if (aAtomBase) { + return (dynamic_cast(aAtomBase))->getSampleForTime(pTime); + } + + return 0; +} + +uint32 TRAKAtom::getSampleForFrame(uint32 pFrame) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('stts'),0); + + if (aAtomBase) { + return (dynamic_cast(aAtomBase))->getSampleForFrame(pFrame); + } + + return 0; +} + +uint32 TRAKAtom::getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('stsc'),0); + + if (aAtomBase) { + return (dynamic_cast(aAtomBase))->getChunkForSample(pSample, pOffsetInChunk); + } + + return 0; +} + +uint32 TRAKAtom::getNoSamplesInChunk(uint32 pChunkID) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('stsc'),0); + + if (aAtomBase) { + return (dynamic_cast(aAtomBase))->getNoSamplesInChunk(pChunkID); + } + + return 0; +} + +uint32 TRAKAtom::getSizeForSample(uint32 pSample) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('stsz'),0); + + if (aAtomBase) { + return (dynamic_cast(aAtomBase))->getSizeForSample(pSample); + } + + return 0; +} + +uint64 TRAKAtom::getOffsetForChunk(uint32 pChunkID) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('stco'),0); + + if (aAtomBase) { + return (dynamic_cast(aAtomBase))->getOffsetForChunk(pChunkID); + } + + return 0; +} + +uint32 TRAKAtom::getFirstSampleInChunk(uint32 pChunkID) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('stsc'),0); + + if (aAtomBase) { + return (dynamic_cast(aAtomBase))->getFirstSampleInChunk(pChunkID); + } + + return 0; +} + + +bool TRAKAtom::IsSyncSample(uint32 pSampleNo) +{ + AtomBase *aAtomBase = GetChildAtom(uint32('stss'),0); + + if (aAtomBase) { + return (dynamic_cast(aAtomBase))->IsSyncSample(pSampleNo); + } + + return false; +} + +bool TRAKAtom::IsSingleSampleSize() +{ + AtomBase *aAtomBase = GetChildAtom(uint32('stsz'),0); + + if (aAtomBase) { + return (dynamic_cast(aAtomBase))->IsSingleSampleSize(); + } + + return false; +} + +bool TRAKAtom::IsActive() +{ + return getTKHDAtom()->IsActive(); +} + +// 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 new file mode 100644 index 0000000000..5ce5a0f497 --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/mp4_reader.cpp @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2005, David McPaul based on avi_reader copyright (c) 2004 Marcus Overhagen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "RawFormats.h" + +#include "mp4_reader.h" + +#define TRACE_MP4_READER +#ifdef TRACE_MP4_READER + #define TRACE printf +#else + #define TRACE(a...) +#endif + +#define ERROR(a...) fprintf(stderr, a) + +struct mp4_cookie +{ + unsigned stream; + char * buffer; + unsigned buffer_size; + + int64 frame_count; + bigtime_t duration; + media_format format; + + bool audio; + + // audio only: + off_t byte_pos; + uint32 chunk_pos; + uint32 bytes_per_sec_rate; + uint32 bytes_per_sec_scale; + + // video only: + uint32 line_count; + + // Common + uint32 frame_pos; + uint32 frames_per_sec_rate; + uint32 frames_per_sec_scale; +}; + + +mp4Reader::mp4Reader() + : theFileReader(0) +{ + TRACE("mp4Reader::mp4Reader\n"); +} + +mp4Reader::~mp4Reader() +{ + delete theFileReader; +} + +const char * +mp4Reader::Copyright() +{ + return "MPEG4 & libMP4, " B_UTF8_COPYRIGHT " by David McPaul"; +} + +status_t +mp4Reader::Sniff(int32 *streamCount) +{ + TRACE("mp4Reader::Sniff\n"); + + BPositionIO *pos_io_source; + + pos_io_source = dynamic_cast(Reader::Source()); + if (!pos_io_source) { + TRACE("mp4Reader::Sniff: not a BPositionIO\n"); + return B_ERROR; + } + + if (!MP4FileReader::IsSupported(pos_io_source)) { + TRACE("mp4Reader::Sniff: unsupported file type\n"); + return B_ERROR; + } + + TRACE("mp4Reader::Sniff: this stream seems to be supported\n"); + + theFileReader = new MP4FileReader(pos_io_source); + if (B_OK != theFileReader->ParseFile()) { + ERROR("mp4Reader::Sniff: error parsing file\n"); + return B_ERROR; + } + + *streamCount = theFileReader->getStreamCount(); + return B_OK; +} + +void +mp4Reader::GetFileFormatInfo(media_file_format *mff) +{ + mff->capabilities = media_file_format::B_READABLE + | media_file_format::B_KNOWS_ENCODED_VIDEO + | media_file_format::B_KNOWS_ENCODED_AUDIO + | media_file_format::B_IMPERFECTLY_SEEKABLE; + mff->family = B_QUICKTIME_FORMAT_FAMILY; + mff->version = 100; + strcpy(mff->mime_type, "video/quicktime"); + strcpy(mff->file_extension, "mp4"); + strcpy(mff->short_name, "MP4"); + strcpy(mff->pretty_name, "MPEG-4 (MP4) file format"); +} + +status_t +mp4Reader::AllocateCookie(int32 streamNumber, void **_cookie) +{ + mp4_cookie *cookie = new mp4_cookie; + *_cookie = cookie; + + cookie->stream = streamNumber; + cookie->buffer = 0; + cookie->buffer_size = 0; + cookie->frame_pos = 0; + + BMediaFormats formats; + media_format *format = &cookie->format; + media_format_description description; + + if (theFileReader->IsActive(cookie->stream) == false) { + ERROR("mp4Reader::AllocateCookie: stream %d is not active\n", cookie->stream); + delete cookie; + return B_ERROR; + } + + const mp4_stream_header *stream_header; + stream_header = theFileReader->StreamFormat(cookie->stream); + if (!stream_header) { + ERROR("mp4Reader::AllocateCookie: stream %d has no header\n", cookie->stream); + delete cookie; + return B_ERROR; + } + + TRACE("mp4Reader::AllocateCookie: stream %ld (%s)\n", streamNumber, theFileReader->IsAudio(cookie->stream) ? "audio" : theFileReader->IsVideo(cookie->stream) ? "video" : "unknown"); + + if (theFileReader->IsAudio(cookie->stream)) { + const AudioMetaData *audio_format = theFileReader->AudioFormat(cookie->stream); + if (!audio_format) { + ERROR("mp4Reader::AllocateCookie: audio stream %d has no format\n", cookie->stream); + delete cookie; + return B_ERROR; + } + + cookie->frame_count = theFileReader->getAudioFrameCount(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 * audio_format->SampleSize * audio_format->NoOfChannels / 8; + cookie->bytes_per_sec_scale = stream_header->scale; + cookie->frames_per_sec_rate = stream_header->rate; + 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); + } else if (stream_header->rate) { + cookie->bytes_per_sec_rate = stream_header->rate * audio_format->SampleSize * audio_format->NoOfChannels / 8; + cookie->bytes_per_sec_scale = 1; + cookie->frames_per_sec_rate = stream_header->rate; + 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); + } else { + cookie->bytes_per_sec_rate = 128000; + cookie->bytes_per_sec_scale = 8; + cookie->frames_per_sec_rate = 16000; + cookie->frames_per_sec_scale = 1; + TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using fallback)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale); + } + + if ((audio_format->compression == AUDIO_NONE) || + (audio_format->compression == AUDIO_RAW) || + (audio_format->compression == AUDIO_TWOS1) || + (audio_format->compression == AUDIO_TWOS2)) { + 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 = cookie->frames_per_sec_rate / cookie->frames_per_sec_scale; + format->u.raw_audio.channel_count = audio_format->NoOfChannels; + + format->u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN; + + if (audio_format->SampleSize <= 8) + format->u.raw_audio.format = B_AUDIO_FORMAT_UINT8; + else if (audio_format->SampleSize <= 16) + format->u.raw_audio.format = B_AUDIO_FORMAT_INT16; + else if (audio_format->SampleSize <= 24) + format->u.raw_audio.format = B_AUDIO_FORMAT_INT24; + else if (audio_format->SampleSize <= 32) + format->u.raw_audio.format = B_AUDIO_FORMAT_INT32; + else { + ERROR("mp4Reader::AllocateCookie: unhandled bits per sample %d\n", audio_format->SampleSize); + return B_ERROR; + } + + if (audio_format->compression == AUDIO_TWOS1) { + if (audio_format->SampleSize <= 8) { + format->u.raw_audio.format = B_AUDIO_FORMAT_INT8; + } else if (audio_format->SampleSize <= 16) { + format->u.raw_audio.format = B_AUDIO_FORMAT_INT16; + format->u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN; + } + } + if (audio_format->compression == AUDIO_TWOS2) { + if (audio_format->SampleSize <= 8) { + format->u.raw_audio.format = B_AUDIO_FORMAT_INT8; + } else if (audio_format->SampleSize <= 16) { + format->u.raw_audio.format = B_AUDIO_FORMAT_INT16; + format->u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN; + } + } + + format->u.raw_audio.buffer_size = stream_header->suggested_buffer_size; + } else { + description.family = B_QUICKTIME_FORMAT_FAMILY; + description.u.quicktime.codec = audio_format->compression; + if (B_OK != formats.GetFormatFor(description, format)) { + format->type = B_MEDIA_ENCODED_AUDIO; + } + + switch (audio_format->compression) { + case AUDIO_MS_PCM02: + TRACE("MS PCM02\n"); + format->u.raw_audio.format |= B_AUDIO_FORMAT_CHANNEL_ORDER_WAVE; + 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; + format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels; + break; + case AUDIO_INTEL_PCM17: + TRACE("INTEL PCM\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; + format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels; + break; + case AUDIO_MPEG3_CBR: + TRACE("MP3\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; + 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; + format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels; + break; + default: + char cc1,cc2,cc3,cc4; + + cc1 = (char)((audio_format->compression >> 24) & 0xff); + cc2 = (char)((audio_format->compression >> 16) & 0xff); + cc3 = (char)((audio_format->compression >> 8) & 0xff); + cc4 = (char)((audio_format->compression >> 0) & 0xff); + + TRACE("compression %c%c%c%c\n", cc1,cc2,cc3,cc4); + 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; + format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels; + break; + } + } + +/* 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; + + // put the Audio struct, including extra data, into the format meta data. + size_t size; + const void *data = theFileReader->AudioFormat(cookie->stream, &size); + format->SetMetaData(data, size); + +#ifdef TRACE_MP4_READER + uint8 *p = 18 + (uint8 *)data; + TRACE("extra_data: %ld: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + size - 18, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]); +#endif + + return B_OK; + } + + if (theFileReader->IsVideo(cookie->stream)) { + const VideoMetaData *video_format = theFileReader->VideoFormat(cookie->stream); + if (!video_format) { + ERROR("mp4Reader::AllocateCookie: video stream %d has no format\n", cookie->stream); + delete cookie; + return B_ERROR; + } + + cookie->audio = false; + cookie->line_count = theFileReader->MovMainHeader()->height; + + if (stream_header->scale && stream_header->rate) { + cookie->frames_per_sec_rate = stream_header->rate; + cookie->frames_per_sec_scale = stream_header->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 (theFileReader->MovMainHeader()->micro_sec_per_frame) { + cookie->frames_per_sec_rate = 1000000; + cookie->frames_per_sec_scale = theFileReader->MovMainHeader()->micro_sec_per_frame; + TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using micro_sec_per_frame)\n", cookie->frames_per_sec_rate, cookie->frames_per_sec_scale); + } else { + cookie->frames_per_sec_rate = 25; + cookie->frames_per_sec_scale = 1; + TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using fallback)\n", cookie->frames_per_sec_rate, cookie->frames_per_sec_scale); + } + + cookie->frame_count = stream_header->length; + cookie->duration = (cookie->frame_count * (int64)cookie->frames_per_sec_scale * 1000000LL) / cookie->frames_per_sec_rate; + + TRACE("frame_count %Ld\n", cookie->frame_count); + TRACE("duration %.6f (%Ld)\n", cookie->duration / 1E6, cookie->duration); + + char cc1,cc2,cc3,cc4; + + cc1 = (char)((video_format->compression >> 24) & 0xff); + cc2 = (char)((video_format->compression >> 16) & 0xff); + cc3 = (char)((video_format->compression >> 8) & 0xff); + cc4 = (char)((video_format->compression >> 0) & 0xff); + + TRACE("compression %c%c%c%c\n", cc1,cc2,cc3,cc4); + + description.family = B_QUICKTIME_FORMAT_FAMILY; + if (stream_header->fourcc_handler == 'ekaf' || stream_header->fourcc_handler == 0) // 'fake' or 0 fourcc => used compression id + description.u.quicktime.codec = video_format->compression; + else + description.u.quicktime.codec = video_format->compression; + if (B_OK != formats.GetFormatFor(description, format)) + format->type = B_MEDIA_ENCODED_VIDEO; + + format->user_data_type = B_CODEC_TYPE_INFO; + *(uint32 *)format->user_data = description.u.quicktime.codec; format->user_data[4] = 0; + format->u.encoded_video.max_bit_rate = 8 * theFileReader->MovMainHeader()->max_bytes_per_sec; + format->u.encoded_video.avg_bit_rate = format->u.encoded_video.max_bit_rate / 2; // XXX fix this + format->u.encoded_video.output.field_rate = cookie->frames_per_sec_rate / (float)cookie->frames_per_sec_scale; + format->u.encoded_video.output.interlace = 1; // 1: progressive + format->u.encoded_video.output.first_active = 0; + format->u.encoded_video.output.last_active = cookie->line_count - 1; + format->u.encoded_video.output.orientation = B_VIDEO_TOP_LEFT_RIGHT; + format->u.encoded_video.output.pixel_width_aspect = 1; + format->u.encoded_video.output.pixel_height_aspect = 1; + // format->u.encoded_video.output.display.format = 0; + format->u.encoded_video.output.display.line_width = theFileReader->MovMainHeader()->width; + format->u.encoded_video.output.display.line_count = cookie->line_count; + format->u.encoded_video.output.display.bytes_per_row = 0; // format->u.encoded_video.output.display.line_width * 4; + format->u.encoded_video.output.display.pixel_offset = 0; + format->u.encoded_video.output.display.line_offset = 0; + format->u.encoded_video.output.display.flags = 0; + + 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); + + return B_OK; + } + + delete cookie; + return B_ERROR; +} + + +status_t +mp4Reader::FreeCookie(void *_cookie) +{ + mp4_cookie *cookie = (mp4_cookie *)_cookie; + + delete [] cookie->buffer; + + delete cookie; + return B_OK; +} + + +status_t +mp4Reader::GetStreamInfo(void *_cookie, int64 *frameCount, bigtime_t *duration, + media_format *format, const void **infoBuffer, size_t *infoSize) +{ + mp4_cookie *cookie = (mp4_cookie *)_cookie; + + *frameCount = cookie->frame_count; + *duration = cookie->duration; + *format = cookie->format; + *infoBuffer = 0; + *infoSize = 0; + return B_OK; +} + + +status_t +mp4Reader::Seek(void *cookie, + uint32 seekTo, + int64 *frame, bigtime_t *time) +{ + +// We should seek to nearest keyframe requested +// currently returning B_OK for audio streams causes many problems. + + mp4_cookie *mp4cookie = (mp4_cookie *)cookie; + + if (seekTo & B_MEDIA_SEEK_TO_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; + } + + 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; + } + + TRACE("mp4Reader::Seek: seekTo%s%s%s%s, time %Ld, frame %Ld\n", + (seekTo & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "", + (seekTo & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "", + (seekTo & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "", + (seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", + *time, *frame); + + return B_ERROR; +} + + +status_t +mp4Reader::GetNextChunk(void *_cookie, + const void **chunkBuffer, size_t *chunkSize, + media_header *mediaHeader) +{ + mp4_cookie *cookie = (mp4_cookie *)_cookie; + + int64 start; 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 (cookie->buffer_size < size) { + delete [] cookie->buffer; + cookie->buffer_size = (size + 15) & ~15; + cookie->buffer = new char [cookie->buffer_size]; + } + + 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); + 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; + 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; + + cookie->frame_pos++; + } + + TRACE("stream %d: start_time %.6f\n", cookie->stream, mediaHeader->start_time / 1000000.0); + + *chunkBuffer = cookie->buffer; + *chunkSize = size; + return (int)size == theFileReader->Source()->ReadAt(start, cookie->buffer, size) ? B_OK : B_LAST_BUFFER_ERROR; +} + + +Reader * +mp4ReaderPlugin::NewReader() +{ + return new mp4Reader; +} + + +MediaPlugin *instantiate_plugin() +{ + return new mp4ReaderPlugin; +} diff --git a/src/add-ons/media/plugins/mp4_reader/mp4_reader.h b/src/add-ons/media/plugins/mp4_reader/mp4_reader.h new file mode 100644 index 0000000000..85ce7a0b9a --- /dev/null +++ b/src/add-ons/media/plugins/mp4_reader/mp4_reader.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2005, David McPaul based on avi_reader copyright (c) 2004 Marcus Overhagen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _MP4_READER_H +#define _MP4_READER_H + +#include "ReaderPlugin.h" +#include "libMP4/MP4FileReader.h" + +class mp4Reader : public Reader +{ +public: + mp4Reader(); + ~mp4Reader(); + + const char *Copyright(); + + status_t Sniff(int32 *streamCount); + + void GetFileFormatInfo(media_file_format *mff); + + status_t AllocateCookie(int32 streamNumber, void **cookie); + status_t FreeCookie(void *cookie); + + status_t GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, + media_format *format, const void **infoBuffer, size_t *infoSize); + + status_t Seek(void *cookie, + uint32 seekTo, + int64 *frame, bigtime_t *time); + + status_t GetNextChunk(void *cookie, + const void **chunkBuffer, size_t *chunkSize, + media_header *mediaHeader); +private: + MP4FileReader *theFileReader; +}; + + +class mp4ReaderPlugin : public ReaderPlugin +{ +public: + Reader *NewReader(); +}; + +MediaPlugin *instantiate_plugin(); + +#endif