@@ -22,66 +22,110 @@ func (d listEntryDuration) MarshalJSON() ([]byte, error) {
2222 return json .Marshal (time .Duration (d ).Seconds ())
2323}
2424
25+ type parsedSegment struct {
26+ start time.Time
27+ init * fmp4.Init
28+ duration time.Duration
29+ }
30+
31+ func parseSegment (seg * recordstore.Segment ) (* parsedSegment , error ) {
32+ f , err := os .Open (seg .Fpath )
33+ if err != nil {
34+ return nil , err
35+ }
36+ defer f .Close ()
37+
38+ init , duration , err := segmentFMP4ReadHeader (f )
39+ if err != nil {
40+ return nil , err
41+ }
42+
43+ // if duration is not present in the header, compute it
44+ // by parsing each part
45+ if duration == 0 {
46+ duration , err = segmentFMP4ReadDurationFromParts (f , init )
47+ if err != nil {
48+ return nil , err
49+ }
50+ }
51+
52+ return & parsedSegment {
53+ start : seg .Start ,
54+ init : init ,
55+ duration : duration ,
56+ }, nil
57+ }
58+
59+ func parseSegments (segments []* recordstore.Segment ) ([]* parsedSegment , error ) {
60+ parsed := make ([]* parsedSegment , len (segments ))
61+ ch := make (chan error )
62+
63+ // process segments in parallel.
64+ // parallel random access should improve performance in most cases.
65+ // ref: https://pkolaczk.github.io/disk-parallelism/
66+ for i , seg := range segments {
67+ go func (i int , seg * recordstore.Segment ) {
68+ var err error
69+ parsed [i ], err = parseSegment (seg )
70+ ch <- err
71+ }(i , seg )
72+ }
73+
74+ var err error
75+
76+ for range segments {
77+ err2 := <- ch
78+ if err2 != nil {
79+ err = err2
80+ }
81+ }
82+
83+ return parsed , err
84+ }
85+
2586type listEntry struct {
2687 Start time.Time `json:"start"`
2788 Duration listEntryDuration `json:"duration"`
2889 URL string `json:"url"`
2990}
3091
31- func readDurationAndConcatenate (
92+ func concatenateSegments (parsed []* parsedSegment ) []listEntry {
93+ out := []listEntry {}
94+ var prevInit * fmp4.Init
95+
96+ for _ , parsed := range parsed {
97+ if len (out ) != 0 && segmentFMP4CanBeConcatenated (
98+ prevInit ,
99+ out [len (out )- 1 ].Start .Add (time .Duration (out [len (out )- 1 ].Duration )),
100+ parsed .init ,
101+ parsed .start ) {
102+ prevStart := out [len (out )- 1 ].Start
103+ curEnd := parsed .start .Add (parsed .duration )
104+ out [len (out )- 1 ].Duration = listEntryDuration (curEnd .Sub (prevStart ))
105+ } else {
106+ out = append (out , listEntry {
107+ Start : parsed .start ,
108+ Duration : listEntryDuration (parsed .duration ),
109+ })
110+ }
111+
112+ prevInit = parsed .init
113+ }
114+
115+ return out
116+ }
117+
118+ func parseAndConcatenate (
32119 recordFormat conf.RecordFormat ,
33120 segments []* recordstore.Segment ,
34121) ([]listEntry , error ) {
35122 if recordFormat == conf .RecordFormatFMP4 {
36- out := []listEntry {}
37- var prevInit * fmp4.Init
38-
39- for _ , seg := range segments {
40- err := func () error {
41- f , err := os .Open (seg .Fpath )
42- if err != nil {
43- return err
44- }
45- defer f .Close ()
46-
47- init , duration , err := segmentFMP4ReadHeader (f )
48- if err != nil {
49- return err
50- }
51-
52- // if duration is not present in the header, compute it
53- // by parsing each part
54- if duration == 0 {
55- duration , err = segmentFMP4ReadDurationFromParts (f , init )
56- if err != nil {
57- return err
58- }
59- }
60-
61- if len (out ) != 0 && segmentFMP4CanBeConcatenated (
62- prevInit ,
63- out [len (out )- 1 ].Start .Add (time .Duration (out [len (out )- 1 ].Duration )),
64- init ,
65- seg .Start ) {
66- prevStart := out [len (out )- 1 ].Start
67- curEnd := seg .Start .Add (duration )
68- out [len (out )- 1 ].Duration = listEntryDuration (curEnd .Sub (prevStart ))
69- } else {
70- out = append (out , listEntry {
71- Start : seg .Start ,
72- Duration : listEntryDuration (duration ),
73- })
74- }
75-
76- prevInit = init
77-
78- return nil
79- }()
80- if err != nil {
81- return nil , err
82- }
123+ parsed , err := parseSegments (segments )
124+ if err != nil {
125+ return nil , err
83126 }
84127
128+ out := concatenateSegments (parsed )
85129 return out , nil
86130 }
87131
@@ -135,7 +179,7 @@ func (s *Server) onList(ctx *gin.Context) {
135179 return
136180 }
137181
138- entries , err := readDurationAndConcatenate (pathConf .RecordFormat , segments )
182+ entries , err := parseAndConcatenate (pathConf .RecordFormat , segments )
139183 if err != nil {
140184 s .writeError (ctx , http .StatusInternalServerError , err )
141185 return
0 commit comments