pre-alpha mp4 reader

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13805 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
David McPaul
2005-07-23 01:51:55 +00:00
parent 5fcc5dfec9
commit 0ddd1fcb84
18 changed files with 4211 additions and 33 deletions
+1
View File
@@ -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
@@ -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"}, // ???
@@ -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)
@@ -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)
{
}
@@ -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:
@@ -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 ;
@@ -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 :-)
@@ -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
;
@@ -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 <stdio.h>
#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;i<pindent;i++) {
printf("-");
}
}
bool AtomBase::IsKnown()
{
return (OnGetAtomName() != NULL);
}
void AtomBase::ReadArrayHeader(array_header *pHeader)
{
getStream()->Read(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;i<TotalChildren;i++) {
if (atomChildren[i]->IsType(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<AtomContainer *>(atomChildren[i])->GetChildAtom(patomType, offset));
if (aAtomBase) {
// found in container
return aAtomBase;
}
// not found
}
}
}
return NULL;
}
void AtomContainer::OnProcessMetaData()
{
}
@@ -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 <File.h>
#include <MediaDefs.h>
#include <MediaFormats.h>
#include <SupportDefs.h>
// Std Headers
#include <map.h>
#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<uint32, AtomBasePtr, std::less<uint32> > 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
@@ -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 <iostream.h>
#include <DataIO.h>
#include <SupportKit.h>
#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;i<TotalChildren;i++) {
if (atomChildren[i]->IsType(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<AtomContainer *>(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<MVHDAtom *>(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<TRAKAtom *>(aAtomBase)->IsVideo())) {
return (dynamic_cast<TRAKAtom *>(aAtomBase)->Duration(1));
}
return 0;
}
bigtime_t MP4FileReader::getAudioDuration(uint32 stream_index)
{
AtomBase *aAtomBase;
aAtomBase = GetChildAtom(uint32('trak'),stream_index);
if ((aAtomBase) && (dynamic_cast<TRAKAtom *>(aAtomBase)->IsAudio())) {
return (dynamic_cast<TRAKAtom *>(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<getStreamCount();i++) {
aAtomBase = GetChildAtom(uint32('trak'),i);
if ((aAtomBase) && (dynamic_cast<TRAKAtom *>(aAtomBase)->IsActive())) {
if (dynamic_cast<TRAKAtom *>(aAtomBase)->IsAudio()) {
audio_index = int32(i);
}
if (dynamic_cast<TRAKAtom *>(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<TRAKAtom *>(aAtomBase)->IsVideo())) {
return dynamic_cast<TRAKAtom *>(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<TRAKAtom *>(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<TRAKAtom *>(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<TRAKAtom *>(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<TRAKAtom *>(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<TRAKAtom *>(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;i<TotalChildren;i++) {
atomChildren[i]->DisplayAtoms();
}
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<TRAKAtom *>(aAtomBase);
aAtomBase = aTrakAtom->GetChildAtom(uint32('stsd'),0);
if (aAtomBase) {
STSDAtom *aSTSDAtom = dynamic_cast<STSDAtom *>(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<TRAKAtom *>(aAtomBase);
aAtomBase = aTrakAtom->GetChildAtom(uint32('stsd'),0);
if (aAtomBase) {
STSDAtom *aSTSDAtom = dynamic_cast<STSDAtom *>(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<STTSAtom *>(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<TRAKAtom *>(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;i<aTrakAtom->getNoSamplesInChunk(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<TRAKAtom *>(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<TRAKAtom *>(aAtomBase);
return aTrakAtom->IsActive();
}
return false;
}
/* static */
bool MP4FileReader::IsSupported(BPositionIO *source)
{
AtomBase *aAtom;
aAtom = getAtom(source);
if (aAtom) {
return (aAtom->IsKnown());
}
return false;
}
@@ -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 <File.h>
#include <MediaDefs.h>
#include <MediaFormats.h>
#include <SupportDefs.h>
#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
File diff suppressed because it is too large Load Diff
@@ -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 <File.h>
#include <MediaDefs.h>
#include <MediaFormats.h>
#include <SupportDefs.h>
// Std Headers
#include <map.h>
#include "MP4Atom.h"
typedef CompTimeToSample* CompTimeToSamplePtr;
typedef std::map<uint32, CompTimeToSamplePtr, std::less<uint32> > CompTimeToSampleArray;
typedef TimeToSample* TimeToSamplePtr;
typedef std::map<uint32, TimeToSamplePtr, std::less<uint32> > TimeToSampleArray;
typedef SampleToChunk* SampleToChunkPtr;
typedef std::map<uint32, SampleToChunkPtr, std::less<uint32> > SampleToChunkArray;
typedef ChunkToOffset* ChunkToOffsetPtr;
typedef std::map<uint32, ChunkToOffsetPtr, std::less<uint32> > ChunkToOffsetArray;
typedef SyncSample* SyncSamplePtr;
typedef std::map<uint32, SyncSamplePtr, std::less<uint32> > SyncSampleArray;
typedef SampleSizeEntry* SampleSizePtr;
typedef std::map<uint32, SampleSizePtr, std::less<uint32> > 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
@@ -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;
};
@@ -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<TKHDAtom *>(GetChildAtom(uint32('tkhd'),0));
// Assert(theTKHDAtom != NULL,"Track has no Track Header");
return theTKHDAtom;
}
MDHDAtom *TRAKAtom::getMDHDAtom()
{
if (theMDHDAtom) {
return theMDHDAtom;
}
theMDHDAtom = dynamic_cast<MDHDAtom *>(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<STTSAtom *>(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<STTSAtom *>(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<STTSAtom *>(aAtomBase))->getSampleForTime(pTime);
}
return 0;
}
uint32 TRAKAtom::getSampleForFrame(uint32 pFrame)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stts'),0);
if (aAtomBase) {
return (dynamic_cast<STTSAtom *>(aAtomBase))->getSampleForFrame(pFrame);
}
return 0;
}
uint32 TRAKAtom::getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stsc'),0);
if (aAtomBase) {
return (dynamic_cast<STSCAtom *>(aAtomBase))->getChunkForSample(pSample, pOffsetInChunk);
}
return 0;
}
uint32 TRAKAtom::getNoSamplesInChunk(uint32 pChunkID)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stsc'),0);
if (aAtomBase) {
return (dynamic_cast<STSCAtom *>(aAtomBase))->getNoSamplesInChunk(pChunkID);
}
return 0;
}
uint32 TRAKAtom::getSizeForSample(uint32 pSample)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stsz'),0);
if (aAtomBase) {
return (dynamic_cast<STSZAtom *>(aAtomBase))->getSizeForSample(pSample);
}
return 0;
}
uint64 TRAKAtom::getOffsetForChunk(uint32 pChunkID)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stco'),0);
if (aAtomBase) {
return (dynamic_cast<STCOAtom *>(aAtomBase))->getOffsetForChunk(pChunkID);
}
return 0;
}
uint32 TRAKAtom::getFirstSampleInChunk(uint32 pChunkID)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stsc'),0);
if (aAtomBase) {
return (dynamic_cast<STSCAtom *>(aAtomBase))->getFirstSampleInChunk(pChunkID);
}
return 0;
}
bool TRAKAtom::IsSyncSample(uint32 pSampleNo)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stss'),0);
if (aAtomBase) {
return (dynamic_cast<STSSAtom *>(aAtomBase))->IsSyncSample(pSampleNo);
}
return false;
}
bool TRAKAtom::IsSingleSampleSize()
{
AtomBase *aAtomBase = GetChildAtom(uint32('stsz'),0);
if (aAtomBase) {
return (dynamic_cast<STSZAtom *>(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
@@ -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 <stdio.h>
#include <string.h>
#include <malloc.h>
#include <DataIO.h>
#include <StopWatch.h>
#include <ByteOrder.h>
#include <InterfaceDefs.h>
#include <MediaFormats.h>
#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<BPositionIO *>(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;
}
@@ -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