@ -3,6 +3,7 @@ package fmp4
@@ -3,6 +3,7 @@ package fmp4
import (
gomp4 "github.com/abema/go-mp4"
"github.com/aler9/gortsplib/v2/pkg/codecs/h264"
"github.com/aler9/gortsplib/v2/pkg/codecs/h265"
"github.com/aler9/gortsplib/v2/pkg/format"
)
@ -46,24 +47,30 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
@@ -46,24 +47,30 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
return err
}
var sps [ ] byte
var pps [ ] byte
var spsp h264 . SPS
var h264SPS [ ] byte
var h264PPS [ ] byte
var h264SPSP h264 . SPS
var h265VPS [ ] byte
var h265SPS [ ] byte
var h265PPS [ ] byte
var h265SPSP h265 . SPS
var width int
var height int
switch ttrack := track . Format . ( type ) {
case * format . H264 :
sps = ttrack . SafeSPS ( )
pps = ttrack . SafePPS ( )
h264SPS = ttrack . SafeSPS ( )
h264PPS = ttrack . SafePPS ( )
err = spsp . Unmarshal ( sps )
err = h264SPSP . Unmarshal ( h264SPS )
if err != nil {
return err
}
width = spsp . Width ( )
height = spsp . Height ( )
width = h264SPSP . Width ( )
height = h264SPSP . Height ( )
_ , err = w . WriteBox ( & gomp4 . Tkhd { // <tkhd/>
FullBox : gomp4 . FullBox {
@ -72,7 +79,33 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
@@ -72,7 +79,33 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
TrackID : uint32 ( track . ID ) ,
Width : uint32 ( width * 65536 ) ,
Height : uint32 ( height * 65536 ) ,
Matrix : [ 9 ] int32 { 0x00010000 , 0 , 0 , 0 , 0x00010000 , 0 , 0 , 0 , 0x40000000 } ,
Matrix : [ 9 ] int32 { 0x10000 , 0 , 0 , 0 , 0x10000 , 0 , 0 , 0 , 0x40000000 } ,
} )
if err != nil {
return err
}
case * format . H265 :
h265VPS = ttrack . SafeVPS ( )
h265SPS = ttrack . SafeSPS ( )
h265PPS = ttrack . SafePPS ( )
err = h265SPSP . Unmarshal ( h265SPS )
if err != nil {
return err
}
width = h265SPSP . Width ( )
height = h265SPSP . Height ( )
_ , err = w . WriteBox ( & gomp4 . Tkhd { // <tkhd/>
FullBox : gomp4 . FullBox {
Flags : [ 3 ] byte { 0 , 0 , 3 } ,
} ,
TrackID : uint32 ( track . ID ) ,
Width : uint32 ( width * 65536 ) ,
Height : uint32 ( height * 65536 ) ,
Matrix : [ 9 ] int32 { 0x10000 , 0 , 0 , 0 , 0x10000 , 0 , 0 , 0 , 0x40000000 } ,
} )
if err != nil {
return err
@ -86,7 +119,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
@@ -86,7 +119,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
TrackID : uint32 ( track . ID ) ,
AlternateGroup : 1 ,
Volume : 256 ,
Matrix : [ 9 ] int32 { 0x000 10000 , 0 , 0 , 0 , 0x000 10000 , 0 , 0 , 0 , 0x40000000 } ,
Matrix : [ 9 ] int32 { 0x10000 , 0 , 0 , 0 , 0x10000 , 0 , 0 , 0 , 0x40000000 } ,
} )
if err != nil {
return err
@ -107,7 +140,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
@@ -107,7 +140,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
}
switch track . Format . ( type ) {
case * format . H264 :
case * format . H264 , * format . H265 :
_ , err = w . WriteBox ( & gomp4 . Hdlr { // <hdlr/>
HandlerType : [ 4 ] byte { 'v' , 'i' , 'd' , 'e' } ,
Name : "VideoHandler" ,
@ -132,7 +165,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
@@ -132,7 +165,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
}
switch track . Format . ( type ) {
case * format . H264 :
case * format . H264 , * format . H265 :
_ , err = w . WriteBox ( & gomp4 . Vmhd { // <vmhd/>
FullBox : gomp4 . FullBox {
Flags : [ 3 ] byte { 0 , 0 , 1 } ,
@ -219,22 +252,22 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
@@ -219,22 +252,22 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
Type : gomp4 . BoxTypeAvcC ( ) ,
} ,
ConfigurationVersion : 1 ,
Profile : spsp . ProfileIdc ,
ProfileCompatibility : sps [ 2 ] ,
Level : spsp . LevelIdc ,
Profile : h264SPSP . ProfileIdc ,
ProfileCompatibility : h264SPS [ 2 ] ,
Level : h264SPSP . LevelIdc ,
LengthSizeMinusOne : 3 ,
NumOfSequenceParameterSets : 1 ,
SequenceParameterSets : [ ] gomp4 . AVCParameterSet {
{
Length : uint16 ( len ( sps ) ) ,
NALUnit : sps ,
Length : uint16 ( len ( h264SPS ) ) ,
NALUnit : h264SPS ,
} ,
} ,
NumOfPictureParameterSets : 1 ,
PictureParameterSets : [ ] gomp4 . AVCParameterSet {
{
Length : uint16 ( len ( pps ) ) ,
NALUnit : pps ,
Length : uint16 ( len ( h264PPS ) ) ,
NALUnit : h264PPS ,
} ,
} ,
} )
@ -255,6 +288,90 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
@@ -255,6 +288,90 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
return err
}
case * format . H265 :
_ , err = w . writeBoxStart ( & gomp4 . VisualSampleEntry { // <hev1>
SampleEntry : gomp4 . SampleEntry {
AnyTypeBox : gomp4 . AnyTypeBox {
Type : gomp4 . BoxTypeHev1 ( ) ,
} ,
DataReferenceIndex : 1 ,
} ,
Width : uint16 ( width ) ,
Height : uint16 ( height ) ,
Horizresolution : 4718592 ,
Vertresolution : 4718592 ,
FrameCount : 1 ,
Depth : 24 ,
PreDefined3 : - 1 ,
} )
if err != nil {
return err
}
_ , err = w . WriteBox ( & gomp4 . HvcC { // <hvcC/>
ConfigurationVersion : 1 ,
GeneralProfileIdc : h265SPSP . ProfileTierLevel . GeneralProfileIdc ,
GeneralProfileCompatibility : h265SPSP . ProfileTierLevel . GeneralProfileCompatibilityFlag ,
GeneralConstraintIndicator : [ 6 ] uint8 {
h265SPS [ 7 ] , h265SPS [ 8 ] , h265SPS [ 9 ] ,
h265SPS [ 10 ] , h265SPS [ 11 ] , h265SPS [ 12 ] ,
} ,
GeneralLevelIdc : h265SPSP . ProfileTierLevel . GeneralLevelIdc ,
// MinSpatialSegmentationIdc
// ParallelismType
ChromaFormatIdc : uint8 ( h265SPSP . ChromaFormatIdc ) ,
BitDepthLumaMinus8 : uint8 ( h265SPSP . BitDepthLumaMinus8 ) ,
BitDepthChromaMinus8 : uint8 ( h265SPSP . BitDepthChromaMinus8 ) ,
// AvgFrameRate
// ConstantFrameRate
NumTemporalLayers : 1 ,
// TemporalIdNested
LengthSizeMinusOne : 3 ,
NumOfNaluArrays : 3 ,
NaluArrays : [ ] gomp4 . HEVCNaluArray {
{
NaluType : byte ( h265 . NALUType_VPS_NUT ) ,
NumNalus : 1 ,
Nalus : [ ] gomp4 . HEVCNalu { {
Length : uint16 ( len ( h265VPS ) ) ,
NALUnit : h265VPS ,
} } ,
} ,
{
NaluType : byte ( h265 . NALUType_SPS_NUT ) ,
NumNalus : 1 ,
Nalus : [ ] gomp4 . HEVCNalu { {
Length : uint16 ( len ( h265SPS ) ) ,
NALUnit : h265SPS ,
} } ,
} ,
{
NaluType : byte ( h265 . NALUType_PPS_NUT ) ,
NumNalus : 1 ,
Nalus : [ ] gomp4 . HEVCNalu { {
Length : uint16 ( len ( h265PPS ) ) ,
NALUnit : h265PPS ,
} } ,
} ,
} ,
} )
if err != nil {
return err
}
_ , err = w . WriteBox ( & gomp4 . Btrt { // <btrt/>
MaxBitrate : 1000000 ,
AvgBitrate : 1000000 ,
} )
if err != nil {
return err
}
err = w . writeBoxEnd ( ) // </hev1>
if err != nil {
return err
}
case * format . MPEG4Audio :
_ , err = w . writeBoxStart ( & gomp4 . AudioSampleEntry { // <mp4a>
SampleEntry : gomp4 . SampleEntry {