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

📄 mp4track.cpp

📁 AAC编解码源码
💻 CPP
📖 第 1 页 / 共 3 页
字号:
/*
 * 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
 *		Alix Marchandise-Franquet	alix@cisco.com
 */

#include "mp4common.h"

MP4Track::MP4Track(MP4File* pFile, MP4Atom* pTrakAtom) 
{
	m_pFile = pFile;
	m_pTrakAtom = pTrakAtom;

	m_lastStsdIndex = 0;
	m_lastSampleFile = NULL;

	m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID;
	m_pCachedReadSample = NULL;
	m_cachedReadSampleSize = 0;

	m_writeSampleId = 1;
	m_fixedSampleDuration = 0;
	m_pChunkBuffer = NULL;
	m_chunkBufferSize = 0;
	m_chunkSamples = 0;
	m_chunkDuration = 0;

	m_samplesPerChunk = 0;
	m_durationPerChunk = 0;

	bool success = true;

	MP4Integer32Property* pTrackIdProperty;
	success &= m_pTrakAtom->FindProperty(
		"trak.tkhd.trackId",
		(MP4Property**)&pTrackIdProperty);
	if (success) {
		m_trackId = pTrackIdProperty->GetValue();
	}

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.mdhd.timeScale", 
		(MP4Property**)&m_pTimeScaleProperty);
	if (success) {
		// default chunking is 1 second of samples
		m_durationPerChunk = m_pTimeScaleProperty->GetValue();
	}

	success &= m_pTrakAtom->FindProperty(
		"trak.tkhd.duration", 
		(MP4Property**)&m_pTrackDurationProperty);

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.mdhd.duration", 
		(MP4Property**)&m_pMediaDurationProperty);

	success &= m_pTrakAtom->FindProperty(
		"trak.tkhd.modificationTime", 
		(MP4Property**)&m_pTrackModificationProperty);

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.mdhd.modificationTime", 
		(MP4Property**)&m_pMediaModificationProperty);

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.hdlr.handlerType",
		(MP4Property**)&m_pTypeProperty);

	// get handles on sample size information

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stsz.sampleSize",
		(MP4Property**)&m_pStszFixedSampleSizeProperty);
	
	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stsz.sampleCount",
		(MP4Property**)&m_pStszSampleCountProperty);

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stsz.entries.sampleSize",
		(MP4Property**)&m_pStszSampleSizeProperty);

	// get handles on information needed to map sample id's to file offsets

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stsc.entryCount",
		(MP4Property**)&m_pStscCountProperty);

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stsc.entries.firstChunk",
		(MP4Property**)&m_pStscFirstChunkProperty);

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stsc.entries.samplesPerChunk",
		(MP4Property**)&m_pStscSamplesPerChunkProperty);

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stsc.entries.sampleDescriptionIndex",
		(MP4Property**)&m_pStscSampleDescrIndexProperty);

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stsc.entries.firstSample",
		(MP4Property**)&m_pStscFirstSampleProperty);

	bool haveStco = m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stco.entryCount",
		(MP4Property**)&m_pChunkCountProperty);

	if (haveStco) {
		success &= m_pTrakAtom->FindProperty(
			"trak.mdia.minf.stbl.stco.entries.chunkOffset",
			(MP4Property**)&m_pChunkOffsetProperty);
	} else {
		success &= m_pTrakAtom->FindProperty(
			"trak.mdia.minf.stbl.co64.entryCount",
			(MP4Property**)&m_pChunkCountProperty);

		success &= m_pTrakAtom->FindProperty(
			"trak.mdia.minf.stbl.co64.entries.chunkOffset",
			(MP4Property**)&m_pChunkOffsetProperty);
	}

	// get handles on sample timing info

	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stts.entryCount",
		(MP4Property**)&m_pSttsCountProperty);
	
	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stts.entries.sampleCount",
		(MP4Property**)&m_pSttsSampleCountProperty);
	
	success &= m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stts.entries.sampleDelta",
		(MP4Property**)&m_pSttsSampleDeltaProperty);
	
	// get handles on rendering offset info if it exists

	m_pCttsCountProperty = NULL;
	m_pCttsSampleCountProperty = NULL;
	m_pCttsSampleOffsetProperty = NULL;

	bool haveCtts = m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.ctts.entryCount",
		(MP4Property**)&m_pCttsCountProperty);

	if (haveCtts) {
		success &= m_pTrakAtom->FindProperty(
			"trak.mdia.minf.stbl.ctts.entries.sampleCount",
			(MP4Property**)&m_pCttsSampleCountProperty);

		success &= m_pTrakAtom->FindProperty(
			"trak.mdia.minf.stbl.ctts.entries.sampleOffset",
			(MP4Property**)&m_pCttsSampleOffsetProperty);
	}

	// get handles on sync sample info if it exists

	m_pStssCountProperty = NULL;
	m_pStssSampleProperty = NULL;

	bool haveStss = m_pTrakAtom->FindProperty(
		"trak.mdia.minf.stbl.stss.entryCount",
		(MP4Property**)&m_pStssCountProperty);

	if (haveStss) {
		success &= m_pTrakAtom->FindProperty(
			"trak.mdia.minf.stbl.stss.entries.sampleNumber",
			(MP4Property**)&m_pStssSampleProperty);
	}

	// edit list
	InitEditListProperties();

	// was everything found?
	if (!success) {
		throw new MP4Error("invalid track", "MP4Track::MP4Track");
	}
}

MP4Track::~MP4Track()
{
	MP4Free(m_pCachedReadSample);
	MP4Free(m_pChunkBuffer);
}

const char* MP4Track::GetType()
{
	return m_pTypeProperty->GetValue();
}

void MP4Track::SetType(const char* type) 
{
	m_pTypeProperty->SetValue(NormalizeTrackType(type));
}

void MP4Track::ReadSample(
	MP4SampleId sampleId,
	u_int8_t** ppBytes, 
	u_int32_t* pNumBytes, 
	MP4Timestamp* pStartTime, 
	MP4Duration* pDuration,
	MP4Duration* pRenderingOffset, 
	bool* pIsSyncSample)
{
	if (sampleId == MP4_INVALID_SAMPLE_ID) {
		throw new MP4Error("sample id can't be zero", 
			"MP4Track::ReadSample");
	}

	// handle unusual case of wanting to read a sample
	// that is still sitting in the write chunk buffer
	if (m_pChunkBuffer && sampleId >= m_writeSampleId - m_chunkSamples) {
		WriteChunkBuffer();
	}

	FILE* pFile = GetSampleFile(sampleId);

	if (pFile == (FILE*)-1) {
		throw new MP4Error("sample is located in an inaccessible file",
			"MP4Track::ReadSample");
	}

	u_int64_t fileOffset = GetSampleFileOffset(sampleId);

	u_int32_t sampleSize = GetSampleSize(sampleId);
	if (*ppBytes != NULL && *pNumBytes < sampleSize) {
		throw new MP4Error("sample buffer is too small",
			 "MP4Track::ReadSample");
	}
	*pNumBytes = sampleSize;

	VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
		printf("ReadSample: track %u id %u offset 0x"LLX" size %u (0x%x)\n",
			m_trackId, sampleId, fileOffset, *pNumBytes, *pNumBytes));

	bool bufferMalloc = false;
	if (*ppBytes == NULL) {
		*ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes);
		bufferMalloc = true;
	}

	u_int64_t oldPos = m_pFile->GetPosition(pFile); // only used in mode == 'w'
	try { 
		m_pFile->SetPosition(fileOffset, pFile);
		m_pFile->ReadBytes(*ppBytes, *pNumBytes, pFile);

		if (pStartTime || pDuration) {
			GetSampleTimes(sampleId, pStartTime, pDuration);

			VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
				printf("ReadSample:  start "LLU" duration "LLD"\n",
					(pStartTime ? *pStartTime : 0), 
					(pDuration ? *pDuration : 0)));
		}
		if (pRenderingOffset) {
			*pRenderingOffset = GetSampleRenderingOffset(sampleId);

			VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
				printf("ReadSample:  renderingOffset "LLD"\n",
					*pRenderingOffset));
		}
		if (pIsSyncSample) {
			*pIsSyncSample = IsSyncSample(sampleId);

			VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
				printf("ReadSample:  isSyncSample %u\n",
					*pIsSyncSample));
		}
	}

	catch (MP4Error* e) {
		if (bufferMalloc) {
			// let's not leak memory
			MP4Free(*ppBytes);
			*ppBytes = NULL;
		}
		if (m_pFile->GetMode() == 'w') {
			m_pFile->SetPosition(oldPos, pFile);
		}
		throw e;
	}

	if (m_pFile->GetMode() == 'w') {
		m_pFile->SetPosition(oldPos, pFile);
	}
}

void MP4Track::ReadSampleFragment(
	MP4SampleId sampleId,
	u_int32_t sampleOffset,
	u_int16_t sampleLength,
	u_int8_t* pDest)
{
	if (sampleId == MP4_INVALID_SAMPLE_ID) {
		throw new MP4Error("invalid sample id", 
			"MP4Track::ReadSampleFragment");
	}

	if (sampleId != m_cachedReadSampleId) {
		MP4Free(m_pCachedReadSample);
		m_pCachedReadSample = NULL;
		m_cachedReadSampleSize = 0;
		m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID;

		ReadSample(
			sampleId,
			&m_pCachedReadSample,
			&m_cachedReadSampleSize);

		m_cachedReadSampleId = sampleId;
	}

	if (sampleOffset + sampleLength > m_cachedReadSampleSize) {
		throw new MP4Error("offset and/or length are too large", 
			"MP4Track::ReadSampleFragment");
	}

	memcpy(pDest, &m_pCachedReadSample[sampleOffset], sampleLength);
}

void MP4Track::WriteSample(
	const u_int8_t* pBytes, 
	u_int32_t numBytes,
	MP4Duration duration, 
	MP4Duration renderingOffset, 
	bool isSyncSample)
{
	VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
		printf("WriteSample: track %u id %u size %u (0x%x) ",
			m_trackId, m_writeSampleId, numBytes, numBytes));

	if (pBytes == NULL && numBytes > 0) {
		throw new MP4Error("no sample data", "MP4WriteSample");
	}

	if (duration == MP4_INVALID_DURATION) {
		duration = GetFixedSampleDuration();
	}

	VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
		printf("duration "LLU"\n", duration));

	// append sample bytes to chunk buffer
	m_pChunkBuffer = (u_int8_t*)MP4Realloc(m_pChunkBuffer, 
		m_chunkBufferSize + numBytes);
	memcpy(&m_pChunkBuffer[m_chunkBufferSize], pBytes, numBytes);
	m_chunkBufferSize += numBytes;
	m_chunkSamples++;
	m_chunkDuration += duration;

	UpdateSampleSizes(m_writeSampleId, numBytes);

	UpdateSampleTimes(duration);

	UpdateRenderingOffsets(m_writeSampleId, renderingOffset);

	UpdateSyncSamples(m_writeSampleId, isSyncSample);

	if (IsChunkFull(m_writeSampleId)) {
		WriteChunkBuffer();
	}

	UpdateDurations(duration);

	UpdateModificationTimes();

	m_writeSampleId++;
}

void MP4Track::WriteChunkBuffer()
{
	if (m_chunkBufferSize == 0) {
		return;
	}

	u_int64_t chunkOffset = m_pFile->GetPosition();

	// write chunk buffer
	m_pFile->WriteBytes(m_pChunkBuffer, m_chunkBufferSize);

	VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
		printf("WriteChunk: track %u offset 0x"LLX" size %u (0x%x) numSamples %u\n",
			m_trackId, chunkOffset, m_chunkBufferSize, 
			m_chunkBufferSize, m_chunkSamples));

	UpdateSampleToChunk(m_writeSampleId, 
		m_pChunkCountProperty->GetValue() + 1, 
		m_chunkSamples);

	UpdateChunkOffsets(chunkOffset);

	// clean up chunk buffer
	MP4Free(m_pChunkBuffer);
	m_pChunkBuffer = NULL;
	m_chunkBufferSize = 0;
	m_chunkSamples = 0;
	m_chunkDuration = 0;
}

void MP4Track::FinishWrite()
{
	// write out any remaining samples in chunk buffer
	WriteChunkBuffer();

	// record buffer size and bitrates
	MP4BitfieldProperty* pBufferSizeProperty;

	if (m_pTrakAtom->FindProperty(
	  "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.bufferSizeDB",
	  (MP4Property**)&pBufferSizeProperty)) {
		pBufferSizeProperty->SetValue(GetMaxSampleSize());
	}

	MP4Integer32Property* pBitrateProperty;

	if (m_pTrakAtom->FindProperty(
	  "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.maxBitrate",
	  (MP4Property**)&pBitrateProperty)) {
		pBitrateProperty->SetValue(GetMaxBitrate());
	}

	if (m_pTrakAtom->FindProperty(
	  "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.avgBitrate",
	  (MP4Property**)&pBitrateProperty)) {
		pBitrateProperty->SetValue(GetAvgBitrate());
	}
}

bool MP4Track::IsChunkFull(MP4SampleId sampleId)
{
	if (m_samplesPerChunk) {
		return m_chunkSamples >= m_samplesPerChunk;
	}

	ASSERT(m_durationPerChunk);
	return m_chunkDuration >= m_durationPerChunk;
}

u_int32_t MP4Track::GetNumberOfSamples()
{
	return m_pStszSampleCountProperty->GetValue();
}

u_int32_t MP4Track::GetSampleSize(MP4SampleId sampleId)
{
	u_int32_t fixedSampleSize = 
		m_pStszFixedSampleSizeProperty->GetValue(); 

	if (fixedSampleSize != 0) {
		return fixedSampleSize;
	}
	return m_pStszSampleSizeProperty->GetValue(sampleId - 1);
}

u_int32_t MP4Track::GetMaxSampleSize()
{
	u_int32_t fixedSampleSize = 
		m_pStszFixedSampleSizeProperty->GetValue(); 

	if (fixedSampleSize != 0) {
		return fixedSampleSize;
	}

	u_int32_t maxSampleSize = 0;
	u_int32_t numSamples = m_pStszSampleSizeProperty->GetCount();
	for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
		u_int32_t sampleSize =
			m_pStszSampleSizeProperty->GetValue(sid - 1);
		if (sampleSize > maxSampleSize) {
			maxSampleSize = sampleSize;
		}
	}
	return maxSampleSize;
}

u_int64_t MP4Track::GetTotalOfSampleSizes()
{
	u_int32_t fixedSampleSize = 
		m_pStszFixedSampleSizeProperty->GetValue(); 

	// if fixed sample size, just need to multiply by number of samples
	if (fixedSampleSize != 0) {
		return fixedSampleSize * GetNumberOfSamples();
	}

	// else non-fixed sample size, sum them
	u_int64_t totalSampleSizes = 0;
	u_int32_t numSamples = m_pStszSampleSizeProperty->GetCount();
	for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
		u_int32_t sampleSize =
			m_pStszSampleSizeProperty->GetValue(sid - 1);
		totalSampleSizes += sampleSize;
	}
	return totalSampleSizes;
}

void MP4Track::UpdateSampleSizes(MP4SampleId sampleId, u_int32_t numBytes)
{
	// for first sample
	if (sampleId == 1) {
		if (numBytes > 0) {
			// presume sample size is fixed
			m_pStszFixedSampleSizeProperty->SetValue(numBytes); 
		} else {
			// special case of first sample is zero bytes in length
			// leave m_pStszFixedSampleSizeProperty at 0
			// start recording variable sample sizes
			m_pStszSampleSizeProperty->AddValue(0);
		}

	} else { // sampleId > 1
		u_int32_t fixedSampleSize = 
			m_pStszFixedSampleSizeProperty->GetValue(); 

		if (fixedSampleSize == 0 || numBytes != fixedSampleSize) {
			// sample size is not fixed

			if (fixedSampleSize) {
				// need to clear fixed sample size
				m_pStszFixedSampleSizeProperty->SetValue(0); 

				// and create sizes for all previous samples
				for (MP4SampleId sid = 1; sid < sampleId; sid++) {
					m_pStszSampleSizeProperty->AddValue(fixedSampleSize);
				}
			}

			// add size value for this sample
			m_pStszSampleSizeProperty->AddValue(numBytes);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -