📄 mp4track.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 - 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"#define AMR_UNINITIALIZED -1#define AMR_TRUE 0#define AMR_FALSE 1MP4Track::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_bytesPerSample should be set to 1, except for the // quicktime audio constant bit rate samples, which have non-1 values m_bytesPerSample = 1; m_samplesPerChunk = 0; m_durationPerChunk = 0; m_isAmr = AMR_UNINITIALIZED; m_curMode = 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"); } CalculateBytesPerSample();}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(MP4NormalizeTrackType(type, m_pFile->GetVerbosity()));}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"X64" 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 "U64" duration "D64"\n", (pStartTime ? *pStartTime : 0), (pDuration ? *pDuration : 0))); } if (pRenderingOffset) { *pRenderingOffset = GetSampleRenderingOffset(sampleId); VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("ReadSample: renderingOffset "D64"\n", *pRenderingOffset)); } if (pIsSyncSample) { *pIsSyncSample = IsSyncSample(sampleId); VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("ReadSample: isSyncSample %u\n", *pIsSyncSample)); } } __except (EXCEPTION_EXECUTE_HANDLER) { 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){ u_int8_t curMode = 0; 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 (m_isAmr == AMR_UNINITIALIZED ) { // figure out if this is an AMR audio track if (m_pTrakAtom->FindAtom("trak.mdia.minf.stbl.stsd.samr") || m_pTrakAtom->FindAtom("trak.mdia.minf.stbl.stsd.sawb")) { m_isAmr = AMR_TRUE; m_curMode = (pBytes[0] >> 3) & 0x000F; } else { m_isAmr = AMR_FALSE; } } if (m_isAmr == AMR_TRUE) { curMode = (pBytes[0] >> 3) &0x000F; // The mode is in the first byte } if (duration == MP4_INVALID_DURATION) { duration = GetFixedSampleDuration(); } VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(), printf("duration "U64"\n", duration)); if ((m_isAmr == AMR_TRUE) && (m_curMode != curMode)) { WriteChunkBuffer(); m_curMode = curMode; } // 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(); m_curMode = curMode; } UpdateDurations(duration); UpdateModificationTimes(); m_writeSampleId++;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -