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:
@@ -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
|
||||
Reference in New Issue
Block a user