@@ -22,66 +22,110 @@ func (d listEntryDuration) MarshalJSON() ([]byte, error) {
22
22
return json .Marshal (time .Duration (d ).Seconds ())
23
23
}
24
24
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
+
25
86
type listEntry struct {
26
87
Start time.Time `json:"start"`
27
88
Duration listEntryDuration `json:"duration"`
28
89
URL string `json:"url"`
29
90
}
30
91
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 (
32
119
recordFormat conf.RecordFormat ,
33
120
segments []* recordstore.Segment ,
34
121
) ([]listEntry , error ) {
35
122
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
83
126
}
84
127
128
+ out := concatenateSegments (parsed )
85
129
return out , nil
86
130
}
87
131
@@ -135,7 +179,7 @@ func (s *Server) onList(ctx *gin.Context) {
135
179
return
136
180
}
137
181
138
- entries , err := readDurationAndConcatenate (pathConf .RecordFormat , segments )
182
+ entries , err := parseAndConcatenate (pathConf .RecordFormat , segments )
139
183
if err != nil {
140
184
s .writeError (ctx , http .StatusInternalServerError , err )
141
185
return
0 commit comments