@@ -18,6 +18,7 @@ import (
1818
1919 "github.com/Dash-Industry-Forum/livesim2/pkg/drm"
2020 "github.com/Dash-Industry-Forum/livesim2/pkg/logging"
21+ m "github.com/Eyevinn/dash-mpd/mpd"
2122 "github.com/Eyevinn/mp4ff/bits"
2223 "github.com/Eyevinn/mp4ff/mp4"
2324 "github.com/stretchr/testify/assert"
@@ -728,6 +729,174 @@ func TestSegmentStatusCodeResponse(t *testing.T) {
728729 }
729730}
730731
732+ // TestMpeghAssets tests MPEG-H assets with 1.6s segment duration
733+ func TestMpeghAssets (t * testing.T ) {
734+ vodFS := os .DirFS ("testdata/assets" )
735+ am := newAssetMgr (vodFS , "" , false )
736+ logger := slog .Default ()
737+ err := am .discoverAssets (logger )
738+ require .NoError (t , err )
739+
740+ testCases := []struct {
741+ asset string
742+ mpdName string
743+ audioRepId string
744+ videoRepId string
745+ expectedAudioTimescale int
746+ expectedVideoTimescale int
747+ expectedSegmentDurMs int
748+ expectedAudioCodec string
749+ expectedVideoCodec string
750+ }{
751+ {
752+ asset : "mpegh_BL_1_6" ,
753+ mpdName : "BL_1_6.mpd" ,
754+ audioRepId : "mhm1_64kbps_per_signal" ,
755+ videoRepId : "hvc1_10000kbps" ,
756+ expectedAudioTimescale : 48000 ,
757+ expectedVideoTimescale : 50 ,
758+ expectedSegmentDurMs : 1600 , // 1.6 seconds
759+ expectedAudioCodec : "mhm1.0x10" ,
760+ expectedVideoCodec : "hvc1.1.6.L123.90" ,
761+ },
762+ {
763+ asset : "mpegh_LC_1_6" ,
764+ mpdName : "LC_1_6.mpd" ,
765+ audioRepId : "mhm1_64kbps_per_signal" ,
766+ videoRepId : "hvc1_10000kbps" ,
767+ expectedAudioTimescale : 48000 ,
768+ expectedVideoTimescale : 50 ,
769+ expectedSegmentDurMs : 1600 , // 1.6 seconds
770+ expectedAudioCodec : "mhm1.0x0B" ,
771+ expectedVideoCodec : "hvc1.1.6.L123.90" ,
772+ },
773+ }
774+
775+ var drmCfg * drm.DrmConfig = nil
776+ for _ , tc := range testCases {
777+ t .Run (tc .asset , func (t * testing.T ) {
778+ asset , ok := am .findAsset (tc .asset )
779+ require .True (t , ok , "Asset %s not found" , tc .asset )
780+
781+ cfg := NewResponseConfig ()
782+ nowMS := 10_000 // 10 seconds into stream
783+
784+ // Test MPD generation
785+ liveMPD , err := LiveMPD (asset , tc .mpdName , cfg , nil , nowMS )
786+ require .NoError (t , err , "Failed to generate live MPD" )
787+ require .NotNil (t , liveMPD , "Live MPD is nil" )
788+ require .Len (t , liveMPD .Periods , 1 , "Expected exactly one period" )
789+ require .Len (t , liveMPD .Periods [0 ].AdaptationSets , 2 , "Expected exactly two adaptation sets" )
790+
791+ // Test audio and video adaptation sets
792+ var audioAS , videoAS * m.AdaptationSetType
793+ for _ , as := range liveMPD .Periods [0 ].AdaptationSets {
794+ switch as .ContentType {
795+ case "audio" :
796+ audioAS = as
797+ case "video" :
798+ videoAS = as
799+ }
800+ }
801+ require .NotNil (t , audioAS , "Audio adaptation set not found" )
802+ require .NotNil (t , videoAS , "Video adaptation set not found" )
803+
804+ // Verify audio adaptation set
805+ require .Equal (t , tc .expectedAudioCodec , audioAS .Codecs , "Audio codec mismatch" )
806+ require .Equal (t , "audio/mp4" , audioAS .MimeType , "Audio mime type mismatch" )
807+ require .Equal (t , tc .expectedAudioTimescale , int (* audioAS .SegmentTemplate .Timescale ), "Audio timescale mismatch" )
808+ expectedAudioDuration := tc .expectedSegmentDurMs * tc .expectedAudioTimescale / 1000
809+ require .Equal (t , expectedAudioDuration , int (* audioAS .SegmentTemplate .Duration ), "Audio duration mismatch" )
810+
811+ // Verify video adaptation set
812+ require .Equal (t , tc .expectedVideoCodec , videoAS .Codecs , "Video codec mismatch" )
813+ require .Equal (t , "video/mp4" , videoAS .MimeType , "Video mime type mismatch" )
814+ require .Equal (t , tc .expectedVideoTimescale , int (* videoAS .SegmentTemplate .Timescale ), "Video timescale mismatch" )
815+ expectedVideoDuration := tc .expectedSegmentDurMs * tc .expectedVideoTimescale / 1000
816+ require .Equal (t , expectedVideoDuration , int (* videoAS .SegmentTemplate .Duration ), "Video duration mismatch" )
817+
818+ // Test audio init segment
819+ audioInitPath := fmt .Sprintf ("%s_init.mp4" , tc .audioRepId )
820+ rr := httptest .NewRecorder ()
821+ wroteInit , err := writeInitSegment (logger , rr , cfg , drmCfg , asset , audioInitPath )
822+ require .True (t , wroteInit , "Failed to write audio init segment" )
823+ require .NoError (t , err , "Error writing audio init segment" )
824+ require .Equal (t , http .StatusOK , rr .Code , "Audio init segment returned wrong status code" )
825+
826+ // Verify audio init segment content
827+ initSeg := rr .Body .Bytes ()
828+ require .Greater (t , len (initSeg ), 0 , "Audio init segment is empty" )
829+ sr := bits .NewFixedSliceReader (initSeg )
830+ mp4File , err := mp4 .DecodeFileSR (sr )
831+ require .NoError (t , err , "Failed to parse audio init segment" )
832+ require .NotNil (t , mp4File .Init , "Init segment has no init part" )
833+ audioTimescale := int (mp4File .Moov .Trak .Mdia .Mdhd .Timescale )
834+ require .Equal (t , tc .expectedAudioTimescale , audioTimescale , "Audio init segment timescale mismatch" )
835+
836+ // Test audio media segment (segment 0)
837+ audioMediaPath := fmt .Sprintf ("%s_0.m4s" , tc .audioRepId )
838+ so , err := genLiveSegment (logger , vodFS , asset , cfg , audioMediaPath , nowMS , false )
839+ require .NoError (t , err , "Failed to generate audio segment" )
840+ require .NotNil (t , so , "Audio segment output is nil" )
841+ require .Equal (t , "audio/mp4" , so .meta .rep .SegmentType (), "Audio segment wrong type" )
842+
843+ // Verify audio segment timing (1.6s duration = 76800 at 48kHz)
844+ seg := so .seg
845+ require .Len (t , seg .Fragments , 1 , "Expected exactly one fragment in audio segment" )
846+ baseDecodeTime := seg .Fragments [0 ].Moof .Traf .Tfdt .BaseMediaDecodeTime ()
847+ require .Equal (t , 0 , int (baseDecodeTime ), "First audio segment should start at time 0" )
848+
849+ // Test video init segment
850+ videoInitPath := fmt .Sprintf ("%s_init.mp4" , tc .videoRepId )
851+ rr = httptest .NewRecorder ()
852+ wroteInit , err = writeInitSegment (logger , rr , cfg , drmCfg , asset , videoInitPath )
853+ require .True (t , wroteInit , "Failed to write video init segment" )
854+ require .NoError (t , err , "Error writing video init segment" )
855+ require .Equal (t , http .StatusOK , rr .Code , "Video init segment returned wrong status code" )
856+
857+ // Verify video init segment content
858+ initSeg = rr .Body .Bytes ()
859+ require .Greater (t , len (initSeg ), 0 , "Video init segment is empty" )
860+ sr = bits .NewFixedSliceReader (initSeg )
861+ mp4File , err = mp4 .DecodeFileSR (sr )
862+ require .NoError (t , err , "Failed to parse video init segment" )
863+ require .NotNil (t , mp4File .Init , "Init segment has no init part" )
864+ videoTimescale := int (mp4File .Moov .Trak .Mdia .Mdhd .Timescale )
865+ require .Equal (t , tc .expectedVideoTimescale , videoTimescale , "Video init segment timescale mismatch" )
866+
867+ // Test video media segment (segment 0)
868+ videoMediaPath := fmt .Sprintf ("%s_0.m4s" , tc .videoRepId )
869+ so , err = genLiveSegment (logger , vodFS , asset , cfg , videoMediaPath , nowMS , false )
870+ require .NoError (t , err , "Failed to generate video segment" )
871+ require .NotNil (t , so , "Video segment output is nil" )
872+ require .Equal (t , "video/mp4" , so .meta .rep .SegmentType (), "Video segment wrong type" )
873+
874+ // Verify video segment timing (1.6s duration = 80 at 50Hz)
875+ seg = so .seg
876+ require .Len (t , seg .Fragments , 1 , "Expected exactly one fragment in video segment" )
877+ baseDecodeTime = seg .Fragments [0 ].Moof .Traf .Tfdt .BaseMediaDecodeTime ()
878+ require .Equal (t , 0 , int (baseDecodeTime ), "First video segment should start at time 0" )
879+
880+ // Test segment 1 to verify timing progression
881+ audioMediaPath = fmt .Sprintf ("%s_1.m4s" , tc .audioRepId )
882+ so , err = genLiveSegment (logger , vodFS , asset , cfg , audioMediaPath , nowMS , false )
883+ require .NoError (t , err , "Failed to generate audio segment 1" )
884+ baseDecodeTime = so .seg .Fragments [0 ].Moof .Traf .Tfdt .BaseMediaDecodeTime ()
885+ expectedAudioTime := 76800 // 1.6s * 48000
886+ require .Equal (t , expectedAudioTime , int (baseDecodeTime ), "Second audio segment timing incorrect" )
887+
888+ videoMediaPath = fmt .Sprintf ("%s_1.m4s" , tc .videoRepId )
889+ so , err = genLiveSegment (logger , vodFS , asset , cfg , videoMediaPath , nowMS , false )
890+ require .NoError (t , err , "Failed to generate video segment 1" )
891+ baseDecodeTime = so .seg .Fragments [0 ].Moof .Traf .Tfdt .BaseMediaDecodeTime ()
892+ expectedVideoTime := 80 // 1.6s * 50
893+ require .Equal (t , expectedVideoTime , int (baseDecodeTime ), "Second video segment timing incorrect" )
894+
895+ t .Logf ("Successfully tested MPEG-H asset %s with 1.6s segments" , tc .asset )
896+ })
897+ }
898+ }
899+
731900func TestMehdBoxRemovedFromInitSegment (t * testing.T ) {
732901 var drmCfg * drm.DrmConfig = nil
733902 vodFS := os .DirFS ("testdata/assets" )
0 commit comments