blob: 601adef0efb623531de14cc2cc0c0996c9d9405f [file] [log] [blame]
/*
* 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
* Kona Blend kona8lend@@gmail.com
*/
#include "src/impl.h"
namespace mp4v2 { namespace impl {
///////////////////////////////////////////////////////////////////////////////
MP4Property::MP4Property(MP4Atom& parentAtom, const char* name)
: m_parentAtom(parentAtom)
{
m_name = name;
m_readOnly = false;
m_implicit = false;
}
bool MP4Property::FindProperty(const char* name,
MP4Property** ppProperty, uint32_t* pIndex)
{
if (name == NULL) {
return false;
}
if (!strcasecmp(m_name, name)) {
log.verbose1f("\"%s\": FindProperty: matched %s",
m_parentAtom.GetFile().GetFilename().c_str(), name);
*ppProperty = this;
return true;
}
return false;
}
// Integer Property
uint64_t MP4IntegerProperty::GetValue(uint32_t index)
{
switch (this->GetType()) {
case Integer8Property:
return ((MP4Integer8Property*)this)->GetValue(index);
case Integer16Property:
return ((MP4Integer16Property*)this)->GetValue(index);
case Integer24Property:
return ((MP4Integer24Property*)this)->GetValue(index);
case Integer32Property:
return ((MP4Integer32Property*)this)->GetValue(index);
case Integer64Property:
return ((MP4Integer64Property*)this)->GetValue(index);
default:
ASSERT(false);
}
return (0);
}
void MP4IntegerProperty::SetValue(uint64_t value, uint32_t index)
{
switch (this->GetType()) {
case Integer8Property:
((MP4Integer8Property*)this)->SetValue(value, index);
break;
case Integer16Property:
((MP4Integer16Property*)this)->SetValue(value, index);
break;
case Integer24Property:
((MP4Integer24Property*)this)->SetValue(value, index);
break;
case Integer32Property:
((MP4Integer32Property*)this)->SetValue(value, index);
break;
case Integer64Property:
((MP4Integer64Property*)this)->SetValue(value, index);
break;
default:
ASSERT(false);
}
}
void MP4IntegerProperty::InsertValue(uint64_t value, uint32_t index)
{
switch (this->GetType()) {
case Integer8Property:
((MP4Integer8Property*)this)->InsertValue(value, index);
break;
case Integer16Property:
((MP4Integer16Property*)this)->InsertValue(value, index);
break;
case Integer24Property:
((MP4Integer24Property*)this)->InsertValue(value, index);
break;
case Integer32Property:
((MP4Integer32Property*)this)->InsertValue(value, index);
break;
case Integer64Property:
((MP4Integer64Property*)this)->InsertValue(value, index);
break;
default:
ASSERT(false);
}
}
void MP4IntegerProperty::DeleteValue(uint32_t index)
{
switch (this->GetType()) {
case Integer8Property:
((MP4Integer8Property*)this)->DeleteValue(index);
break;
case Integer16Property:
((MP4Integer16Property*)this)->DeleteValue(index);
break;
case Integer24Property:
((MP4Integer24Property*)this)->DeleteValue(index);
break;
case Integer32Property:
((MP4Integer32Property*)this)->DeleteValue(index);
break;
case Integer64Property:
((MP4Integer64Property*)this)->DeleteValue(index);
break;
default:
ASSERT(false);
}
}
void MP4IntegerProperty::IncrementValue(int32_t increment, uint32_t index)
{
SetValue(GetValue() + increment);
}
void MP4Integer8Property::Dump(uint8_t indent,
bool dumpImplicits, uint32_t index)
{
if (m_implicit && !dumpImplicits) {
return;
}
if (index != 0)
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%02x)",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, index, m_values[index], m_values[index]);
else
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%02x)",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, m_values[index], m_values[index]);
}
void MP4Integer16Property::Dump(uint8_t indent,
bool dumpImplicits, uint32_t index)
{
if (m_implicit && !dumpImplicits) {
return;
}
if (index != 0)
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%04x)",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, index, m_values[index], m_values[index]);
else
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%04x)",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, m_values[index], m_values[index]);
}
void MP4Integer24Property::Dump(uint8_t indent,
bool dumpImplicits, uint32_t index)
{
if (m_implicit && !dumpImplicits) {
return;
}
if (index != 0)
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%06x)",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, index, m_values[index], m_values[index]);
else
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%06x)",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, m_values[index], m_values[index]);
}
void MP4Integer32Property::Dump(uint8_t indent,
bool dumpImplicits, uint32_t index)
{
if (m_implicit && !dumpImplicits) {
return;
}
if (index != 0)
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%08x)",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, index, m_values[index], m_values[index]);
else
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%08x)",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, m_values[index], m_values[index]);
}
void MP4Integer64Property::Dump(uint8_t indent,
bool dumpImplicits, uint32_t index)
{
if (m_implicit && !dumpImplicits) {
return;
}
if (index != 0)
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %" PRIu64 " (0x%016" PRIx64 ")",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, index, m_values[index], m_values[index]);
else
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %" PRIu64 " (0x%016" PRIx64 ")",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, m_values[index], m_values[index]);
}
// MP4BitfieldProperty
void MP4BitfieldProperty::Read(MP4File& file, uint32_t index)
{
if (m_implicit) {
return;
}
m_values[index] = file.ReadBits(m_numBits);
}
void MP4BitfieldProperty::Write(MP4File& file, uint32_t index)
{
if (m_implicit) {
return;
}
file.WriteBits(m_values[index], m_numBits);
}
void MP4BitfieldProperty::Dump(uint8_t indent,
bool dumpImplicits, uint32_t index)
{
if (m_implicit && !dumpImplicits) {
return;
}
uint8_t hexWidth = m_numBits / 4;
if (hexWidth == 0 || (m_numBits % 4)) {
hexWidth++;
}
if (index != 0)
log.dump(indent, MP4_LOG_VERBOSE1,
"\"%s\": %s[%u] = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits>",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, index, m_values[index], (int)hexWidth, m_values[index], m_numBits);
else
log.dump(indent, MP4_LOG_VERBOSE1,
"\"%s\": %s = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits>",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, m_values[index], (int)hexWidth, m_values[index], m_numBits);
}
// MP4Float32Property
void MP4Float32Property::Read(MP4File& file, uint32_t index)
{
if (m_implicit) {
return;
}
if (m_useFixed16Format) {
m_values[index] = file.ReadFixed16();
} else if (m_useFixed32Format) {
m_values[index] = file.ReadFixed32();
} else {
m_values[index] = file.ReadFloat();
}
}
void MP4Float32Property::Write(MP4File& file, uint32_t index)
{
if (m_implicit) {
return;
}
if (m_useFixed16Format) {
file.WriteFixed16(m_values[index]);
} else if (m_useFixed32Format) {
file.WriteFixed32(m_values[index]);
} else {
file.WriteFloat(m_values[index]);
}
}
void MP4Float32Property::Dump(uint8_t indent,
bool dumpImplicits, uint32_t index)
{
if (m_implicit && !dumpImplicits) {
return;
}
if (index != 0)
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %f",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, index, m_values[index]);
else
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %f",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, m_values[index]);
}
// MP4StringProperty
MP4StringProperty::MP4StringProperty(
MP4Atom& parentAtom,
const char* name,
bool useCountedFormat,
bool useUnicode,
bool arrayMode )
: MP4Property( parentAtom, name )
, m_arrayMode ( arrayMode )
, m_useCountedFormat ( useCountedFormat )
, m_useExpandedCount ( false )
, m_useUnicode ( useUnicode )
, m_fixedLength ( 0 )
{
SetCount( 1 );
m_values[0] = NULL;
}
MP4StringProperty::~MP4StringProperty()
{
uint32_t count = GetCount();
for (uint32_t i = 0; i < count; i++) {
MP4Free(m_values[i]);
}
}
void MP4StringProperty::SetCount(uint32_t count)
{
uint32_t oldCount = m_values.Size();
m_values.Resize(count);
for (uint32_t i = oldCount; i < count; i++) {
m_values[i] = NULL;
}
}
void MP4StringProperty::SetValue(const char* value, uint32_t index)
{
if (m_readOnly) {
ostringstream msg;
msg << "property " << m_name << "is read-only";
throw new PlatformException(msg.str().c_str(), EACCES, __FILE__, __LINE__, __FUNCTION__ );
}
MP4Free(m_values[index]);
if (m_fixedLength) {
m_values[index] = (char*)MP4Calloc(m_fixedLength + 1);
if (value) {
strncpy(m_values[index], value, m_fixedLength);
}
} else {
if (value) {
m_values[index] = MP4Stralloc(value);
} else {
m_values[index] = NULL;
}
}
}
void MP4StringProperty::Read( MP4File& file, uint32_t index )
{
if( m_implicit )
return;
uint32_t begin = index;
uint32_t max = index + 1;
if( m_arrayMode ) {
begin = 0;
max = GetCount();
}
for( uint32_t i = begin; i < max; i++ ) {
char*& value = m_values[i];
// Generally a default atom setting, e.g. see atom_avc1.cpp, "JVT/AVC Coding"; we'll leak this string if
// we don't free. Note that MP4Free checks for null.
MP4Free(value);
if( m_useCountedFormat ) {
value = file.ReadCountedString( (m_useUnicode ? 2 : 1), m_useExpandedCount, m_fixedLength );
}
else if( m_fixedLength ) {
value = (char*)MP4Calloc( m_fixedLength + 1 );
file.ReadBytes( (uint8_t*)value, m_fixedLength );
}
else {
value = file.ReadString();
}
}
}
void MP4StringProperty::Write( MP4File& file, uint32_t index )
{
if( m_implicit )
return;
uint32_t begin = index;
uint32_t max = index + 1;
if( m_arrayMode ) {
begin = 0;
max = GetCount();
}
for( uint32_t i = begin; i < max; i++ ) {
char*& value = m_values[i];
if( m_useCountedFormat ) {
file.WriteCountedString( value, (m_useUnicode ? 2 : 1), m_useExpandedCount, m_fixedLength );
}
else if( m_fixedLength ) {
file.WriteBytes( (uint8_t*)value, m_fixedLength );
}
else {
file.WriteString( value );
}
}
}
void MP4StringProperty::Dump( uint8_t indent, bool dumpImplicits, uint32_t index )
{
if( m_implicit && !dumpImplicits )
return;
if( !m_arrayMode ) {
char indexd[32];
if( index != 0 )
snprintf( indexd, 32, "[%u]", index );
else
indexd[0] = '\0';
if( m_useUnicode )
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s%s = %ls",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, indexd, (wchar_t*)m_values[index] );
else
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s%s = %s",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, indexd, m_values[index] );
}
else if( log.verbosity >= MP4_LOG_VERBOSE2 )
{
const uint32_t max = GetCount();
log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s (size=%u)",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, max );
for( uint32_t i = 0; i < max; i++ ) {
char*& value = m_values[i];
if( m_useUnicode )
log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s[%u] = %ls",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, i, (wchar_t*)value );
else
log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s[%u] = %s",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, i, value );
}
}
else {
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": <table entries suppressed>",
m_parentAtom.GetFile().GetFilename().c_str() );
}
}
// MP4BytesProperty
MP4BytesProperty::MP4BytesProperty(MP4Atom& parentAtom, const char* name, uint32_t valueSize,
uint32_t defaultValueSize)
: MP4Property(parentAtom, name)
, m_fixedValueSize(0)
, m_defaultValueSize(defaultValueSize)
{
SetCount(1);
m_values[0] = (uint8_t*)MP4Calloc(valueSize);
m_valueSizes[0] = valueSize;
}
MP4BytesProperty::~MP4BytesProperty()
{
uint32_t count = GetCount();
for (uint32_t i = 0; i < count; i++) {
MP4Free(m_values[i]);
}
}
void MP4BytesProperty::SetCount(uint32_t count)
{
uint32_t oldCount = m_values.Size();
m_values.Resize(count);
m_valueSizes.Resize(count);
for (uint32_t i = oldCount; i < count; i++) {
m_values[i] = NULL;
m_valueSizes[i] = m_defaultValueSize;
}
}
void MP4BytesProperty::SetValue(const uint8_t* pValue, uint32_t valueSize,
uint32_t index)
{
if (m_readOnly) {
ostringstream msg;
msg << "property " << m_name << "is read-only";
throw new PlatformException(msg.str().c_str(), EACCES, __FILE__, __LINE__, __FUNCTION__ );
}
if (m_fixedValueSize) {
if (valueSize > m_fixedValueSize) {
ostringstream msg;
msg << GetParentAtom().GetType() << "." << GetName() << " value size " << valueSize << " exceeds fixed value size " << m_fixedValueSize;
throw new Exception(msg.str().c_str(), __FILE__, __LINE__, __FUNCTION__ );
}
if (m_values[index] == NULL) {
m_values[index] = (uint8_t*)MP4Calloc(m_fixedValueSize);
m_valueSizes[index] = m_fixedValueSize;
}
if (pValue) {
memcpy(m_values[index], pValue, valueSize);
}
} else {
MP4Free(m_values[index]);
if (pValue) {
m_values[index] = (uint8_t*)MP4Malloc(valueSize);
memcpy(m_values[index], pValue, valueSize);
m_valueSizes[index] = valueSize;
} else {
m_values[index] = NULL;
m_valueSizes[index] = 0;
}
}
}
void MP4BytesProperty::SetValueSize(uint32_t valueSize, uint32_t index)
{
if (m_fixedValueSize) {
throw new Exception("can't change size of fixed sized property",
__FILE__, __LINE__, __FUNCTION__ );
}
if (m_values[index] != NULL) {
m_values[index] = (uint8_t*)MP4Realloc(m_values[index], valueSize);
}
m_valueSizes[index] = valueSize;
}
void MP4BytesProperty::SetFixedSize(uint32_t fixedSize)
{
m_fixedValueSize = 0;
for (uint32_t i = 0; i < GetCount(); i++) {
SetValueSize(fixedSize, i);
}
m_fixedValueSize = fixedSize;
}
void MP4BytesProperty::Read(MP4File& file, uint32_t index)
{
if (m_implicit) {
return;
}
MP4Free(m_values[index]);
m_values[index] = (uint8_t*)MP4Malloc(m_valueSizes[index]);
file.ReadBytes(m_values[index], m_valueSizes[index]);
}
void MP4BytesProperty::Write(MP4File& file, uint32_t index)
{
if (m_implicit) {
return;
}
file.WriteBytes(m_values[index], m_valueSizes[index]);
}
void MP4BytesProperty::Dump(uint8_t indent,
bool dumpImplicits, uint32_t index)
{
if( m_implicit && !dumpImplicits )
return;
const uint32_t size = m_valueSizes[index];
const uint8_t* const value = m_values[index];
if( size == 0 ) {
log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = <%u bytes>",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, size );
return;
}
if( size <= 16 ) {
ostringstream oss;
ostringstream text;
oss << " ";
for( uint32_t i = 0; i < size; i++ ) {
if( i )
oss << ' ';
oss << std::hex << std::setw(2) << std::setfill('0') << right
<< static_cast<uint32_t>(value[i]);
text << (isprint( static_cast<int>(value[i]) ) ? static_cast<char>(value[i]) : '.');
}
oss << " |" << text.str() << "|";
log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = <%u bytes>%s",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, size, oss.str().c_str() );
return;
}
// specialization for ilst item data always show all bytes except for covr
bool showall = false;
MP4Atom* const datac = m_parentAtom.GetParentAtom(); // data container
MP4Atom* const datacc = datac->GetParentAtom();
if( datacc &&
ATOMID( datacc->GetType() ) == ATOMID( "ilst" ) &&
ATOMID( datac->GetType() ) != ATOMID( "covr" ) )
{
showall = true;
}
uint32_t adjsize;
bool supressed;
if( showall ||
size < 128 || log.verbosity >= MP4_LOG_VERBOSE2 )
{
adjsize = size;
supressed = false;
}
else {
adjsize = 128;
supressed = true;
}
ostringstream oss;
ostringstream text;
log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = <%u bytes>",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, size );
log.hexDump(indent, MP4_LOG_VERBOSE2, value, adjsize, "\"%s\": %s",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name);
if( supressed ) {
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": <remaining bytes supressed>",
m_parentAtom.GetFile().GetFilename().c_str() );
}
}
// MP4TableProperty
MP4TableProperty::MP4TableProperty(MP4Atom& parentAtom, const char* name, MP4IntegerProperty* pCountProperty)
: MP4Property(parentAtom, name)
{
m_pCountProperty = pCountProperty;
m_pCountProperty->SetReadOnly();
}
MP4TableProperty::~MP4TableProperty()
{
for (uint32_t i = 0; i < m_pProperties.Size(); i++) {
delete m_pProperties[i];
}
}
void MP4TableProperty::AddProperty(MP4Property* pProperty)
{
ASSERT(pProperty);
ASSERT(pProperty->GetType() != TableProperty);
ASSERT(pProperty->GetType() != DescriptorProperty);
m_pProperties.Add(pProperty);
pProperty->SetCount(0);
}
bool MP4TableProperty::FindProperty(const char *name,
MP4Property** ppProperty, uint32_t* pIndex)
{
ASSERT(m_name);
// check if first component of name matches ourselves
if (!MP4NameFirstMatches(m_name, name)) {
return false;
}
// check if the specified table entry exists
uint32_t index;
bool haveIndex = MP4NameFirstIndex(name, &index);
if (haveIndex) {
if (index >= GetCount()) {
return false;
}
if (pIndex) {
*pIndex = index;
}
}
log.verbose1f("\"%s\": FindProperty: matched %s",
m_parentAtom.GetFile().GetFilename().c_str(), name);
// get name of table property
const char *tablePropName = MP4NameAfterFirst(name);
if (tablePropName == NULL) {
if (!haveIndex) {
*ppProperty = this;
return true;
}
return false;
}
// check if this table property exists
return FindContainedProperty(tablePropName, ppProperty, pIndex);
}
bool MP4TableProperty::FindContainedProperty(const char *name,
MP4Property** ppProperty, uint32_t* pIndex)
{
uint32_t numProperties = m_pProperties.Size();
for (uint32_t i = 0; i < numProperties; i++) {
if (m_pProperties[i]->FindProperty(name, ppProperty, pIndex)) {
return true;
}
}
return false;
}
void MP4TableProperty::Read(MP4File& file, uint32_t index)
{
ASSERT(index == 0);
if (m_implicit) {
return;
}
uint32_t numProperties = m_pProperties.Size();
if (numProperties == 0) {
WARNING(numProperties == 0);
return;
}
uint32_t numEntries = GetCount();
/* for each property set size */
for (uint32_t j = 0; j < numProperties; j++) {
m_pProperties[j]->SetCount(numEntries);
}
for (uint32_t i = 0; i < numEntries; i++) {
ReadEntry(file, i);
}
}
void MP4TableProperty::ReadEntry(MP4File& file, uint32_t index)
{
for (uint32_t j = 0; j < m_pProperties.Size(); j++) {
m_pProperties[j]->Read(file, index);
}
}
void MP4TableProperty::Write(MP4File& file, uint32_t index)
{
ASSERT(index == 0);
if (m_implicit) {
return;
}
uint32_t numProperties = m_pProperties.Size();
if (numProperties == 0) {
WARNING(numProperties == 0);
return;
}
uint32_t numEntries = GetCount();
if (m_pProperties[0]->GetCount() != numEntries) {
log.errorf("%s: \"%s\": %s %s \"%s\"table entries %u doesn't match count %u",
__FUNCTION__, m_parentAtom.GetFile().GetFilename().c_str(),
GetParentAtom().GetType(),
GetName(), m_pProperties[0]->GetName(),
m_pProperties[0]->GetCount(), numEntries);
ASSERT(m_pProperties[0]->GetCount() == numEntries);
}
for (uint32_t i = 0; i < numEntries; i++) {
WriteEntry(file, i);
}
}
void MP4TableProperty::WriteEntry(MP4File& file, uint32_t index)
{
for (uint32_t j = 0; j < m_pProperties.Size(); j++) {
m_pProperties[j]->Write(file, index);
}
}
void MP4TableProperty::Dump(uint8_t indent,
bool dumpImplicits, uint32_t index)
{
ASSERT(index == 0);
// implicit tables just can't be dumped
if (m_implicit) {
return;
}
uint32_t numProperties = m_pProperties.Size();
if (numProperties == 0) {
WARNING(numProperties == 0);
return;
}
uint32_t numEntries = GetCount();
for (uint32_t i = 0; i < numEntries; i++) {
for (uint32_t j = 0; j < numProperties; j++) {
m_pProperties[j]->Dump(indent + 1, dumpImplicits, i);
}
}
}
// MP4DescriptorProperty
MP4DescriptorProperty::MP4DescriptorProperty(MP4Atom& parentAtom, const char* name,
uint8_t tagsStart, uint8_t tagsEnd, bool mandatory, bool onlyOne)
: MP4Property(parentAtom, name)
{
SetTags(tagsStart, tagsEnd);
m_sizeLimit = 0;
m_mandatory = mandatory;
m_onlyOne = onlyOne;
}
MP4DescriptorProperty::~MP4DescriptorProperty()
{
for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
delete m_pDescriptors[i];
}
}
MP4Descriptor* MP4DescriptorProperty::AddDescriptor(uint8_t tag)
{
// check that tag is in expected range
ASSERT(tag >= m_tagsStart && tag <= m_tagsEnd);
MP4Descriptor* pDescriptor = CreateDescriptor(m_parentAtom, tag);
ASSERT(pDescriptor);
m_pDescriptors.Add(pDescriptor);
return pDescriptor;
}
void MP4DescriptorProperty::DeleteDescriptor(uint32_t index)
{
delete m_pDescriptors[index];
m_pDescriptors.Delete(index);
}
void MP4DescriptorProperty::Generate()
{
// generate a default descriptor
// if it is mandatory, and single
if (m_mandatory && m_onlyOne) {
MP4Descriptor* pDescriptor =
AddDescriptor(m_tagsStart);
pDescriptor->Generate();
}
}
bool MP4DescriptorProperty::FindProperty(const char *name,
MP4Property** ppProperty, uint32_t* pIndex)
{
// we're unnamed, so just check contained properties
if (m_name == NULL || !strcmp(m_name, "")) {
return FindContainedProperty(name, ppProperty, pIndex);
}
// check if first component of name matches ourselves
if (!MP4NameFirstMatches(m_name, name)) {
return false;
}
// check if the specific descriptor entry exists
uint32_t descrIndex;
bool haveDescrIndex = MP4NameFirstIndex(name, &descrIndex);
if (haveDescrIndex && descrIndex >= GetCount()) {
return false;
}
log.verbose1f("\"%s\": matched %s",
m_parentAtom.GetFile().GetFilename().c_str(),
name);
// get name of descriptor property
name = MP4NameAfterFirst(name);
if (name == NULL) {
if (!haveDescrIndex) {
*ppProperty = this;
return true;
}
return false;
}
/* check rest of name */
if (haveDescrIndex) {
return m_pDescriptors[descrIndex]->FindProperty(name,
ppProperty, pIndex);
} else {
return FindContainedProperty(name, ppProperty, pIndex);
}
}
bool MP4DescriptorProperty::FindContainedProperty(const char *name,
MP4Property** ppProperty, uint32_t* pIndex)
{
for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
if (m_pDescriptors[i]->FindProperty(name, ppProperty, pIndex)) {
return true;
}
}
return false;
}
void MP4DescriptorProperty::Read(MP4File& file, uint32_t index)
{
ASSERT(index == 0);
if (m_implicit) {
return;
}
uint64_t start = file.GetPosition();
while (true) {
// enforce size limitation
if (m_sizeLimit && file.GetPosition() >= start + m_sizeLimit) {
break;
}
uint8_t tag;
try {
file.PeekBytes(&tag, 1);
}
catch (Exception* x) {
if (file.GetPosition() >= file.GetSize()) {
// EOF
delete x;
break;
}
throw x;
}
// check if tag is in desired range
if (tag < m_tagsStart || tag > m_tagsEnd) {
break;
}
MP4Descriptor* pDescriptor =
AddDescriptor(tag);
pDescriptor->Read(file);
}
// warnings
if (m_mandatory && m_pDescriptors.Size() == 0) {
log.warningf("%s: \"%s\": Mandatory descriptor 0x%02x missing",
__FUNCTION__, GetParentAtom().GetFile().GetFilename().c_str(), m_tagsStart);
} else if (m_onlyOne && m_pDescriptors.Size() > 1) {
log.warningf("%s: \"%s\": Descriptor 0x%02x has more than one instance",
__FUNCTION__, GetParentAtom().GetFile().GetFilename().c_str(), m_tagsStart);
}
}
void MP4DescriptorProperty::Write(MP4File& file, uint32_t index)
{
ASSERT(index == 0);
if (m_implicit) {
return;
}
for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
m_pDescriptors[i]->Write(file);
}
}
void MP4DescriptorProperty::Dump(uint8_t indent,
bool dumpImplicits, uint32_t index)
{
ASSERT(index == 0);
if (m_implicit && !dumpImplicits) {
return;
}
if (m_name) {
if (index != 0)
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u]",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, index);
else
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name);
indent++;
}
for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
m_pDescriptors[i]->Dump(indent, dumpImplicits);
}
}
///////////////////////////////////////////////////////////////////////////////
MP4LanguageCodeProperty::MP4LanguageCodeProperty( MP4Atom& parentAtom, const char* name, bmff::LanguageCode value )
: MP4Property( parentAtom, name )
{
SetValue( value );
}
MP4LanguageCodeProperty::MP4LanguageCodeProperty( MP4Atom& parentAtom, const char* name, const std::string& code )
: MP4Property( parentAtom, name )
{
SetValue( bmff::enumLanguageCode.toType( code ));
}
void
MP4LanguageCodeProperty::Dump( uint8_t indent, bool dumpImplicits, uint32_t index )
{
uint16_t data = 0;
std::string svalue;
bmff::enumLanguageCode.toString( _value, svalue );
if( svalue.length() == 3 ) {
data = (((svalue[0] - 0x60) & 0x001f) << 10)
| (((svalue[1] - 0x60) & 0x001f) << 5)
| (((svalue[2] - 0x60) & 0x001f) );
}
log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = %s (0x%04x)",
m_parentAtom.GetFile().GetFilename().c_str(),
m_name, bmff::enumLanguageCode.toString( _value, true ).c_str(), data );
}
uint32_t
MP4LanguageCodeProperty::GetCount()
{
return 1;
}
MP4PropertyType
MP4LanguageCodeProperty::GetType()
{
return LanguageCodeProperty;
}
bmff::LanguageCode
MP4LanguageCodeProperty::GetValue()
{
return _value;
}
void
MP4LanguageCodeProperty::Read( MP4File& file, uint32_t index )
{
uint16_t data = file.ReadBits( 16 );
char code[3];
code[0] = ((data & 0x7c00) >> 10) + 0x60;
code[1] = ((data & 0x03e0) >> 5) + 0x60;
code[2] = ((data & 0x001f) ) + 0x60;
SetValue( bmff::enumLanguageCode.toType( std::string( code, sizeof(code) )));
}
void
MP4LanguageCodeProperty::SetCount( uint32_t count )
{
// do nothing; count is always 1
}
void
MP4LanguageCodeProperty::SetValue( bmff::LanguageCode value )
{
_value = value;
}
void
MP4LanguageCodeProperty::Write( MP4File& file, uint32_t index )
{
uint16_t data = 0;
std::string svalue;
bmff::enumLanguageCode.toString( _value, svalue );
if( svalue.length() == 3 ) {
data = (((svalue[0] - 0x60) & 0x001f) << 10)
| (((svalue[1] - 0x60) & 0x001f) << 5)
| (((svalue[2] - 0x60) & 0x001f) );
}
file.WriteBits( data, 16 );
}
///////////////////////////////////////////////////////////////////////////////
MP4BasicTypeProperty::MP4BasicTypeProperty( MP4Atom& parentAtom, const char* name, itmf::BasicType type )
: MP4Property( parentAtom, name )
{
SetValue( type );
}
void
MP4BasicTypeProperty::Dump( uint8_t indent, bool dumpImplicits, uint32_t index )
{
log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %s (0x%02x)",
m_parentAtom.GetFile().GetFilename().c_str(), m_name,
itmf::enumBasicType.toString( _value, true ).c_str(), _value );
}
uint32_t
MP4BasicTypeProperty::GetCount()
{
return 1;
}
MP4PropertyType
MP4BasicTypeProperty::GetType()
{
return BasicTypeProperty;
}
itmf::BasicType
MP4BasicTypeProperty::GetValue()
{
return _value;
}
void
MP4BasicTypeProperty::Read( MP4File& file, uint32_t index )
{
SetValue( static_cast<itmf::BasicType>( file.ReadBits( 8 )));
}
void
MP4BasicTypeProperty::SetCount( uint32_t count )
{
// do nothing; count is always 1
}
void
MP4BasicTypeProperty::SetValue( itmf::BasicType value )
{
_value = value;
}
void
MP4BasicTypeProperty::Write( MP4File& file, uint32_t index )
{
file.WriteBits( _value, 8 );
}
///////////////////////////////////////////////////////////////////////////////
}} // namespace mp4v2::impl