⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mp4file.cpp

📁 MPEG-4编解码的实现(包括MPEG4视音频编解码)
💻 CPP
📖 第 1 页 / 共 4 页
字号:
/*
 * 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 + -