📄 mp4file.cpp
字号:
/* * 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. All Rights Reserved. * * Contributor(s): * Dave Mackie dmackie@cisco.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_use64bits = false; 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, bool use64bits){ m_fileName = MP4Stralloc(fileName); m_mode = 'w'; m_use64bits = use64bits; Open("wb+"); // generate a skeletal atom tree m_pRootAtom = MP4Atom::CreateAtom(NULL); m_pRootAtom->SetFile(this); m_pRootAtom->Generate(); CacheProperties(); // create mdat, and insert it after ftyp, and before moov InsertChildAtom(m_pRootAtom, "mdat", 1); // start writing m_pRootAtom->BeginWrite();}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(){ m_pRootAtom->BeginWrite();}void MP4File::FinishWrite(){ // for all tracks, flush chunking buffers for (u_int32_t i = 0; i < m_pTracks.Size(); i++) { ASSERT(m_pTracks[i]); m_pTracks[i]->FinishWrite(); } // ask root atom to write m_pRootAtom->FinishWrite(); // check if file shrunk, e.g. we deleted a track if (GetSize() < m_orgFileSize) { // just use a free atom to mark unused space // MP4Optimize() should be used to clean up this space MP4Atom* pFreeAtom = MP4Atom::CreateAtom("free"); ASSERT(pFreeAtom); pFreeAtom->SetFile(this); pFreeAtom->SetSize(MAX(m_orgFileSize - (m_fileSize + 8), 0)); pFreeAtom->Write(); delete pFreeAtom; }}MP4Duration MP4File::UpdateDuration(MP4Duration duration){ MP4Duration currentDuration = GetDuration(); if (duration > currentDuration) { SetDuration(duration); return duration; } return currentDuration;}void MP4File::Dump(FILE* pDumpFile, bool dumpImplicits){ if (pDumpFile == NULL) { pDumpFile = stdout; } fprintf(pDumpFile, "Dumping %s meta-information...\n", m_fileName); m_pRootAtom->Dump(pDumpFile, 0, dumpImplicits);}void MP4File::Close(){ if (m_mode == 'w') { SetIntegerProperty("moov.mvhd.modificationTime", MP4GetAbsTimestamp()); FinishWrite(); } fclose(m_pFile); m_pFile = NULL;}const char* MP4File::TempFileName(){ // there are so many attempts in libc to get this right // that for portablity reasons, it's best just to roll our own#ifndef _WIN32 static char tempFileName[64]; u_int32_t i; for (i = getpid(); i < 0xFFFFFFFF; i++) { sprintf(tempFileName, "./tmp%u.mp4", i); if (access(tempFileName, F_OK) != 0) { break; } } if (i == 0xFFFFFFFF) { throw new MP4Error("can't create temporary file", "TempFileName"); }#else static char tempFileName[MAX_PATH + 3]; GetTempFileName(".", // dir. for temp. files "mp4", // temp. filename prefix 0, // create unique name tempFileName); // buffer for name #endif return tempFileName;}void MP4File::Rename(const char* oldFileName, const char* newFileName){ int rc;#ifdef _WIN32 rc = remove(newFileName); if (rc == 0) { rc = rename(oldFileName, newFileName); }#else rc = rename(oldFileName, newFileName);#endif if (rc != 0) { throw new MP4Error(errno, "can't overwrite existing file", "Rename"); }}void MP4File::ProtectWriteOperation(char* where){ if (m_mode == 'r') { throw new MP4Error("operation not permitted in read mode", where); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -