mirror of
https://gitlab.com/ecodis/exhale.git
synced 2025-03-12 01:00:11 +01:00
cleanup and bugfix
This commit is contained in:
parent
ce09e2af6e
commit
505b4d4c1a
@ -1,5 +1,6 @@
|
||||
/* basicMP4Writer.cpp - source file for class with basic MPEG-4 file writing capability
|
||||
* written by C. R. Helmrich, last modified in 2020 - see License.htm for legal notices
|
||||
* pre-roll serializer and related code added by J. Calhoun in 2020, see merge request 4
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style License
|
||||
* and comes with ABSOLUTELY NO WARRANTY. This software may be subject to other third-
|
||||
@ -73,7 +74,7 @@ static uint16_t toUShortValue (const uint8_t hiByte, const uint8_t loByte)
|
||||
// public functions
|
||||
int BasicMP4Writer::addFrameAU (const uint8_t* byteBuf, const uint32_t byteCount)
|
||||
{
|
||||
if ((m_fileHandle == -1) || (m_m4aMediaSize > 0xFFFFFFF0u - byteCount))
|
||||
if ((m_fileHandle == -1) || (m_mediaSize > 0xFFFFFFF0u - m_mediaOffset - byteCount))
|
||||
{
|
||||
return 1; // invalid file handle or file getting too big
|
||||
}
|
||||
@ -86,9 +87,9 @@ int BasicMP4Writer::addFrameAU (const uint8_t* byteBuf, const uint32_t byteCount
|
||||
|
||||
if (((m_frameCount++) % m_rndAccPeriod) == 0) // add RAP to list (stco)
|
||||
{
|
||||
m_rndAccOffsets.push_back (m_m4aMediaSize);
|
||||
m_rndAccOffsets.push_back (m_mediaSize);
|
||||
}
|
||||
m_m4aMediaSize += byteCount;
|
||||
m_mediaSize += byteCount;
|
||||
|
||||
return _WRITE (m_fileHandle, byteBuf, byteCount); // write access unit
|
||||
}
|
||||
@ -105,40 +106,16 @@ int BasicMP4Writer::finishFile (const unsigned avgBitrate, const unsigned maxBit
|
||||
#ifndef NO_FIX_FOR_ISSUE_1
|
||||
const uint32_t stssAtomSize = STSX_BSIZE;
|
||||
// The following code creates a 'prol' sample group with a repeating pattern of membership,
|
||||
// indicating that the first sample in each increment of m_rndAccPeriod samples is a member and therefore an IF,
|
||||
// and the remainder are not.
|
||||
// If the encoder is changed to signal the locations of IFs by some other means or in some other arrangement,
|
||||
// either the code that creates the 'csgp' atom below must also be altered accordingly
|
||||
// or new code that creates an 'sbgp' atom instead must be substituted.
|
||||
const uint32_t sgpdAtomSize = STSX_BSIZE + 4 /* defaultLength == 2 */ + 4 /* entryCount == 1 */ + 2 /* rollDistance */;
|
||||
uint32_t patternLengthSize = 0;
|
||||
if (m_rndAccPeriod > UINT16_MAX)
|
||||
{
|
||||
patternLengthSize = 4;
|
||||
}
|
||||
else if (m_rndAccPeriod > UINT8_MAX)
|
||||
{
|
||||
patternLengthSize = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
patternLengthSize = 1;
|
||||
}
|
||||
const uint32_t compactPatternLength = (m_rndAccPeriod + 1) >> 1; // 4 bits per index, padded for byte alignment
|
||||
uint32_t sampleCountSize = 0;
|
||||
if (m_frameCount > UINT16_MAX)
|
||||
{
|
||||
sampleCountSize = 4;
|
||||
}
|
||||
else if (m_frameCount > UINT8_MAX)
|
||||
{
|
||||
sampleCountSize = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleCountSize = 1;
|
||||
}
|
||||
const uint32_t csgpAtomSize = STSX_BSIZE + 4 /* patternCount == 1 */ + patternLengthSize /* patternLength */ + sampleCountSize /* sampleCount */ + compactPatternLength;
|
||||
// indicating that the first sample in each increment of m_rndAccPeriod samples is a member
|
||||
// and therefore an independent frame (IF), and the remainder are not.
|
||||
// If the encoder is changed to signal the locations of IFs by some other means or in some
|
||||
// other arrangement, either the code that creates the 'csgp' atom below must be modified
|
||||
// accordingly or new code that creates an 'sbgp' atom instead must be substituted.
|
||||
const uint32_t sgpdAtomSize = STSX_BSIZE + 4 /*defaultLength == 2*/ + 4 /*entryCount == 1*/ + 2 /*rollDistance*/;
|
||||
const uint32_t patternLengthSize = (m_rndAccPeriod > UINT8_MAX ? 2 : 1);
|
||||
const uint32_t compPatternLength = (m_rndAccPeriod + 1) >> 1; // 4 bits per index, padded for byte alignment
|
||||
const uint32_t sampleCountSize = (m_frameCount > UINT16_MAX ? 4 : (m_frameCount > UINT8_MAX ? 2 : 1));
|
||||
const uint32_t csgpAtomSize = STSX_BSIZE + 4 /*patternCount == 1*/ + patternLengthSize + sampleCountSize + compPatternLength;
|
||||
const uint32_t stblIncrSize = m_ascSizeM5 + stszAtomSize + stscAtomSize + stcoAtomSize + stssAtomSize + sgpdAtomSize + csgpAtomSize;
|
||||
#else
|
||||
const uint32_t stblIncrSize = m_ascSizeM5 + stszAtomSize + stscAtomSize + stcoAtomSize;
|
||||
@ -153,7 +130,7 @@ int BasicMP4Writer::finishFile (const unsigned avgBitrate, const unsigned maxBit
|
||||
uint32_t* const header4Byte = (uint32_t* const) m_staticHeader;
|
||||
int bytesWritten = 0;
|
||||
|
||||
if ((m_fileHandle == -1) || (m_m4aMediaSize > 0xFFFFFFF0u - m_m4aMediaOffset))
|
||||
if ((m_fileHandle == -1) || (m_mediaSize > 0xFFFFFFF0u - m_mediaOffset))
|
||||
{
|
||||
return 1; // invalid file handle or file getting too big
|
||||
}
|
||||
@ -250,7 +227,7 @@ int BasicMP4Writer::finishFile (const unsigned avgBitrate, const unsigned maxBit
|
||||
// add header size corrected random-access offsets to file
|
||||
for (uint32_t i = 0; i < (uint32_t) m_rndAccOffsets.size (); i++)
|
||||
{
|
||||
const uint32_t rndAccOffset = m_rndAccOffsets.at (i) + m_m4aMediaOffset;
|
||||
const uint32_t rndAccOffset = m_rndAccOffsets.at (i) + m_mediaOffset;
|
||||
|
||||
m_dynamicHeader.push_back ((rndAccOffset >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((rndAccOffset >> 16) & UCHAR_MAX);
|
||||
@ -290,8 +267,7 @@ int BasicMP4Writer::finishFile (const unsigned avgBitrate, const unsigned maxBit
|
||||
m_dynamicHeader.push_back (0x63); m_dynamicHeader.push_back (0x73);
|
||||
m_dynamicHeader.push_back (0x67); m_dynamicHeader.push_back (0x70); // csgp
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back ((patternLengthSize > 2 ? (patternLengthSize - 1) : patternLengthSize) << 4 | (sampleCountSize > 2 ? (sampleCountSize - 1) : sampleCountSize) << 2);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back ((__min (3, patternLengthSize) << 4) | (__min (3, sampleCountSize) << 2));
|
||||
m_dynamicHeader.push_back (0x70); m_dynamicHeader.push_back (0x72);
|
||||
m_dynamicHeader.push_back (0x6F); m_dynamicHeader.push_back (0x6C); // prol
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
@ -305,7 +281,7 @@ int BasicMP4Writer::finishFile (const unsigned avgBitrate, const unsigned maxBit
|
||||
{
|
||||
m_dynamicHeader.push_back ((m_rndAccPeriod >> 8) & UCHAR_MAX);
|
||||
}
|
||||
m_dynamicHeader.push_back ( m_rndAccPeriod & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( m_rndAccPeriod & UCHAR_MAX);
|
||||
if (sampleCountSize > 2)
|
||||
{
|
||||
m_dynamicHeader.push_back ((m_frameCount >> 24) & UCHAR_MAX);
|
||||
@ -315,52 +291,39 @@ int BasicMP4Writer::finishFile (const unsigned avgBitrate, const unsigned maxBit
|
||||
{
|
||||
m_dynamicHeader.push_back ((m_frameCount >> 8) & UCHAR_MAX);
|
||||
}
|
||||
m_dynamicHeader.push_back ( m_frameCount & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( (0x01 << 4) | 0x00 ); // the first frame is a member of the 'prol' group and the remainder are not
|
||||
for (uint32_t pNdx = 1; pNdx < compactPatternLength; ++pNdx)
|
||||
m_dynamicHeader.push_back ( m_frameCount & UCHAR_MAX);
|
||||
|
||||
m_dynamicHeader.push_back ( (0x01 << 4) | 0x00 ); // the first frame is a member of the 'prol' group and the remainder are not
|
||||
for (uint32_t pNdx = 1; pNdx < compPatternLength; pNdx++)
|
||||
{
|
||||
// remaining pairs of non-members
|
||||
m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); // remaining 4-bit pairs of non-members
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t moovAndMdatOverhead = STAT_HEADER_SIZE + m_dynamicHeader.size() + 8;
|
||||
if (moovAndMdatOverhead > m_m4aMediaOffset)
|
||||
const uint32_t moovAndMdatOverhead = STAT_HEADER_SIZE + (uint32_t) m_dynamicHeader.size () + 8;
|
||||
const uint32_t headerPaddingLength = uint32_t (m_mediaOffset - moovAndMdatOverhead);
|
||||
|
||||
if (moovAndMdatOverhead > m_mediaOffset) // header has grown to encroach upon the media data - fatal error
|
||||
{
|
||||
return 1; // header has grown to encroach upon the media data. Fatal error.
|
||||
// Time to reconsider the calculation of estimHeaderSize in BasicMP4Writer::initHeader.
|
||||
return 1;
|
||||
}
|
||||
if (moovAndMdatOverhead < m_m4aMediaOffset)
|
||||
else // start the 'mdat' atom early with a small amount of unused but informative data before the first AU
|
||||
{
|
||||
const uint32_t freeSize = m_m4aMediaOffset - moovAndMdatOverhead;
|
||||
if (freeSize >= 8)
|
||||
{
|
||||
// insert a 'free' atom between the 'moov' and 'mdat' atoms
|
||||
m_dynamicHeader.push_back ((freeSize >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((freeSize >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((freeSize >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( freeSize & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back (0x66); m_dynamicHeader.push_back (0x72);
|
||||
m_dynamicHeader.push_back (0x65); m_dynamicHeader.push_back (0x65); // free
|
||||
for (uint32_t fNdx = 8; fNdx < freeSize; ++fNdx)
|
||||
{
|
||||
m_dynamicHeader.push_back (0x00);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// start the 'mdat' atom early with a small amount of unused data before the first frame
|
||||
m_m4aMediaSize += freeSize;
|
||||
}
|
||||
m_mediaSize += headerPaddingLength;
|
||||
}
|
||||
|
||||
uint32_t mdatSize = m_m4aMediaSize + 8;
|
||||
m_dynamicHeader.push_back ((mdatSize >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((mdatSize >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((mdatSize >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( mdatSize & UCHAR_MAX);
|
||||
const uint32_t mdatAtomSize = m_mediaSize + 8;
|
||||
|
||||
m_dynamicHeader.push_back ((mdatAtomSize >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((mdatAtomSize >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((mdatAtomSize >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( mdatAtomSize & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back (0x6D); m_dynamicHeader.push_back (0x64);
|
||||
m_dynamicHeader.push_back (0x61); m_dynamicHeader.push_back (0x74); // mdat
|
||||
for (uint32_t pNdx = 0; pNdx < headerPaddingLength; pNdx++)
|
||||
{
|
||||
m_dynamicHeader.push_back (0x00); // add padding bytes. TODO: ver string?
|
||||
}
|
||||
|
||||
_SEEK (m_fileHandle, 0, 0 /*SEEK_SET*/); // back to start
|
||||
|
||||
@ -393,7 +356,8 @@ int BasicMP4Writer::initHeader (const uint32_t audioLength) // reserve bytes for
|
||||
const unsigned chunkCount = ((frameCount + m_rndAccPeriod - 1) / m_rndAccPeriod);
|
||||
const unsigned finalChunk = (frameCount <= m_rndAccPeriod ? 0 : frameCount % m_rndAccPeriod);
|
||||
#ifndef NO_FIX_FOR_ISSUE_1
|
||||
const int estimHeaderSize = STAT_HEADER_SIZE + m_ascSizeM5 + 6+4 + frameCount * 4 /*stsz*/ + STSX_BSIZE * 10 +
|
||||
const unsigned smpGrpSize = 10 /*sgpd*/ + (m_rndAccPeriod > UINT8_MAX ? 10 : 9) + ((m_rndAccPeriod + 1) >> 1) /*csgp*/;
|
||||
const int estimHeaderSize = STAT_HEADER_SIZE + m_ascSizeM5 + 6+4 + frameCount * 4 /*stsz*/ + STSX_BSIZE * 6 + smpGrpSize +
|
||||
#else
|
||||
const int estimHeaderSize = STAT_HEADER_SIZE + m_ascSizeM5 + 6+4 + frameCount * 4 /*stsz*/ + STSX_BSIZE * 3 +
|
||||
#endif
|
||||
@ -404,7 +368,8 @@ int BasicMP4Writer::initHeader (const uint32_t audioLength) // reserve bytes for
|
||||
{
|
||||
bytesWritten += _WRITE (m_fileHandle, m_staticHeader, __min (i, STAT_HEADER_SIZE));
|
||||
}
|
||||
m_m4aMediaOffset = bytesWritten; // first frame will be written at this offset
|
||||
m_mediaOffset = bytesWritten; // first frame will be written at this offset
|
||||
|
||||
return bytesWritten;
|
||||
#endif
|
||||
}
|
||||
@ -427,7 +392,11 @@ unsigned BasicMP4Writer::open (const int mp4FileHandle, const unsigned sampleRat
|
||||
}
|
||||
|
||||
m_fileHandle = mp4FileHandle;
|
||||
#ifndef NO_FIX_FOR_ISSUE_1
|
||||
reset (frameLength, pregapLength, __min (USHRT_MAX, raPeriod));
|
||||
#else
|
||||
reset (frameLength, pregapLength, raPeriod);
|
||||
#endif
|
||||
#if 0 // DEBUG
|
||||
m_sampleRate = sampleRate;
|
||||
#endif
|
||||
@ -499,8 +468,8 @@ void BasicMP4Writer::reset (const unsigned frameLength /*= 0*/, const unsigned p
|
||||
m_ascSizeM5 = 0;
|
||||
m_frameCount = 0;
|
||||
m_frameLength = frameLength;
|
||||
m_m4aMediaSize = 0; // bytes of media data (i.e. audio frames)
|
||||
m_m4aMediaOffset = 0; // offset of first byte of media data
|
||||
m_mediaOffset = 0; // offset of first 'mdat' data byte serialized to file
|
||||
m_mediaSize = 0; // total length of 'mdat' (access unit) payload in file
|
||||
m_pregapLength = pregapLength;
|
||||
m_rndAccPeriod = raPeriod;
|
||||
m_sampleRate = 0;
|
||||
|
@ -35,8 +35,8 @@ private:
|
||||
int m_fileHandle;
|
||||
unsigned m_frameCount;
|
||||
unsigned m_frameLength;
|
||||
unsigned m_m4aMediaSize;
|
||||
unsigned m_m4aMediaOffset; // offset of first frame serialized to file
|
||||
uint32_t m_mediaOffset; // offset of first mdat payload
|
||||
uint32_t m_mediaSize; // number of bytes of mdat content
|
||||
unsigned m_pregapLength; // encoder look-ahead, pre-roll
|
||||
unsigned m_rndAccPeriod; // random-access (RA) interval
|
||||
unsigned m_sampleRate;
|
||||
|
Loading…
x
Reference in New Issue
Block a user