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 vorbis ;
|
||||||
SubInclude OBOS_TOP src add-ons media plugins speex ;
|
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 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
|
# The following add-ons are GPL licensed, and can only be used with
|
||||||
# software whose license is GPL compatible. To include these GPL
|
# 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_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, 0x6D730050, "MP Layer2"},
|
||||||
{CODEC_ID_MP2, B_MEDIA_ENCODED_AUDIO, B_QUICKTIME_FORMAT_FAMILY, 0x6D730055, "MP Layer3"},
|
{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
|
#ifdef HAS_MACE_AUDIO
|
||||||
{CODEC_ID_MACE3, B_MEDIA_ENCODED_AUDIO, B_QUICKTIME_FORMAT_FAMILY, 'MAC3', "MACE 3:1"},
|
{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_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_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, 'cvid', "Cinepak Video"},
|
||||||
{CODEC_ID_CINEPAK, B_MEDIA_ENCODED_VIDEO, B_QUICKTIME_FORMAT_FAMILY, FOURCC('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('RLE '), "MS RLE"}, // ???
|
||||||
{CODEC_ID_MSRLE, B_MEDIA_ENCODED_VIDEO, B_AVI_FORMAT_FAMILY, FOURCC('mrle'), "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);
|
return new STSZAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aAtomType == uint32('ftyp')) {
|
|
||||||
return new FTYPAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aAtomType == uint32('cmov')) {
|
if (aAtomType == uint32('cmov')) {
|
||||||
return new CMOVAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize);
|
return new CMOVAtom(pStream, aStreamOffset, aAtomType, aRealAtomSize);
|
||||||
}
|
}
|
||||||
@@ -979,23 +975,6 @@ char *WIDEAtom::OnGetAtomName()
|
|||||||
return "WIDE Atom";
|
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)
|
FREEAtom::FREEAtom(BPositionIO *pStream, off_t pstreamOffset, uint32 patomType, uint64 patomSize) : AtomBase(pStream, pstreamOffset, patomType, patomSize)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,16 +120,6 @@ private:
|
|||||||
uint8 *Buffer;
|
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
|
// Atom class for reading the wide atom
|
||||||
class WIDEAtom : public AtomBase {
|
class WIDEAtom : public AtomBase {
|
||||||
public:
|
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