mp4file.cpp
来自「faac-1.25.rar音频编解码器demo」· C++ 代码 · 共 2,476 行 · 第 1/5 页
CPP
2,476 行
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is MPEG4IP.
*
* The Initial Developer of the Original Code is Cisco Systems Inc.
* Portions created by Cisco Systems Inc. are
* Copyright (C) Cisco Systems Inc. 2001 - 2004. All Rights Reserved.
*
* 3GPP features implementation is based on 3GPP's TS26.234-v5.60,
* and was contributed by Ximpo Group Ltd.
*
* Portions created by Ximpo Group Ltd. are
* Copyright (C) Ximpo Group Ltd. 2003, 2004. All Rights Reserved.
*
* Contributor(s):
* Dave Mackie dmackie@cisco.com
* Alix Marchandise-Franquet alix@cisco.com
* Ximpo Group Ltd. mp4v2@ximpo.com
*/
#include "mp4common.h"
MP4File::MP4File(u_int32_t verbosity)
{
m_fileName = NULL;
m_pFile = NULL;
m_orgFileSize = 0;
m_fileSize = 0;
m_pRootAtom = NULL;
m_odTrackId = MP4_INVALID_TRACK_ID;
m_verbosity = verbosity;
m_mode = 0;
m_createFlags = 0;
m_useIsma = false;
m_pModificationProperty = NULL;
m_pTimeScaleProperty = NULL;
m_pDurationProperty = NULL;
m_memoryBuffer = NULL;
m_memoryBufferSize = 0;
m_memoryBufferPosition = 0;
m_numReadBits = 0;
m_bufReadBits = 0;
m_numWriteBits = 0;
m_bufWriteBits = 0;
}
MP4File::~MP4File()
{
MP4Free(m_fileName);
delete m_pRootAtom;
for (u_int32_t i = 0; i < m_pTracks.Size(); i++) {
delete m_pTracks[i];
}
MP4Free(m_memoryBuffer); // just in case
}
void MP4File::Read(const char* fileName)
{
m_fileName = MP4Stralloc(fileName);
m_mode = 'r';
Open("rb");
ReadFromFile();
CacheProperties();
}
void MP4File::Create(const char* fileName, u_int32_t flags)
{
CreateEx( fileName, flags, NULL, 0, NULL, 0 );
}
void MP4File::CreateEx(const char* fileName, u_int32_t flags,
char* majorBrand, u_int32_t minorVersion,
char** supportedBrands, u_int32_t supportedBrandsCount)
{
m_fileName = MP4Stralloc(fileName);
m_mode = 'w';
m_createFlags = flags;
Open("wb+");
// generate a skeletal atom tree
m_pRootAtom = MP4Atom::CreateAtom(NULL);
m_pRootAtom->SetFile(this);
m_pRootAtom->Generate();
if ((majorBrand != NULL) || (minorVersion != 0)) {
MakeFtypAtom(majorBrand, minorVersion,
supportedBrands, supportedBrandsCount);
}
CacheProperties();
// create mdat, and insert it after ftyp, and before moov
InsertChildAtom(m_pRootAtom, "mdat", 1);
// start writing
m_pRootAtom->BeginWrite();
}
bool MP4File::Use64Bits (const char *atomName)
{
if (!strcmp(atomName, "mdat") || !strcmp(atomName, "stbl")) {
return (m_createFlags & MP4_CREATE_64BIT_DATA) == MP4_CREATE_64BIT_DATA;
}
if (!strcmp(atomName, "mvhd") ||
!strcmp(atomName, "tkhd") ||
!strcmp(atomName, "mdhd")) {
return (m_createFlags & MP4_CREATE_64BIT_TIME) == MP4_CREATE_64BIT_TIME;
}
return false;
}
void MP4File::Modify(const char* fileName)
{
m_fileName = MP4Stralloc(fileName);
m_mode = 'r';
Open("rb+");
ReadFromFile();
m_mode = 'w';
// find the moov atom
MP4Atom* pMoovAtom = m_pRootAtom->FindAtom("moov");
u_int32_t numAtoms;
if (pMoovAtom == NULL) {
// there isn't one, odd but we can still proceed
pMoovAtom = AddChildAtom(m_pRootAtom, "moov");
} else {
numAtoms = m_pRootAtom->GetNumberOfChildAtoms();
// work backwards thru the top level atoms
int32_t i;
bool lastAtomIsMoov = true;
MP4Atom* pLastAtom = NULL;
for (i = numAtoms - 1; i >= 0; i--) {
MP4Atom* pAtom = m_pRootAtom->GetChildAtom(i);
const char* type = pAtom->GetType();
// get rid of any trailing free or skips
if (!strcmp(type, "free") || !strcmp(type, "skip")) {
m_pRootAtom->DeleteChildAtom(pAtom);
continue;
}
if (strcmp(type, "moov")) {
if (pLastAtom == NULL) {
pLastAtom = pAtom;
lastAtomIsMoov = false;
}
continue;
}
// now at moov atom
// multiple moov atoms?!?
if (pAtom != pMoovAtom) {
throw new MP4Error(
"Badly formed mp4 file, multiple moov atoms",
"MP4Modify");
}
if (lastAtomIsMoov) {
// position to start of moov atom,
// effectively truncating file
// prior to adding new mdat
SetPosition(pMoovAtom->GetStart());
} else { // last atom isn't moov
// need to place a free atom
MP4Atom* pFreeAtom = MP4Atom::CreateAtom("free");
// in existing position of the moov atom
m_pRootAtom->InsertChildAtom(pFreeAtom, i);
m_pRootAtom->DeleteChildAtom(pMoovAtom);
m_pRootAtom->AddChildAtom(pMoovAtom);
// write free atom to disk
SetPosition(pMoovAtom->GetStart());
pFreeAtom->SetSize(pMoovAtom->GetSize());
pFreeAtom->Write();
// finally set our file position to the end of the last atom
SetPosition(pLastAtom->GetEnd());
}
break;
}
ASSERT(i != -1);
}
CacheProperties(); // of moov atom
numAtoms = m_pRootAtom->GetNumberOfChildAtoms();
// insert another mdat prior to moov atom (the last atom)
MP4Atom* pMdatAtom = InsertChildAtom(m_pRootAtom, "mdat", numAtoms - 1);
// start writing new mdat
pMdatAtom->BeginWrite();
}
void MP4File::Optimize(const char* orgFileName, const char* newFileName)
{
m_fileName = MP4Stralloc(orgFileName);
m_mode = 'r';
// first load meta-info into memory
Open("rb");
ReadFromFile();
CacheProperties(); // of moov atom
// now switch over to writing the new file
MP4Free(m_fileName);
// create a temporary file if necessary
if (newFileName == NULL) {
m_fileName = MP4Stralloc(TempFileName());
} else {
m_fileName = MP4Stralloc(newFileName);
}
FILE* pReadFile = m_pFile;
m_pFile = NULL;
m_mode = 'w';
Open("wb");
SetIntegerProperty("moov.mvhd.modificationTime",
MP4GetAbsTimestamp());
// writing meta info in the optimal order
((MP4RootAtom*)m_pRootAtom)->BeginOptimalWrite();
// write data in optimal order
RewriteMdat(pReadFile, m_pFile);
// finish writing
((MP4RootAtom*)m_pRootAtom)->FinishOptimalWrite();
// cleanup
fclose(m_pFile);
m_pFile = NULL;
fclose(pReadFile);
// move temporary file into place
if (newFileName == NULL) {
Rename(m_fileName, orgFileName);
}
}
void MP4File::RewriteMdat(FILE* pReadFile, FILE* pWriteFile)
{
u_int32_t numTracks = m_pTracks.Size();
MP4ChunkId* chunkIds = new MP4ChunkId[numTracks];
MP4ChunkId* maxChunkIds = new MP4ChunkId[numTracks];
MP4Timestamp* nextChunkTimes = new MP4Timestamp[numTracks];
for (u_int32_t i = 0; i < numTracks; i++) {
chunkIds[i] = 1;
maxChunkIds[i] = m_pTracks[i]->GetNumberOfChunks();
nextChunkTimes[i] = MP4_INVALID_TIMESTAMP;
}
while (true) {
u_int32_t nextTrackIndex = (u_int32_t)-1;
MP4Timestamp nextTime = MP4_INVALID_TIMESTAMP;
for (u_int32_t i = 0; i < numTracks; i++) {
if (chunkIds[i] > maxChunkIds[i]) {
continue;
}
if (nextChunkTimes[i] == MP4_INVALID_TIMESTAMP) {
MP4Timestamp chunkTime =
m_pTracks[i]->GetChunkTime(chunkIds[i]);
nextChunkTimes[i] = MP4ConvertTime(chunkTime,
m_pTracks[i]->GetTimeScale(), GetTimeScale());
}
// time is not earliest so far
if (nextChunkTimes[i] > nextTime) {
continue;
}
// prefer hint tracks to media tracks if times are equal
if (nextChunkTimes[i] == nextTime
&& strcmp(m_pTracks[i]->GetType(), MP4_HINT_TRACK_TYPE)) {
continue;
}
// this is our current choice of tracks
nextTime = nextChunkTimes[i];
nextTrackIndex = i;
}
if (nextTrackIndex == (u_int32_t)-1) {
break;
}
// point into original mp4 file for read chunk call
m_pFile = pReadFile;
m_mode = 'r';
u_int8_t* pChunk;
u_int32_t chunkSize;
m_pTracks[nextTrackIndex]->
ReadChunk(chunkIds[nextTrackIndex], &pChunk, &chunkSize);
// point back at the new mp4 file for write chunk
m_pFile = pWriteFile;
m_mode = 'w';
m_pTracks[nextTrackIndex]->
RewriteChunk(chunkIds[nextTrackIndex], pChunk, chunkSize);
MP4Free(pChunk);
chunkIds[nextTrackIndex]++;
nextChunkTimes[nextTrackIndex] = MP4_INVALID_TIMESTAMP;
}
delete [] chunkIds;
delete [] maxChunkIds;
delete [] nextChunkTimes;
}
void MP4File::Open(const char* fmode)
{
ASSERT(m_pFile == NULL);
#ifdef O_LARGEFILE
// UGH! fopen doesn't open a file in 64-bit mode, period.
// So we need to use open() and then fdopen()
int fd;
int flags = O_LARGEFILE;
if (strchr(fmode, '+')) {
flags |= O_CREAT | O_RDWR;
if (fmode[0] == 'w') {
flags |= O_TRUNC;
}
} else {
if (fmode[0] == 'w') {
flags |= O_CREAT | O_TRUNC | O_WRONLY;
} else {
flags |= O_RDONLY;
}
}
fd = open(m_fileName, flags, 0666);
if (fd >= 0) {
m_pFile = fdopen(fd, fmode);
}
#else
m_pFile = fopen(m_fileName, fmode);
#endif
if (m_pFile == NULL) {
throw new MP4Error(errno, "failed", "MP4Open");
}
if (m_mode == 'r') {
struct stat s;
if (fstat(fileno(m_pFile), &s) < 0) {
throw new MP4Error(errno, "stat failed", "MP4Open");
}
m_orgFileSize = m_fileSize = s.st_size;
} else {
m_orgFileSize = m_fileSize = 0;
}
}
void MP4File::ReadFromFile()
{
// ensure we start at beginning of file
SetPosition(0);
// create a new root atom
ASSERT(m_pRootAtom == NULL);
m_pRootAtom = MP4Atom::CreateAtom(NULL);
u_int64_t fileSize = GetSize();
m_pRootAtom->SetFile(this);
m_pRootAtom->SetStart(0);
m_pRootAtom->SetSize(fileSize);
m_pRootAtom->SetEnd(fileSize);
m_pRootAtom->Read();
// create MP4Track's for any tracks in the file
GenerateTracks();
}
void MP4File::GenerateTracks()
{
u_int32_t trackIndex = 0;
while (true) {
char trackName[32];
snprintf(trackName, sizeof(trackName), "moov.trak[%u]", trackIndex);
// find next trak atom
MP4Atom* pTrakAtom = m_pRootAtom->FindAtom(trackName);
// done, no more trak atoms
if (pTrakAtom == NULL) {
break;
}
// find track id property
MP4Integer32Property* pTrackIdProperty = NULL;
pTrakAtom->FindProperty(
"trak.tkhd.trackId",
(MP4Property**)&pTrackIdProperty);
// find track type property
MP4StringProperty* pTypeProperty = NULL;
pTrakAtom->FindProperty(
"trak.mdia.hdlr.handlerType",
(MP4Property**)&pTypeProperty);
// ensure we have the basics properties
if (pTrackIdProperty && pTypeProperty) {
m_trakIds.Add(pTrackIdProperty->GetValue());
MP4Track* pTrack = NULL;
try {
if (!strcmp(pTypeProperty->GetValue(), MP4_HINT_TRACK_TYPE)) {
pTrack = new MP4RtpHintTrack(this, pTrakAtom);
} else {
pTrack = new MP4Track(this, pTrakAtom);
}
m_pTracks.Add(pTrack);
}
catch (MP4Error* e) {
VERBOSE_ERROR(m_verbosity, e->Print());
delete e;
}
// remember when we encounter the OD track
if (pTrack && !strcmp(pTrack->GetType(), MP4_OD_TRACK_TYPE)) {
if (m_odTrackId == MP4_INVALID_TRACK_ID) {
m_odTrackId = pTrackIdProperty->GetValue();
} else {
VERBOSE_READ(GetVerbosity(),
printf("Warning: multiple OD tracks present\n"));
}
}
} else {
m_trakIds.Add(0);
}
trackIndex++;
}
}
void MP4File::CacheProperties()
{
FindIntegerProperty("moov.mvhd.modificationTime",
(MP4Property**)&m_pModificationProperty);
FindIntegerProperty("moov.mvhd.timeScale",
(MP4Property**)&m_pTimeScaleProperty);
FindIntegerProperty("moov.mvhd.duration",
(MP4Property**)&m_pDurationProperty);
}
void MP4File::BeginWrite()
{
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?