| /* |
| * 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 "src/impl.h" |
| |
| namespace mp4v2 { |
| namespace impl { |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| // MP4File low level IO support |
| |
| uint64_t MP4File::GetPosition( File* file ) |
| { |
| if( m_memoryBuffer ) |
| return m_memoryBufferPosition; |
| |
| if( !file ) |
| file = m_file; |
| |
| ASSERT( file ); |
| return file->position; |
| } |
| |
| void MP4File::SetPosition( uint64_t pos, File* file ) |
| { |
| if( m_memoryBuffer ) { |
| if( pos >= m_memoryBufferSize ) |
| throw new Exception( "position out of range", __FILE__, __LINE__, __FUNCTION__ ); |
| m_memoryBufferPosition = pos; |
| return; |
| } |
| |
| if( !file ) |
| file = m_file; |
| |
| ASSERT( file ); |
| if( file->seek( pos )) |
| throw new PlatformException( "seek failed", sys::getLastError(), __FILE__, __LINE__, __FUNCTION__ ); |
| } |
| |
| uint64_t MP4File::GetSize( File* file ) |
| { |
| if( m_memoryBuffer ) |
| return m_memoryBufferSize; |
| |
| if( !file ) |
| file = m_file; |
| |
| ASSERT( file ); |
| return file->size; |
| } |
| |
| void MP4File::ReadBytes( uint8_t* buf, uint32_t bufsiz, File* file ) |
| { |
| if( bufsiz == 0 ) |
| return; |
| |
| ASSERT( buf ); |
| WARNING( m_numReadBits > 0 ); |
| |
| if( m_memoryBuffer ) { |
| if( m_memoryBufferPosition + bufsiz > m_memoryBufferSize ) |
| throw new Exception( "not enough bytes, reached end-of-memory", __FILE__, __LINE__, __FUNCTION__ ); |
| memcpy( buf, &m_memoryBuffer[m_memoryBufferPosition], bufsiz ); |
| m_memoryBufferPosition += bufsiz; |
| return; |
| } |
| |
| if( !file ) |
| file = m_file; |
| |
| ASSERT( file ); |
| File::Size nin; |
| if( file->read( buf, bufsiz, nin )) |
| throw new PlatformException( "read failed", sys::getLastError(), __FILE__, __LINE__, __FUNCTION__ ); |
| if( nin != bufsiz ) |
| throw new Exception( "not enough bytes, reached end-of-file", __FILE__, __LINE__, __FUNCTION__ ); |
| } |
| |
| void MP4File::PeekBytes( uint8_t* buf, uint32_t bufsiz, File* file ) |
| { |
| const uint64_t pos = GetPosition( file ); |
| ReadBytes( buf, bufsiz, file ); |
| SetPosition( pos, file ); |
| } |
| |
| void MP4File::EnableMemoryBuffer( uint8_t* pBytes, uint64_t numBytes ) |
| { |
| ASSERT( !m_memoryBuffer ); |
| |
| if (pBytes) { |
| m_memoryBuffer = pBytes; |
| m_memoryBufferSize = numBytes; |
| } else { |
| if (numBytes) { |
| m_memoryBufferSize = numBytes; |
| } else { |
| m_memoryBufferSize = 4096; |
| } |
| m_memoryBuffer = (uint8_t*)MP4Malloc(m_memoryBufferSize); |
| } |
| m_memoryBufferPosition = 0; |
| } |
| |
| void MP4File::DisableMemoryBuffer( uint8_t** ppBytes, uint64_t* pNumBytes ) |
| { |
| ASSERT(m_memoryBuffer != NULL); |
| |
| if (ppBytes) { |
| *ppBytes = m_memoryBuffer; |
| } |
| if (pNumBytes) { |
| *pNumBytes = m_memoryBufferPosition; |
| } |
| |
| m_memoryBuffer = NULL; |
| m_memoryBufferSize = 0; |
| m_memoryBufferPosition = 0; |
| } |
| |
| void MP4File::WriteBytes( uint8_t* buf, uint32_t bufsiz, File* file ) |
| { |
| ASSERT( m_numWriteBits == 0 || m_numWriteBits >= 8 ); |
| |
| if( !buf || bufsiz == 0 ) |
| return; |
| |
| if( m_memoryBuffer ) { |
| if( m_memoryBufferPosition + bufsiz > m_memoryBufferSize ) { |
| m_memoryBufferSize = 2 * (m_memoryBufferSize + bufsiz); |
| m_memoryBuffer = (uint8_t*)MP4Realloc( m_memoryBuffer, m_memoryBufferSize ); |
| } |
| memcpy( &m_memoryBuffer[m_memoryBufferPosition], buf, bufsiz ); |
| m_memoryBufferPosition += bufsiz; |
| return; |
| } |
| |
| if( !file ) |
| file = m_file; |
| |
| ASSERT( file ); |
| File::Size nout; |
| if( file->write( buf, bufsiz, nout )) |
| throw new PlatformException( "write failed", sys::getLastError(), __FILE__, __LINE__, __FUNCTION__ ); |
| if( nout != bufsiz ) |
| throw new Exception( "not all bytes written", __FILE__, __LINE__, __FUNCTION__ ); |
| } |
| |
| uint64_t MP4File::ReadUInt(uint8_t size) |
| { |
| switch (size) { |
| case 1: |
| return ReadUInt8(); |
| case 2: |
| return ReadUInt16(); |
| case 3: |
| return ReadUInt24(); |
| case 4: |
| return ReadUInt32(); |
| case 8: |
| return ReadUInt64(); |
| default: |
| ASSERT(false); |
| return 0; |
| } |
| } |
| |
| uint8_t MP4File::ReadUInt8() |
| { |
| uint8_t data; |
| ReadBytes(&data, 1); |
| return data; |
| } |
| |
| void MP4File::WriteUInt8(uint8_t value) |
| { |
| WriteBytes(&value, 1); |
| } |
| |
| uint16_t MP4File::ReadUInt16() |
| { |
| uint8_t data[2]; |
| ReadBytes(&data[0], 2); |
| return ((data[0] << 8) | data[1]); |
| } |
| |
| void MP4File::WriteUInt16(uint16_t value) |
| { |
| uint8_t data[2]; |
| data[0] = (value >> 8) & 0xFF; |
| data[1] = value & 0xFF; |
| WriteBytes(data, 2); |
| } |
| |
| uint32_t MP4File::ReadUInt24() |
| { |
| uint8_t data[3]; |
| ReadBytes(&data[0], 3); |
| return ((data[0] << 16) | (data[1] << 8) | data[2]); |
| } |
| |
| void MP4File::WriteUInt24(uint32_t value) |
| { |
| uint8_t data[3]; |
| data[0] = (value >> 16) & 0xFF; |
| data[1] = (value >> 8) & 0xFF; |
| data[2] = value & 0xFF; |
| WriteBytes(data, 3); |
| } |
| |
| uint32_t MP4File::ReadUInt32() |
| { |
| uint8_t data[4]; |
| ReadBytes(&data[0], 4); |
| return ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]); |
| } |
| |
| void MP4File::WriteUInt32(uint32_t value) |
| { |
| uint8_t data[4]; |
| data[0] = (value >> 24) & 0xFF; |
| data[1] = (value >> 16) & 0xFF; |
| data[2] = (value >> 8) & 0xFF; |
| data[3] = value & 0xFF; |
| WriteBytes(data, 4); |
| } |
| |
| uint64_t MP4File::ReadUInt64() |
| { |
| uint8_t data[8]; |
| uint64_t result = 0; |
| uint64_t temp; |
| |
| ReadBytes(&data[0], 8); |
| |
| for (int i = 0; i < 8; i++) { |
| temp = data[i]; |
| result |= temp << ((7 - i) * 8); |
| } |
| return result; |
| } |
| |
| void MP4File::WriteUInt64(uint64_t value) |
| { |
| uint8_t data[8]; |
| |
| for (int i = 7; i >= 0; i--) { |
| data[i] = value & 0xFF; |
| value >>= 8; |
| } |
| WriteBytes(data, 8); |
| } |
| |
| float MP4File::ReadFixed16() |
| { |
| uint8_t iPart = ReadUInt8(); |
| uint8_t fPart = ReadUInt8(); |
| |
| return iPart + (((float)fPart) / 0x100); |
| } |
| |
| void MP4File::WriteFixed16(float value) |
| { |
| if (value >= 0x100) { |
| ostringstream msg; |
| msg << value << " out of range"; |
| throw new PlatformException(msg.str().c_str(), ERANGE, __FILE__, __LINE__, __FUNCTION__); |
| } |
| |
| uint8_t iPart = (uint8_t)value; |
| uint8_t fPart = (uint8_t)((value - iPart) * 0x100); |
| |
| WriteUInt8(iPart); |
| WriteUInt8(fPart); |
| } |
| |
| float MP4File::ReadFixed32() |
| { |
| uint16_t iPart = ReadUInt16(); |
| uint16_t fPart = ReadUInt16(); |
| |
| return iPart + (((float)fPart) / 0x10000); |
| } |
| |
| void MP4File::WriteFixed32(float value) |
| { |
| if (value >= 0x10000) { |
| ostringstream msg; |
| msg << value << " out of range"; |
| throw new PlatformException(msg.str().c_str(), ERANGE, __FILE__, __LINE__, __FUNCTION__); |
| } |
| |
| uint16_t iPart = (uint16_t)value; |
| uint16_t fPart = (uint16_t)((value - iPart) * 0x10000); |
| |
| WriteUInt16(iPart); |
| WriteUInt16(fPart); |
| } |
| |
| float MP4File::ReadFloat() |
| { |
| union { |
| float f; |
| uint32_t i; |
| } u; |
| |
| u.i = ReadUInt32(); |
| return u.f; |
| } |
| |
| void MP4File::WriteFloat(float value) |
| { |
| union { |
| float f; |
| uint32_t i; |
| } u; |
| |
| u.f = value; |
| WriteUInt32(u.i); |
| } |
| |
| char* MP4File::ReadString() |
| { |
| uint32_t length = 0; |
| uint32_t alloced = 64; |
| char* data = (char*)MP4Malloc(alloced); |
| |
| do { |
| if (length == alloced) { |
| data = (char*)MP4Realloc(data, alloced * 2); |
| if (data == NULL) return NULL; |
| alloced *= 2; |
| } |
| ReadBytes((uint8_t*)&data[length], 1); |
| length++; |
| } while (data[length - 1] != 0); |
| |
| data = (char*)MP4Realloc(data, length); |
| return data; |
| } |
| |
| void MP4File::WriteString(char* string) |
| { |
| if (string == NULL) { |
| uint8_t zero = 0; |
| WriteBytes(&zero, 1); |
| } else { |
| WriteBytes((uint8_t*)string, (uint32_t)strlen(string) + 1); |
| } |
| } |
| |
| char* MP4File::ReadCountedString(uint8_t charSize, bool allowExpandedCount, uint8_t fixedLength) |
| { |
| uint32_t charLength; |
| if (allowExpandedCount) { |
| uint8_t b; |
| uint32_t ix = 0; |
| charLength = 0; |
| do { |
| b = ReadUInt8(); |
| charLength += b; |
| ix++; |
| if (ix > 25) |
| throw new PlatformException("Counted string too long 25 * 255",ERANGE, |
| __FILE__, __LINE__, __FUNCTION__); |
| } while (b == 255); |
| } else { |
| charLength = ReadUInt8(); |
| } |
| |
| if (fixedLength && (charLength > fixedLength - 1)) { |
| /* |
| * The counted length of this string is greater than the |
| * maxiumum fixed length, so truncate the string to the |
| * maximum fixed length amount (take 1 byte away from the |
| * fixedlength since we've already sacrificed one byte for |
| * reading the counted length, and there has been a bug where |
| * a non counted string has been used in the place of a |
| * counted string). |
| */ |
| WARNING(charLength > fixedLength - 1); |
| charLength = fixedLength - 1U; |
| } |
| |
| uint32_t byteLength = charLength * charSize; |
| char* data = (char*)MP4Malloc(byteLength + 1); |
| if (byteLength > 0) { |
| ReadBytes((uint8_t*)data, byteLength); |
| } |
| data[byteLength] = '\0'; |
| |
| // read padding |
| if (fixedLength) { |
| const uint8_t padsize = fixedLength - byteLength -1U; |
| if( padsize ) { |
| uint8_t* padbuf = (uint8_t*)malloc( padsize ); |
| ReadBytes( padbuf, padsize ); |
| free( padbuf ); |
| } |
| } |
| |
| return data; |
| } |
| |
| void MP4File::WriteCountedString(char* string, |
| uint8_t charSize, bool allowExpandedCount, |
| uint32_t fixedLength) |
| { |
| uint32_t byteLength; |
| uint8_t zero[1]; |
| |
| if (string) { |
| byteLength = (uint32_t)strlen(string); |
| if (fixedLength && (byteLength >= fixedLength)) { |
| byteLength = fixedLength-1; |
| } |
| } |
| else { |
| byteLength = 0; |
| } |
| uint32_t charLength = byteLength / charSize; |
| |
| if (allowExpandedCount) { |
| while (charLength >= 0xFF) { |
| WriteUInt8(0xFF); |
| charLength -= 0xFF; |
| } |
| // Write the count |
| WriteUInt8(charLength); |
| } else { |
| if (charLength > 255) { |
| ostringstream msg; |
| msg << "Length is " << charLength; |
| throw new PlatformException(msg.str().c_str(), ERANGE, __FILE__, __LINE__, __FUNCTION__); |
| } |
| // Write the count |
| WriteUInt8(charLength); |
| } |
| |
| if (byteLength > 0) { |
| // Write the string (or the portion that we want to write) |
| WriteBytes((uint8_t*)string, byteLength); |
| } |
| |
| // Write any padding if this is a fixed length counted string |
| if (fixedLength) { |
| zero[0] = 0; |
| while (byteLength < fixedLength-1U) { |
| WriteBytes(zero, 1); |
| byteLength++; |
| } |
| } |
| } |
| |
| uint64_t MP4File::ReadBits(uint8_t numBits) |
| { |
| ASSERT(numBits > 0); |
| ASSERT(numBits <= 64); |
| |
| uint64_t bits = 0; |
| |
| for (uint8_t i = numBits; i > 0; i--) { |
| if (m_numReadBits == 0) { |
| ReadBytes(&m_bufReadBits, 1); |
| m_numReadBits = 8; |
| } |
| bits = (bits << 1) | ((m_bufReadBits >> (--m_numReadBits)) & 1); |
| } |
| |
| return bits; |
| } |
| |
| void MP4File::FlushReadBits() |
| { |
| // eat any remaining bits in the read buffer |
| m_numReadBits = 0; |
| } |
| |
| void MP4File::WriteBits(uint64_t bits, uint8_t numBits) |
| { |
| ASSERT(numBits <= 64); |
| |
| for (uint8_t i = numBits; i > 0; i--) { |
| m_bufWriteBits |= |
| (((bits >> (i - 1)) & 1) << (8 - ++m_numWriteBits)); |
| |
| if (m_numWriteBits == 8) { |
| FlushWriteBits(); |
| } |
| } |
| } |
| |
| void MP4File::PadWriteBits(uint8_t pad) |
| { |
| if (m_numWriteBits) { |
| WriteBits(pad ? 0xFF : 0x00, 8 - m_numWriteBits); |
| } |
| } |
| |
| void MP4File::FlushWriteBits() |
| { |
| if (m_numWriteBits > 0) { |
| WriteBytes(&m_bufWriteBits, 1); |
| m_numWriteBits = 0; |
| m_bufWriteBits = 0; |
| } |
| } |
| |
| uint32_t MP4File::ReadMpegLength() |
| { |
| uint32_t length = 0; |
| uint8_t numBytes = 0; |
| uint8_t b; |
| |
| do { |
| b = ReadUInt8(); |
| length = (length << 7) | (b & 0x7F); |
| numBytes++; |
| } while ((b & 0x80) && numBytes < 4); |
| |
| return length; |
| } |
| |
| void MP4File::WriteMpegLength(uint32_t value, bool compact) |
| { |
| if (value > 0x0FFFFFFF) { |
| ostringstream msg; |
| msg << "out of range: " << value; |
| throw new PlatformException(msg.str().c_str(), ERANGE, __FILE__, __LINE__, __FUNCTION__ ); |
| } |
| |
| int8_t numBytes; |
| |
| if (compact) { |
| if (value <= 0x7F) { |
| numBytes = 1; |
| } else if (value <= 0x3FFF) { |
| numBytes = 2; |
| } else if (value <= 0x1FFFFF) { |
| numBytes = 3; |
| } else { |
| numBytes = 4; |
| } |
| } else { |
| numBytes = 4; |
| } |
| |
| int8_t i = numBytes; |
| do { |
| i--; |
| uint8_t b = (value >> (i * 7)) & 0x7F; |
| if (i > 0) { |
| b |= 0x80; |
| } |
| WriteUInt8(b); |
| } while (i > 0); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| } |
| } // namespace mp4v2::impl |