@@ -26,13 +26,15 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
26
26
#include " H263plusVideoRTPSource.hh" // for the special header
27
27
#include " MPEG4GenericRTPSource.hh" // for "samplingFrequencyFromAudioSpecificConfig()"
28
28
#include " MPEG4LATMAudioRTPSource.hh" // for "parseGeneralConfigStr()"
29
+ #include " H264or5VideoStreamFramer.hh" // for "removeH264or5EmulationBytes()"
29
30
#include " Base64.hh"
30
31
31
32
#include < ctype.h>
32
33
33
34
#define fourChar (x,y,z,w ) ( ((x)<<24 )|((y)<<16 )|((z)<<8 )|(w) )
34
35
35
- #define H264_IDR_FRAME 0x65 // bit 8 == 0, bits 7-6 (ref) == 3, bits 5-0 (type) == 5
36
+ #define H264_IDR_FRAME 0x65 // bit 8 == 0, bits 7-6 (ref) == 3, bits 5-1 (type) == 5
37
+ #define isIDRFrame (firstByte ) (firstByte == H264_IDR_FRAME || ((firstByte&0x7E )>>1 ) == 19 || ((firstByte&0x7E )>>1 ) == 20 )
36
38
37
39
// //////// SubsessionIOState, ChunkDescriptor ///////////
38
40
// A structure used to represent the I/O state of each input 'subsession':
@@ -653,6 +655,10 @@ Boolean SubsessionIOState::setQTstate() {
653
655
fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_avc1;
654
656
fQTTimeScale = 600 ;
655
657
fQTTimeUnitsPerSample = fQTTimeScale /fOurSink .fMovieFPS ;
658
+ } else if (strcmp (fOurSubsession .codecName (), " H265" ) == 0 ) {
659
+ fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_hvc1;
660
+ fQTTimeScale = 600 ;
661
+ fQTTimeUnitsPerSample = fQTTimeScale /fOurSink .fMovieFPS ;
656
662
} else if (strcmp (fOurSubsession .codecName (), " MP4V-ES" ) == 0 ) {
657
663
fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_mp4v;
658
664
fQTTimeScale = 600 ;
@@ -801,15 +807,18 @@ void SubsessionIOState::useFrame(SubsessionBuffer& buffer) {
801
807
struct timeval const & presentationTime = buffer.presentationTime ();
802
808
int64_t const destFileOffset = TellFile64 (fOurSink .fOutFid );
803
809
unsigned sampleNumberOfFrameStart = fQTTotNumSamples + 1 ;
804
- Boolean avcHack = fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_avc1;
810
+ Boolean h264or5Hack =
811
+ fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_avc1 ||
812
+ fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_hvc1;
813
+
805
814
806
815
// If we're not syncing streams, or this subsession is not video, then
807
816
// just give this frame a fixed duration:
808
817
if (!fOurSink .fSyncStreams
809
818
|| fQTcomponentSubtype != fourChar (' v' ,' i' ,' d' ,' e' )) {
810
819
unsigned const frameDuration = fQTTimeUnitsPerSample *fQTSamplesPerFrame ;
811
820
unsigned frameSizeToUse = frameSize;
812
- if (avcHack ) frameSizeToUse += 4 ; // H.264/AVC gets the frame size prefix
821
+ if (h264or5Hack ) frameSizeToUse += 4 ; // H.264/5 gets the frame size prefix
813
822
814
823
fQTTotNumSamples += useFrame1 (frameSizeToUse, presentationTime, frameDuration, destFileOffset);
815
824
} else {
@@ -825,15 +834,15 @@ void SubsessionIOState::useFrame(SubsessionBuffer& buffer) {
825
834
unsigned frameDuration
826
835
= (unsigned )((2 *duration*fQTTimeScale +1 )/2 ); // round
827
836
unsigned frameSizeToUse = fPrevFrameState .frameSize ;
828
- if (avcHack ) frameSizeToUse += 4 ; // H.264/AVC gets the frame size prefix
837
+ if (h264or5Hack ) frameSizeToUse += 4 ; // H.264/5 gets the frame size prefix
829
838
830
839
unsigned numSamples
831
840
= useFrame1 (frameSizeToUse, ppt, frameDuration, fPrevFrameState .destFileOffset );
832
841
fQTTotNumSamples += numSamples;
833
842
sampleNumberOfFrameStart = fQTTotNumSamples + 1 ;
834
843
}
835
844
836
- if (avcHack && (*frameSource == H264_IDR_FRAME )) {
845
+ if (h264or5Hack && isIDRFrame (*frameSource)) {
837
846
SyncFrame* newSyncFrame = new SyncFrame (fQTTotNumSamples + 1 );
838
847
if (fTailSyncFrame == NULL ) {
839
848
fHeadSyncFrame = newSyncFrame;
@@ -849,7 +858,7 @@ void SubsessionIOState::useFrame(SubsessionBuffer& buffer) {
849
858
fPrevFrameState .destFileOffset = destFileOffset;
850
859
}
851
860
852
- if (avcHack ) fOurSink .addWord (frameSize);
861
+ if (h264or5Hack ) fOurSink .addWord (frameSize);
853
862
854
863
// Write the data into the file:
855
864
fwrite (frameSource, 1 , frameSize, fOurSink .fOutFid );
@@ -1119,7 +1128,7 @@ Boolean SubsessionIOState::syncOK(struct timeval presentationTime) {
1119
1128
1120
1129
// if audio is in sync, wait for the next IDR frame to start
1121
1130
unsigned char * const frameSource = fBuffer ->dataStart ();
1122
- if (*frameSource != H264_IDR_FRAME ) return False;
1131
+ if (! isIDRFrame ( *frameSource) ) return False;
1123
1132
}
1124
1133
// But now we are
1125
1134
fHaveBeenSynced = True;
@@ -1888,45 +1897,165 @@ addAtom(avc1);
1888
1897
addAtomEnd;
1889
1898
1890
1899
addAtom (avcC);
1891
- // Begin by Base-64 decoding the "sprop" parameter sets strings:
1900
+ // Begin by Base-64 decoding the "sprop" parameter sets strings:
1892
1901
char * psets = strDup (fCurrentIOState ->fOurSubsession .fmtp_spropparametersets ());
1893
1902
if (psets == NULL ) return 0 ;
1894
1903
1904
+ char const * spsBase64 = psets;
1895
1905
size_t comma_pos = strcspn (psets, " ," );
1896
1906
psets[comma_pos] = ' \0 ' ;
1897
- char const * sps_b64 = psets;
1898
- char const * pps_b64 = &psets[comma_pos+1 ];
1899
- unsigned sps_count;
1900
- unsigned char * sps_data = base64Decode (sps_b64, sps_count, false );
1901
- unsigned pps_count;
1902
- unsigned char * pps_data = base64Decode (pps_b64, pps_count, false );
1903
-
1904
- // Then add the decoded data:
1907
+
1908
+ char const * ppsBase64 = &psets[comma_pos+1 ];
1909
+
1910
+ unsigned spsSize;
1911
+ unsigned char * sps = base64Decode (spsBase64, spsSize, false );
1912
+
1913
+ unsigned ppsSize;
1914
+ unsigned char * pps = base64Decode (ppsBase64, ppsSize, false );
1915
+
1916
+ // We use some of the data from the "SPS". Remove any 'emulation bytes' from it first:
1917
+ if (spsSize == 0 ) return 0 ;
1918
+ u_int8_t * spsWEB = new u_int8_t [spsSize]; // "WEB" means "Without Emulation Bytes"
1919
+ unsigned spsWEBSize = removeH264or5EmulationBytes (spsWEB, spsSize, sps, spsSize);
1920
+ if (spsWEBSize < 4 ) { // Bad SPS size
1921
+ delete[] spsWEB;
1922
+ return 0 ;
1923
+ }
1924
+
1925
+ // Then add the decoded data:
1905
1926
size += addByte (0x01 ); // configuration version
1906
- size += addByte (sps_data [1 ]); // profile
1907
- size += addByte (sps_data [2 ]); // profile compat
1908
- size += addByte (sps_data [3 ]); // level
1927
+ size += addByte (spsWEB [1 ]); // profile
1928
+ size += addByte (spsWEB [2 ]); // profile compat
1929
+ size += addByte (spsWEB [3 ]); // level
1909
1930
size += addByte (0xff ); /* 0b11111100 | lengthsize = 0x11 */
1910
- size += addByte (0xe0 | (sps_count > 0 ? 1 : 0 ) );
1911
- if (sps_count > 0 ) {
1912
- size += addHalfWord (sps_count );
1913
- for (unsigned i = 0 ; i < sps_count ; i++) {
1914
- size += addByte (sps_data [i]);
1931
+ size += addByte (0xe0 | (spsSize > 0 ? 1 : 0 ) );
1932
+ if (spsSize > 0 ) {
1933
+ size += addHalfWord (spsSize );
1934
+ for (unsigned i = 0 ; i < spsSize ; i++) {
1935
+ size += addByte (sps [i]);
1915
1936
}
1916
1937
}
1917
- size += addByte (pps_count > 0 ? 1 : 0 );
1918
- if (pps_count > 0 ) {
1919
- size += addHalfWord (pps_count );
1920
- for (unsigned i = 0 ; i < pps_count ; i++) {
1921
- size += addByte (pps_data [i]);
1938
+ size += addByte (ppsSize > 0 ? 1 : 0 );
1939
+ if (ppsSize > 0 ) {
1940
+ size += addHalfWord (ppsSize );
1941
+ for (unsigned i = 0 ; i < ppsSize ; i++) {
1942
+ size += addByte (pps [i]);
1922
1943
}
1923
1944
}
1924
1945
1925
- // Finally, delete the data that we allocated:
1926
- delete[] pps_data; delete[] sps_data;
1946
+ // Finally, delete the data that we allocated:
1947
+ delete[] spsWEB;
1948
+ delete[] pps; delete[] sps;
1927
1949
delete[] psets;
1928
1950
addAtomEnd;
1929
1951
1952
+ addAtom (hvc1);
1953
+ // General sample description fields:
1954
+ size += addWord (0x00000000 ); // Reserved
1955
+ size += addWord (0x00000001 ); // Reserved+Data reference index
1956
+ // Video sample description fields:
1957
+ size += addWord (0x00000000 ); // Version+Revision level
1958
+ size += add4ByteString (" appl" ); // Vendor
1959
+ size += addWord (0x00000000 ); // Temporal quality
1960
+ size += addWord (0x00000000 ); // Spatial quality
1961
+ unsigned const widthAndHeight = (fMovieWidth <<16 )|fMovieHeight ;
1962
+ size += addWord (widthAndHeight); // Width+height
1963
+ size += addWord (0x00480000 ); // Horizontal resolution
1964
+ size += addWord (0x00480000 ); // Vertical resolution
1965
+ size += addWord (0x00000000 ); // Data size
1966
+ size += addWord (0x00010548 ); // Frame count+Compressor name (start)
1967
+ // "H.265"
1968
+ size += addWord (0x2e323635 ); // Compressor name (continued)
1969
+ size += addZeroWords (6 ); // Compressor name (continued - zero)
1970
+ size += addWord (0x00000018 ); // Compressor name (final)+Depth
1971
+ size += addHalfWord (0xffff ); // Color table id
1972
+ size += addAtom_hvcC ();
1973
+ addAtomEnd;
1974
+
1975
+ addAtom (hvcC);
1976
+ // Begin by Base-64 decoding the "sprop" parameter sets strings:
1977
+ char const * vpsBase64 = strDup (fCurrentIOState ->fOurSubsession .fmtp_spropvps ());
1978
+ char const * spsBase64 = strDup (fCurrentIOState ->fOurSubsession .fmtp_spropsps ());
1979
+ char const * ppsBase64 = strDup (fCurrentIOState ->fOurSubsession .fmtp_sproppps ());
1980
+ if (vpsBase64 == NULL || spsBase64 == NULL || ppsBase64 == NULL ) return 0 ;
1981
+
1982
+ unsigned vpsSize;
1983
+ unsigned char * vps = base64Decode (vpsBase64, vpsSize, false );
1984
+
1985
+ unsigned spsSize;
1986
+ unsigned char * sps = base64Decode (spsBase64, spsSize, false );
1987
+
1988
+ unsigned ppsSize;
1989
+ unsigned char * pps = base64Decode (ppsBase64, ppsSize, false );
1990
+
1991
+ // We use some of the data from the "VPS". Remove any 'emulation bytes' from it first:
1992
+ if (vpsSize == 0 ) return 0 ;
1993
+ u_int8_t * vpsWEB = new u_int8_t [vpsSize]; // "WEB" means "Without Emulation Bytes"
1994
+ unsigned vpsWEBSize = removeH264or5EmulationBytes (vpsWEB, vpsSize, vps, vpsSize);
1995
+ if (vpsWEBSize < 6 /* 'profile_tier_level' offset*/ + 12 /* num 'profile_tier_level' bytes*/ ) {
1996
+ // Bad VPS size
1997
+ delete[] vpsWEB;
1998
+ return 0 ;
1999
+ }
2000
+
2001
+ // Then add the decoded data:
2002
+ size += addByte (0x01 ); // configurationVersion = 1
2003
+
2004
+ u_int8_t const * profileTierLevelHeaderBytes = &vpsWEB[6 ];
2005
+ size += addByte (profileTierLevelHeaderBytes[0 ]);
2006
+ // general_profile_space + general_tier_flag + general_profile_idc
2007
+
2008
+ u_int8_t const * general_profile_compatibility_flags = &profileTierLevelHeaderBytes[1 ];
2009
+ for (unsigned i = 0 ; i < 4 ; ++i) size += addByte (general_profile_compatibility_flags[i]);
2010
+ // general_profile_compatibility_flags
2011
+
2012
+ u_int8_t const * general_constraint_indicator_flags = &profileTierLevelHeaderBytes[5 ];
2013
+ for (unsigned i = 0 ; i < 6 ; ++i) size += addByte (general_constraint_indicator_flags[i]);
2014
+ // general_constraint_indicator_flags
2015
+
2016
+ size += addByte (profileTierLevelHeaderBytes[11 ]); // general_level_idc
2017
+ size += addHalfWord (0xF000 ); // min_spatial_segmentation_idc = 0 ???
2018
+ size += addByte (0xFC ); // parallelismType = 0 ???
2019
+ size += addByte (0xFD ); // chroma_format_idc = 1 ???
2020
+ size += addByte (0xF8 ); // bit_depth_luma_minus8 = 0 ???
2021
+ size += addByte (0xF8 ); // bit_depth_chroma_minus8 = 0 ???
2022
+ size += addHalfWord (0x0000 ); // avgFrameRate = 0
2023
+ size += addByte (0x0F );
2024
+ // constantFrameRate = 0; numTemporalLayers = 1; temporalIdNested = 1; lengthSizeMinusOne = 3 ???
2025
+ size += addByte (1 + (spsSize > 0 ) + (ppsSize > 0 )); // numOfArrays
2026
+
2027
+ if (vpsSize > 0 ) {
2028
+ size += addByte (0x20 ); // array_completeness = 0; NAL_unit_type = VPS
2029
+ size += addHalfWord (1 ); // numNalus
2030
+ size += addHalfWord (vpsSize);
2031
+ for (unsigned i = 0 ; i < vpsSize; i++) {
2032
+ size += addByte (vps[i]);
2033
+ }
2034
+ }
2035
+
2036
+ if (spsSize > 0 ) {
2037
+ size += addByte (0x21 ); // array_completeness = 0; NAL_unit_type = SPS
2038
+ size += addHalfWord (1 ); // numNalus
2039
+ size += addHalfWord (spsSize);
2040
+ for (unsigned i = 0 ; i < spsSize; i++) {
2041
+ size += addByte (sps[i]);
2042
+ }
2043
+ }
2044
+
2045
+ if (ppsSize > 0 ) {
2046
+ size += addByte (0x22 ); // array_completeness = 0; NAL_unit_type = PPS
2047
+ size += addHalfWord (1 ); // numNalus
2048
+ size += addHalfWord (ppsSize);
2049
+ for (unsigned i = 0 ; i < ppsSize; i++) {
2050
+ size += addByte (pps[i]);
2051
+ }
2052
+ }
2053
+
2054
+ // Finally, delete the data that we allocated:
2055
+ delete[] vpsWEB;
2056
+ delete[] vps; delete[] pps; delete[] sps;
2057
+ addAtomEnd;
2058
+
1930
2059
addAtom (mp4v);
1931
2060
// General sample description fields:
1932
2061
size += addWord (0x00000000 ); // Reserved
0 commit comments