diff --git a/CMakeLists.txt b/CMakeLists.txt index 94a6b7e..a7112d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -217,6 +217,9 @@ set(MP4V2_SOURCE_FILES src/atom_amr.cpp src/atom_avc1.cpp src/atom_avcC.cpp + src/atom_hvc1.cpp + src/atom_hev1.cpp + src/atom_hvcC.cpp src/atom_chpl.cpp src/atom_colr.cpp src/atom_d263.cpp diff --git a/include/mp4v2/track.h b/include/mp4v2/track.h index 8686004..1f58c7c 100644 --- a/include/mp4v2/track.h +++ b/include/mp4v2/track.h @@ -296,6 +296,53 @@ void MP4AddH264PictureParameterSet( const uint8_t* pPict, uint16_t pictLen ); +/** Add an HEVC (H265) video track. + * + * This function adds an HEVC video track to the file. The track uses + * the 'hvc1' sample entry type. + * + * @param hFile handle of file to add track to. + * @param timeScale the timescale of the track. + * @param sampleDuration the default sample duration. + * @param width the video width in pixels. + * @param height the video height in pixels. + * @param general_profile_idc the HEVC profile indicator. + * @param general_level_idc the HEVC level indicator. + * @param sampleLenFieldSizeMinusOne the size of the NAL unit length + * field minus one. Valid values are 0, 1, and 3, corresponding + * to NAL unit lengths of 1, 2, and 4 bytes. + * + * @return the track ID of the new track, or MP4_INVALID_TRACK_ID on failure. + */ +MP4V2_EXPORT +MP4TrackId MP4AddHEVCVideoTrack( + MP4FileHandle hFile, + uint32_t timeScale, + MP4Duration sampleDuration, + uint16_t width, + uint16_t height, + uint8_t general_profile_idc, + uint8_t general_level_idc, + uint8_t sampleLenFieldSizeMinusOne ); + +/** Add an HEVC (H265) video track with hev1 sample entry. + * + * Similar to MP4AddHEVCVideoTrack but uses 'hev1' sample entry type. + * Use hev1 when parameter sets may appear in-band (within samples). + * + * @see MP4AddHEVCVideoTrack for parameter documentation. + */ +MP4V2_EXPORT +MP4TrackId MP4AddHEV1VideoTrack( + MP4FileHandle hFile, + uint32_t timeScale, + MP4Duration sampleDuration, + uint16_t width, + uint16_t height, + uint8_t general_profile_idc, + uint8_t general_level_idc, + uint8_t sampleLenFieldSizeMinusOne ); + MP4V2_EXPORT void MP4SetH263Vendor( MP4FileHandle hFile, diff --git a/libplatform/config.h.cmake b/libplatform/config.h.cmake index 49bacbe..8ad5ade 100644 --- a/libplatform/config.h.cmake +++ b/libplatform/config.h.cmake @@ -26,7 +26,7 @@ builds, but we use MP4V2_EXPORTS instead. */ #ifdef DLL_EXPORT # define MP4V2_EXPORTS -#else +#elif !defined(MP4V2_USE_STATIC_LIB) # define MP4V2_USE_STATIC_LIB #endif diff --git a/src/atom_hev1.cpp b/src/atom_hev1.cpp new file mode 100644 index 0000000..82b8ec3 --- /dev/null +++ b/src/atom_hev1.cpp @@ -0,0 +1,86 @@ +/* + * 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. + * + * Contributor(s): + * Chema Gonzalez + */ + +#include "src/impl.h" + +namespace mp4v2 { +namespace impl { + +/////////////////////////////////////////////////////////////////////////////// + +MP4Hev1Atom::MP4Hev1Atom(MP4File &file) + : MP4Atom(file, "hev1") +{ + AddReserved(*this, "reserved1", 6); /* 0 */ + + AddProperty( /* 1 */ + new MP4Integer16Property(*this, "dataReferenceIndex")); + + AddReserved(*this, "reserved2", 16); /* 2 */ + + AddProperty( /* 3 */ + new MP4Integer16Property(*this, "width")); + AddProperty( /* 4 */ + new MP4Integer16Property(*this, "height")); + + AddReserved(*this, "reserved3", 14); /* 5 */ + + MP4StringProperty* pProp = + new MP4StringProperty(*this, "compressorName"); + pProp->SetFixedLength(32); + pProp->SetCountedFormat(true); + pProp->SetValue("HEVC Coding"); + AddProperty(pProp); /* 6 */ + + AddReserved(*this, "reserved4", 4); /* 7 */ + + ExpectChildAtom("hvcC", Required, OnlyOne); + ExpectChildAtom("btrt", Optional, OnlyOne); + ExpectChildAtom("colr", Optional, OnlyOne); + ExpectChildAtom("pasp", Optional, OnlyOne); +} + +void MP4Hev1Atom::Generate() +{ + MP4Atom::Generate(); + + ((MP4Integer16Property*)m_pProperties[1])->SetValue(1); + + // property reserved3 has non-zero fixed values + static uint8_t reserved3[14] = { + 0x00, 0x48, 0x00, 0x00, + 0x00, 0x48, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + }; + m_pProperties[5]->SetReadOnly(false); + ((MP4BytesProperty*)m_pProperties[5])-> + SetValue(reserved3, sizeof(reserved3)); + m_pProperties[5]->SetReadOnly(true); + + // property reserved4 has non-zero fixed values + static uint8_t reserved4[4] = { + 0x00, 0x18, 0xFF, 0xFF, + }; + m_pProperties[7]->SetReadOnly(false); + ((MP4BytesProperty*)m_pProperties[7])-> + SetValue(reserved4, sizeof(reserved4)); + m_pProperties[7]->SetReadOnly(true); +} + +/////////////////////////////////////////////////////////////////////////////// + +} +} // namespace mp4v2::impl diff --git a/src/atom_hvc1.cpp b/src/atom_hvc1.cpp new file mode 100644 index 0000000..757d267 --- /dev/null +++ b/src/atom_hvc1.cpp @@ -0,0 +1,86 @@ +/* + * 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. + * + * Contributor(s): + * Chema Gonzalez + */ + +#include "src/impl.h" + +namespace mp4v2 { +namespace impl { + +/////////////////////////////////////////////////////////////////////////////// + +MP4Hvc1Atom::MP4Hvc1Atom(MP4File &file) + : MP4Atom(file, "hvc1") +{ + AddReserved(*this, "reserved1", 6); /* 0 */ + + AddProperty( /* 1 */ + new MP4Integer16Property(*this, "dataReferenceIndex")); + + AddReserved(*this, "reserved2", 16); /* 2 */ + + AddProperty( /* 3 */ + new MP4Integer16Property(*this, "width")); + AddProperty( /* 4 */ + new MP4Integer16Property(*this, "height")); + + AddReserved(*this, "reserved3", 14); /* 5 */ + + MP4StringProperty* pProp = + new MP4StringProperty(*this, "compressorName"); + pProp->SetFixedLength(32); + pProp->SetCountedFormat(true); + pProp->SetValue("HEVC Coding"); + AddProperty(pProp); /* 6 */ + + AddReserved(*this, "reserved4", 4); /* 7 */ + + ExpectChildAtom("hvcC", Required, OnlyOne); + ExpectChildAtom("btrt", Optional, OnlyOne); + ExpectChildAtom("colr", Optional, OnlyOne); + ExpectChildAtom("pasp", Optional, OnlyOne); +} + +void MP4Hvc1Atom::Generate() +{ + MP4Atom::Generate(); + + ((MP4Integer16Property*)m_pProperties[1])->SetValue(1); + + // property reserved3 has non-zero fixed values + static uint8_t reserved3[14] = { + 0x00, 0x48, 0x00, 0x00, + 0x00, 0x48, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + }; + m_pProperties[5]->SetReadOnly(false); + ((MP4BytesProperty*)m_pProperties[5])-> + SetValue(reserved3, sizeof(reserved3)); + m_pProperties[5]->SetReadOnly(true); + + // property reserved4 has non-zero fixed values + static uint8_t reserved4[4] = { + 0x00, 0x18, 0xFF, 0xFF, + }; + m_pProperties[7]->SetReadOnly(false); + ((MP4BytesProperty*)m_pProperties[7])-> + SetValue(reserved4, sizeof(reserved4)); + m_pProperties[7]->SetReadOnly(true); +} + +/////////////////////////////////////////////////////////////////////////////// + +} +} // namespace mp4v2::impl diff --git a/src/atom_hvcC.cpp b/src/atom_hvcC.cpp new file mode 100644 index 0000000..6ea5cec --- /dev/null +++ b/src/atom_hvcC.cpp @@ -0,0 +1,324 @@ +/* + * 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. + * + * Contributor(s): + * Chema Gonzalez + */ + +#include "src/impl.h" + +namespace mp4v2 { +namespace impl { + +/////////////////////////////////////////////////////////////////////////////// + +/* + * HvcCSizeTableProperty is a special version of the MP4TableProperty - + * the BytesProperty will need to set the value before it can read + * from the file. This is similar to SizeTableProperty in atom_avcC.cpp. + */ +class HvcCSizeTableProperty : public MP4TableProperty +{ +public: + HvcCSizeTableProperty(MP4Atom& parentAtom, const char *name, MP4IntegerProperty *pCountProperty) : + MP4TableProperty(parentAtom, name, pCountProperty) {}; +protected: + void ReadEntry(MP4File& file, uint32_t index) { + // Each table entry has a size (16-bit), followed by the NAL unit data + // first, read the length + m_pProperties[0]->Read(file, index); + MP4IntegerProperty *pIntProp = (MP4IntegerProperty *)m_pProperties[0]; + // set the size in the bytes property + MP4BytesProperty *pBytesProp = (MP4BytesProperty *)m_pProperties[1]; + pBytesProp->SetValueSize(pIntProp->GetValue(index), index); + // And read the bytes + m_pProperties[1]->Read(file, index); + }; +private: + HvcCSizeTableProperty(); + HvcCSizeTableProperty ( const HvcCSizeTableProperty &src ); + HvcCSizeTableProperty &operator= ( const HvcCSizeTableProperty &src ); +}; + +/* + * HvcCNalArrayProperty represents one NAL unit array in the hvcC atom. + * Each array has: + * - array_completeness (1 bit) + reserved (1 bit) + NAL_unit_type (6 bits) + * - numNalus (16 bits) + * - For each NAL: nalUnitLength (16 bits) + nalUnit (bytes) + */ +class HvcCNalArrayProperty : public MP4TableProperty +{ +public: + HvcCNalArrayProperty(MP4Atom& parentAtom, const char *name, MP4IntegerProperty *pCountProperty) : + MP4TableProperty(parentAtom, name, pCountProperty) {}; +protected: + void ReadEntry(MP4File& file, uint32_t index); +private: + HvcCNalArrayProperty(); + HvcCNalArrayProperty ( const HvcCNalArrayProperty &src ); + HvcCNalArrayProperty &operator= ( const HvcCNalArrayProperty &src ); +}; + +void HvcCNalArrayProperty::ReadEntry(MP4File& file, uint32_t index) +{ + // Read the array header (1 byte: completeness + type) + m_pProperties[0]->Read(file, index); + + // Read the NAL count (16 bits) + m_pProperties[1]->Read(file, index); + MP4Integer16Property *pCountProp = (MP4Integer16Property *)m_pProperties[1]; + uint16_t nalCount = pCountProp->GetValue(index); + + // The NAL units themselves are in nested tables, but for simplicity + // we'll store them as opaque data that includes all NAL units for this array + // This is handled by the parent atom's custom read logic + + // For now, just read the remaining properties + for (uint32_t i = 2; i < m_pProperties.Size(); i++) { + m_pProperties[i]->Read(file, index); + } +} + +MP4HvcCAtom::MP4HvcCAtom(MP4File &file) + : MP4Atom(file, "hvcC") +{ + // Configuration version (always 1) + AddProperty( new MP4Integer8Property(*this, "configurationVersion")); /* 0 */ + + // general_profile_space (2 bits) + general_tier_flag (1 bit) + general_profile_idc (5 bits) + AddProperty( new MP4BitfieldProperty(*this, "general_profile_space", 2)); /* 1 */ + AddProperty( new MP4BitfieldProperty(*this, "general_tier_flag", 1)); /* 2 */ + AddProperty( new MP4BitfieldProperty(*this, "general_profile_idc", 5)); /* 3 */ + + // general_profile_compatibility_flags (32 bits) + AddProperty( new MP4Integer32Property(*this, "general_profile_compatibility_flags")); /* 4 */ + + // general_constraint_indicator_flags (48 bits = 6 bytes) + AddProperty( new MP4BytesProperty(*this, "general_constraint_indicator_flags", 6)); /* 5 */ + + // general_level_idc (8 bits) + AddProperty( new MP4Integer8Property(*this, "general_level_idc")); /* 6 */ + + // reserved (4 bits, 0xF) + min_spatial_segmentation_idc (12 bits) + AddProperty( new MP4BitfieldProperty(*this, "reserved1", 4)); /* 7 */ + AddProperty( new MP4BitfieldProperty(*this, "min_spatial_segmentation_idc", 12)); /* 8 */ + + // reserved (6 bits, 0x3F) + parallelismType (2 bits) + AddProperty( new MP4BitfieldProperty(*this, "reserved2", 6)); /* 9 */ + AddProperty( new MP4BitfieldProperty(*this, "parallelismType", 2)); /* 10 */ + + // reserved (6 bits, 0x3F) + chromaFormat (2 bits) + AddProperty( new MP4BitfieldProperty(*this, "reserved3", 6)); /* 11 */ + AddProperty( new MP4BitfieldProperty(*this, "chromaFormat", 2)); /* 12 */ + + // reserved (5 bits, 0x1F) + bitDepthLumaMinus8 (3 bits) + AddProperty( new MP4BitfieldProperty(*this, "reserved4", 5)); /* 13 */ + AddProperty( new MP4BitfieldProperty(*this, "bitDepthLumaMinus8", 3)); /* 14 */ + + // reserved (5 bits, 0x1F) + bitDepthChromaMinus8 (3 bits) + AddProperty( new MP4BitfieldProperty(*this, "reserved5", 5)); /* 15 */ + AddProperty( new MP4BitfieldProperty(*this, "bitDepthChromaMinus8", 3)); /* 16 */ + + // avgFrameRate (16 bits) + AddProperty( new MP4Integer16Property(*this, "avgFrameRate")); /* 17 */ + + // constantFrameRate (2 bits) + numTemporalLayers (3 bits) + + // temporalIdNested (1 bit) + lengthSizeMinusOne (2 bits) + AddProperty( new MP4BitfieldProperty(*this, "constantFrameRate", 2)); /* 18 */ + AddProperty( new MP4BitfieldProperty(*this, "numTemporalLayers", 3)); /* 19 */ + AddProperty( new MP4BitfieldProperty(*this, "temporalIdNested", 1)); /* 20 */ + AddProperty( new MP4BitfieldProperty(*this, "lengthSizeMinusOne", 2)); /* 21 */ + + // numOfArrays (8 bits) - count of NAL unit arrays + AddProperty( new MP4Integer8Property(*this, "numOfArrays")); /* 22 */ + + // The NAL unit arrays are complex: each array has a type and contains + // multiple NAL units. For simplicity, we store the raw bytes after numOfArrays + // and provide helper functions to parse/build them. + // This approach is simpler than deeply nested table properties. + AddProperty( new MP4BytesProperty(*this, "nalArraysData")); /* 23 */ +} + +void MP4HvcCAtom::Generate() +{ + MP4Atom::Generate(); + + // configurationVersion = 1 + ((MP4Integer8Property*)m_pProperties[0])->SetValue(1); + + // Set reserved fields to their required values + m_pProperties[7]->SetReadOnly(false); + ((MP4BitfieldProperty*)m_pProperties[7])->SetValue(0xF); // reserved1 + m_pProperties[7]->SetReadOnly(true); + + m_pProperties[9]->SetReadOnly(false); + ((MP4BitfieldProperty*)m_pProperties[9])->SetValue(0x3F); // reserved2 + m_pProperties[9]->SetReadOnly(true); + + m_pProperties[11]->SetReadOnly(false); + ((MP4BitfieldProperty*)m_pProperties[11])->SetValue(0x3F); // reserved3 + m_pProperties[11]->SetReadOnly(true); + + m_pProperties[13]->SetReadOnly(false); + ((MP4BitfieldProperty*)m_pProperties[13])->SetValue(0x1F); // reserved4 + m_pProperties[13]->SetReadOnly(true); + + m_pProperties[15]->SetReadOnly(false); + ((MP4BitfieldProperty*)m_pProperties[15])->SetValue(0x1F); // reserved5 + m_pProperties[15]->SetReadOnly(true); + + // lengthSizeMinusOne defaults to 3 (4-byte NAL length fields) + ((MP4BitfieldProperty*)m_pProperties[21])->SetValue(3); + + // numOfArrays defaults to 0 + ((MP4Integer8Property*)m_pProperties[22])->SetValue(0); +} + +void MP4HvcCAtom::Read() +{ + // Read all fixed properties first + ReadProperties(0, 23); + + // Get the number of NAL arrays + uint8_t numArrays = ((MP4Integer8Property*)m_pProperties[22])->GetValue(); + + if (numArrays > 0) { + // Calculate how many bytes remain in the atom for NAL array data + uint64_t remainingBytes = GetEnd() - m_File.GetPosition(); + + if (remainingBytes > 0) { + // Read all NAL array data as opaque bytes + MP4BytesProperty* pNalData = (MP4BytesProperty*)m_pProperties[23]; + pNalData->SetValueSize(remainingBytes); + pNalData->Read(m_File); + } + } + + Skip(); // Skip any remaining data +} + +// +// Clone - clone my properties to destination atom +// +void MP4HvcCAtom::Clone(MP4HvcCAtom *dstAtom) +{ + // Start with defaults + dstAtom->Generate(); + + // Copy non-reserved properties + // 0: configurationVersion (skip, set by Generate) + + // 1-3: profile_space, tier_flag, profile_idc + MP4Property *dstProperty; + + dstProperty = dstAtom->GetProperty(1); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[1])->GetValue()); + + dstProperty = dstAtom->GetProperty(2); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[2])->GetValue()); + + dstProperty = dstAtom->GetProperty(3); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[3])->GetValue()); + + // 4: profile_compatibility_flags + dstProperty = dstAtom->GetProperty(4); + ((MP4Integer32Property *)dstProperty)->SetValue( + ((MP4Integer32Property *)m_pProperties[4])->GetValue()); + + // 5: constraint_indicator_flags (6 bytes) + dstProperty = dstAtom->GetProperty(5); + uint8_t constraintFlags[6]; + ((MP4BytesProperty *)m_pProperties[5])->CopyValue(constraintFlags, 0); + ((MP4BytesProperty *)dstProperty)->SetValue(constraintFlags, 6); + + // 6: level_idc + dstProperty = dstAtom->GetProperty(6); + ((MP4Integer8Property *)dstProperty)->SetValue( + ((MP4Integer8Property *)m_pProperties[6])->GetValue()); + + // 7-8: reserved1, min_spatial_segmentation_idc + dstProperty = dstAtom->GetProperty(8); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[8])->GetValue()); + + // 9-10: reserved2, parallelismType + dstProperty = dstAtom->GetProperty(10); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[10])->GetValue()); + + // 11-12: reserved3, chromaFormat + dstProperty = dstAtom->GetProperty(12); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[12])->GetValue()); + + // 13-14: reserved4, bitDepthLumaMinus8 + dstProperty = dstAtom->GetProperty(14); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[14])->GetValue()); + + // 15-16: reserved5, bitDepthChromaMinus8 + dstProperty = dstAtom->GetProperty(16); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[16])->GetValue()); + + // 17: avgFrameRate + dstProperty = dstAtom->GetProperty(17); + ((MP4Integer16Property *)dstProperty)->SetValue( + ((MP4Integer16Property *)m_pProperties[17])->GetValue()); + + // 18-21: constantFrameRate, numTemporalLayers, temporalIdNested, lengthSizeMinusOne + dstProperty = dstAtom->GetProperty(18); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[18])->GetValue()); + + dstProperty = dstAtom->GetProperty(19); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[19])->GetValue()); + + dstProperty = dstAtom->GetProperty(20); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[20])->GetValue()); + + dstProperty = dstAtom->GetProperty(21); + ((MP4BitfieldProperty *)dstProperty)->SetValue( + ((MP4BitfieldProperty *)m_pProperties[21])->GetValue()); + + // 22: numOfArrays + dstProperty = dstAtom->GetProperty(22); + dstProperty->SetReadOnly(false); + ((MP4Integer8Property *)dstProperty)->SetValue( + ((MP4Integer8Property *)m_pProperties[22])->GetValue()); + dstProperty->SetReadOnly(true); + + // 23: nalArraysData (copy all NAL unit arrays) + MP4BytesProperty* srcNalData = (MP4BytesProperty*)m_pProperties[23]; + MP4BytesProperty* dstNalData = (MP4BytesProperty*)dstAtom->GetProperty(23); + + uint32_t nalDataSize = 0; + uint8_t* nalDataValue = nullptr; + srcNalData->GetValue(&nalDataValue, &nalDataSize); + + if (nalDataValue != nullptr && nalDataSize > 0) { + uint8_t* tmp = (uint8_t*)MP4Malloc(nalDataSize); + memcpy(tmp, nalDataValue, nalDataSize); + dstNalData->SetValue(tmp, nalDataSize); + MP4Free(tmp); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +} +} // namespace mp4v2::impl diff --git a/src/atom_stsd.cpp b/src/atom_stsd.cpp index c83ff39..c9e68b7 100644 --- a/src/atom_stsd.cpp +++ b/src/atom_stsd.cpp @@ -58,6 +58,8 @@ MP4StsdAtom::MP4StsdAtom(MP4File &file) ExpectChildAtom("sawb", Optional, Many); // For AMR-WB ExpectChildAtom("s263", Optional, Many); // For H.263 ExpectChildAtom("avc1", Optional, Many); + ExpectChildAtom("hvc1", Optional, Many); + ExpectChildAtom("hev1", Optional, Many); ExpectChildAtom("alac", Optional, Many); ExpectChildAtom("text", Optional, Many); ExpectChildAtom("tx3g", Optional, Many); diff --git a/src/atoms.h b/src/atoms.h index 382706f..3d66246 100644 --- a/src/atoms.h +++ b/src/atoms.h @@ -178,6 +178,40 @@ class MP4AvcCAtom : public MP4Atom { MP4AvcCAtom &operator= ( const MP4AvcCAtom &src ); }; +// H265/HEVC atoms + +class MP4Hvc1Atom : public MP4Atom { +public: + MP4Hvc1Atom(MP4File &file); + void Generate(); +private: + MP4Hvc1Atom(); + MP4Hvc1Atom( const MP4Hvc1Atom &src ); + MP4Hvc1Atom &operator= ( const MP4Hvc1Atom &src ); +}; + +class MP4Hev1Atom : public MP4Atom { +public: + MP4Hev1Atom(MP4File &file); + void Generate(); +private: + MP4Hev1Atom(); + MP4Hev1Atom( const MP4Hev1Atom &src ); + MP4Hev1Atom &operator= ( const MP4Hev1Atom &src ); +}; + +class MP4HvcCAtom : public MP4Atom { +public: + MP4HvcCAtom(MP4File &file); + void Generate(); + void Read(); + void Clone(MP4HvcCAtom *dstAtom); +private: + MP4HvcCAtom(); + MP4HvcCAtom( const MP4HvcCAtom &src ); + MP4HvcCAtom &operator= ( const MP4HvcCAtom &src ); +}; + class MP4D263Atom : public MP4Atom { public: diff --git a/src/mp4.cpp b/src/mp4.cpp index c56f3ee..6eebc3a 100644 --- a/src/mp4.cpp +++ b/src/mp4.cpp @@ -1273,6 +1273,70 @@ MP4FileHandle MP4ModifyCallbacks(const MP4IOCallbacks* callbacks, return MP4_INVALID_TRACK_ID; } + MP4TrackId MP4AddHEVCVideoTrack(MP4FileHandle hFile, + uint32_t timeScale, + MP4Duration sampleDuration, + uint16_t width, + uint16_t height, + uint8_t general_profile_idc, + uint8_t general_level_idc, + uint8_t sampleLenFieldSizeMinusOne) + { + if (MP4_IS_VALID_FILE_HANDLE(hFile)) { + try { + MP4File *pFile = (MP4File *)hFile; + + return pFile->AddHEVCVideoTrack(timeScale, + sampleDuration, + width, + height, + general_profile_idc, + general_level_idc, + sampleLenFieldSizeMinusOne); + } + catch( Exception* x ) { + mp4v2::impl::log.errorf(*x); + delete x; + } + catch( ... ) { + mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ ); + } + } + return MP4_INVALID_TRACK_ID; + } + + MP4TrackId MP4AddHEV1VideoTrack(MP4FileHandle hFile, + uint32_t timeScale, + MP4Duration sampleDuration, + uint16_t width, + uint16_t height, + uint8_t general_profile_idc, + uint8_t general_level_idc, + uint8_t sampleLenFieldSizeMinusOne) + { + if (MP4_IS_VALID_FILE_HANDLE(hFile)) { + try { + MP4File *pFile = (MP4File *)hFile; + + return pFile->AddHEV1VideoTrack(timeScale, + sampleDuration, + width, + height, + general_profile_idc, + general_level_idc, + sampleLenFieldSizeMinusOne); + } + catch( Exception* x ) { + mp4v2::impl::log.errorf(*x); + delete x; + } + catch( ... ) { + mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ ); + } + } + return MP4_INVALID_TRACK_ID; + } + MP4TrackId MP4AddEncH264VideoTrack( MP4FileHandle hFile, uint32_t timeScale, diff --git a/src/mp4atom.cpp b/src/mp4atom.cpp index 999fa3e..31e6119 100644 --- a/src/mp4atom.cpp +++ b/src/mp4atom.cpp @@ -892,6 +892,12 @@ MP4Atom::factory( MP4File &file, MP4Atom* parent, const char* type ) return new MP4VideoAtom( file, type ); if( ATOMID(type) == ATOMID("href") ) return new MP4HrefAtom(file); + if( ATOMID(type) == ATOMID("hvc1") ) + return new MP4Hvc1Atom(file); + if( ATOMID(type) == ATOMID("hev1") ) + return new MP4Hev1Atom(file); + if( ATOMID(type) == ATOMID("hvcC") ) + return new MP4HvcCAtom(file); break; case 'i': diff --git a/src/mp4file.cpp b/src/mp4file.cpp index 4e43903..3d2c056 100644 --- a/src/mp4file.cpp +++ b/src/mp4file.cpp @@ -2023,6 +2023,75 @@ MP4TrackId MP4File::AddEncH264VideoTrack( } +// HEVC/H265 track creation + +MP4TrackId MP4File::AddHEVCVideoTrack( + uint32_t timeScale, + MP4Duration sampleDuration, + uint16_t width, + uint16_t height, + uint8_t general_profile_idc, + uint8_t general_level_idc, + uint8_t sampleLenFieldSizeMinusOne) +{ + MP4TrackId trackId = AddVideoTrackDefault(timeScale, + sampleDuration, + width, + height, + "hvc1"); + + SetTrackIntegerProperty(trackId, + "mdia.minf.stbl.stsd.hvc1.width", width); + SetTrackIntegerProperty(trackId, + "mdia.minf.stbl.stsd.hvc1.height", height); + + SetTrackIntegerProperty(trackId, + "mdia.minf.stbl.stsd.hvc1.hvcC.general_profile_idc", + general_profile_idc); + SetTrackIntegerProperty(trackId, + "mdia.minf.stbl.stsd.hvc1.hvcC.general_level_idc", + general_level_idc); + SetTrackIntegerProperty(trackId, + "mdia.minf.stbl.stsd.hvc1.hvcC.lengthSizeMinusOne", + sampleLenFieldSizeMinusOne); + + return trackId; +} + +MP4TrackId MP4File::AddHEV1VideoTrack( + uint32_t timeScale, + MP4Duration sampleDuration, + uint16_t width, + uint16_t height, + uint8_t general_profile_idc, + uint8_t general_level_idc, + uint8_t sampleLenFieldSizeMinusOne) +{ + MP4TrackId trackId = AddVideoTrackDefault(timeScale, + sampleDuration, + width, + height, + "hev1"); + + SetTrackIntegerProperty(trackId, + "mdia.minf.stbl.stsd.hev1.width", width); + SetTrackIntegerProperty(trackId, + "mdia.minf.stbl.stsd.hev1.height", height); + + SetTrackIntegerProperty(trackId, + "mdia.minf.stbl.stsd.hev1.hvcC.general_profile_idc", + general_profile_idc); + SetTrackIntegerProperty(trackId, + "mdia.minf.stbl.stsd.hev1.hvcC.general_level_idc", + general_level_idc); + SetTrackIntegerProperty(trackId, + "mdia.minf.stbl.stsd.hev1.hvcC.lengthSizeMinusOne", + sampleLenFieldSizeMinusOne); + + return trackId; +} + + void MP4File::AddH264SequenceParameterSet (MP4TrackId trackId, const uint8_t *pSequence, uint16_t sequenceLen) diff --git a/src/mp4file.h b/src/mp4file.h index a1a2eb4..ce8c18d 100644 --- a/src/mp4file.h +++ b/src/mp4file.h @@ -377,6 +377,26 @@ class MP4File void AddH264PictureParameterSet(MP4TrackId trackId, const uint8_t *pPicture, uint16_t pictureLen); + + // HEVC/H265 track creation + MP4TrackId AddHEVCVideoTrack( + uint32_t timeScale, + MP4Duration sampleDuration, + uint16_t width, + uint16_t height, + uint8_t general_profile_idc, + uint8_t general_level_idc, + uint8_t sampleLenFieldSizeMinusOne); + + MP4TrackId AddHEV1VideoTrack( + uint32_t timeScale, + MP4Duration sampleDuration, + uint16_t width, + uint16_t height, + uint8_t general_profile_idc, + uint8_t general_level_idc, + uint8_t sampleLenFieldSizeMinusOne); + MP4TrackId AddHintTrack(MP4TrackId refTrackId); MP4TrackId AddTextTrack(MP4TrackId refTrackId);