mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-03 13:44:33 -04:00
Use new keyframes struct in stream to allow async keyframes analysis
This commit is contained in:
parent
6a1fff227e
commit
df8a1d3b26
@ -14,7 +14,7 @@ func NewAudioStream(file *FileStream, idx int32) *AudioStream {
|
|||||||
log.Printf("Creating a audio stream %d for %s", idx, file.Path)
|
log.Printf("Creating a audio stream %d for %s", idx, file.Path)
|
||||||
ret := new(AudioStream)
|
ret := new(AudioStream)
|
||||||
ret.index = idx
|
ret.index = idx
|
||||||
ret.Stream = NewStream(file, ret)
|
NewStream(file, ret, &ret.Stream)
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,18 +62,20 @@ var DeletedHead = Head{
|
|||||||
command: nil,
|
command: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStream(file *FileStream, handle StreamHandle) Stream {
|
func NewStream(file *FileStream, handle StreamHandle, ret *Stream) {
|
||||||
ret := Stream{
|
ret.handle = handle
|
||||||
handle: handle,
|
ret.file = file
|
||||||
file: file,
|
ret.heads = make([]Head, 0)
|
||||||
segments: make([]Segment, len(file.Keyframes)),
|
|
||||||
heads: make([]Head, 0),
|
length, is_done := file.Keyframes.Length()
|
||||||
}
|
ret.segments = make([]Segment, length)
|
||||||
for seg := range ret.segments {
|
for seg := range ret.segments {
|
||||||
ret.segments[seg].channel = make(chan struct{})
|
ret.segments[seg].channel = make(chan struct{})
|
||||||
}
|
}
|
||||||
// Copy default value before use is safe. Next warning can be safely ignored
|
|
||||||
return ret
|
if !is_done {
|
||||||
|
// TODO: create new ret.segments for every new keyframes that get created in the file.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember to lock before calling this.
|
// Remember to lock before calling this.
|
||||||
@ -104,8 +106,14 @@ func toSegmentStr(segments []float64) string {
|
|||||||
|
|
||||||
func (ts *Stream) run(start int32) error {
|
func (ts *Stream) run(start int32) error {
|
||||||
// Start the transcode up to the 100th segment (or less)
|
// Start the transcode up to the 100th segment (or less)
|
||||||
|
length, is_done := ts.file.Keyframes.Length()
|
||||||
|
end := min(start+100, length)
|
||||||
|
// if keyframes analysys is not finished, always have a 1-segment padding
|
||||||
|
// for the extra segment needed for precise split (look comment before -to flag)
|
||||||
|
if !is_done {
|
||||||
|
end--
|
||||||
|
}
|
||||||
// Stop at the first finished segment
|
// Stop at the first finished segment
|
||||||
end := min(start+100, int32(len(ts.file.Keyframes)))
|
|
||||||
ts.lock.Lock()
|
ts.lock.Lock()
|
||||||
for i := start; i < end; i++ {
|
for i := start; i < end; i++ {
|
||||||
if ts.isSegmentReady(i) || ts.isSegmentTranscoding(i) {
|
if ts.isSegmentReady(i) || ts.isSegmentTranscoding(i) {
|
||||||
@ -113,7 +121,7 @@ func (ts *Stream) run(start int32) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if start == end {
|
if start >= end {
|
||||||
// this can happens if the start segment was finished between the check
|
// this can happens if the start segment was finished between the check
|
||||||
// to call run() and the actual call.
|
// to call run() and the actual call.
|
||||||
// since most checks are done in a RLock() instead of a Lock() this can
|
// since most checks are done in a RLock() instead of a Lock() this can
|
||||||
@ -131,7 +139,7 @@ func (ts *Stream) run(start int32) error {
|
|||||||
ts.file.Path,
|
ts.file.Path,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
len(ts.file.Keyframes),
|
length,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Include both the start and end delimiter because -ss and -to are not accurate
|
// Include both the start and end delimiter because -ss and -to are not accurate
|
||||||
@ -148,17 +156,17 @@ func (ts *Stream) run(start int32) error {
|
|||||||
// the param for the -ss takes the keyframe before the specificed time
|
// the param for the -ss takes the keyframe before the specificed time
|
||||||
// (if the specified time is a keyframe, it either takes that keyframe or the one before)
|
// (if the specified time is a keyframe, it either takes that keyframe or the one before)
|
||||||
// to prevent this weird behavior, we specify a bit after the keyframe that interest us
|
// to prevent this weird behavior, we specify a bit after the keyframe that interest us
|
||||||
if start_segment+1 == int32(len(ts.file.Keyframes)) {
|
if start_segment+1 == length {
|
||||||
start_ref = (ts.file.Keyframes[start_segment] + float64(ts.file.Info.Duration)) / 2
|
start_ref = (ts.file.Keyframes.Get(start_segment) + float64(ts.file.Info.Duration)) / 2
|
||||||
} else {
|
} else {
|
||||||
start_ref = (ts.file.Keyframes[start_segment] + ts.file.Keyframes[start_segment+1]) / 2
|
start_ref = (ts.file.Keyframes.Get(start_segment) + ts.file.Keyframes.Get(start_segment+1)) / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end_padding := int32(1)
|
end_padding := int32(1)
|
||||||
if end == int32(len(ts.file.Keyframes)) {
|
if end == length {
|
||||||
end_padding = 0
|
end_padding = 0
|
||||||
}
|
}
|
||||||
segments := ts.file.Keyframes[start+1 : end+end_padding]
|
segments := ts.file.Keyframes.Slice(start+1, end+end_padding)
|
||||||
if len(segments) == 0 {
|
if len(segments) == 0 {
|
||||||
// we can't leave that empty else ffmpeg errors out.
|
// we can't leave that empty else ffmpeg errors out.
|
||||||
segments = []float64{9999999}
|
segments = []float64{9999999}
|
||||||
@ -187,14 +195,14 @@ func (ts *Stream) run(start int32) error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// do not include -to if we want the file to go to the end
|
// do not include -to if we want the file to go to the end
|
||||||
if end+1 < int32(len(ts.file.Keyframes)) {
|
if end+1 < length {
|
||||||
// sometimes, the duration is shorter than expected (only during transcode it seems)
|
// sometimes, the duration is shorter than expected (only during transcode it seems)
|
||||||
// always include more and use the -f segment to split the file where we want
|
// always include more and use the -f segment to split the file where we want
|
||||||
end_ref := ts.file.Keyframes[end+1]
|
end_ref := ts.file.Keyframes.Get(end + 1)
|
||||||
// it seems that the -to is confused when -ss seek before the given time (because it searches for a keyframe)
|
// it seems that the -to is confused when -ss seek before the given time (because it searches for a keyframe)
|
||||||
// add back the time that would be lost otherwise
|
// add back the time that would be lost otherwise
|
||||||
// this only appens when -to is before -i but having -to after -i gave a bug (not sure, don't remember)
|
// this only appens when -to is before -i but having -to after -i gave a bug (not sure, don't remember)
|
||||||
end_ref += start_ref - ts.file.Keyframes[start_segment]
|
end_ref += start_ref - ts.file.Keyframes.Get(start_segment)
|
||||||
args = append(args,
|
args = append(args,
|
||||||
"-to", fmt.Sprintf("%.6f", end_ref),
|
"-to", fmt.Sprintf("%.6f", end_ref),
|
||||||
)
|
)
|
||||||
@ -223,7 +231,7 @@ func (ts *Stream) run(start int32) error {
|
|||||||
// segment_times want durations, not timestamps so we must substract the -ss param
|
// segment_times want durations, not timestamps so we must substract the -ss param
|
||||||
// since we give a greater value to -ss to prevent wrong seeks but -segment_times
|
// since we give a greater value to -ss to prevent wrong seeks but -segment_times
|
||||||
// needs precise segments, we use the keyframe we want to seek to as a reference.
|
// needs precise segments, we use the keyframe we want to seek to as a reference.
|
||||||
return seg - ts.file.Keyframes[start_segment]
|
return seg - ts.file.Keyframes.Get(start_segment)
|
||||||
})),
|
})),
|
||||||
"-segment_list_type", "flat",
|
"-segment_list_type", "flat",
|
||||||
"-segment_list", "pipe:1",
|
"-segment_list", "pipe:1",
|
||||||
@ -316,24 +324,29 @@ func (ts *Stream) run(start int32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ts *Stream) GetIndex() (string, error) {
|
func (ts *Stream) GetIndex() (string, error) {
|
||||||
|
// playlist type is event since we can append to the list if Keyframe.IsDone is false.
|
||||||
|
// start time offset makes the stream start at 0s instead of ~3segments from the end (requires version 6 of hls)
|
||||||
index := `#EXTM3U
|
index := `#EXTM3U
|
||||||
#EXT-X-VERSION:3
|
#EXT-X-VERSION:6
|
||||||
#EXT-X-PLAYLIST-TYPE:VOD
|
#EXT-X-PLAYLIST-TYPE:EVENT
|
||||||
#EXT-X-ALLOW-CACHE:YES
|
#EXT-X-START:TIME-OFFSET=0
|
||||||
#EXT-X-TARGETDURATION:4
|
#EXT-X-TARGETDURATION:4
|
||||||
#EXT-X-MEDIA-SEQUENCE:0
|
#EXT-X-MEDIA-SEQUENCE:0
|
||||||
#EXT-X-INDEPENDENT-SEGMENTS
|
#EXT-X-INDEPENDENT-SEGMENTS
|
||||||
`
|
`
|
||||||
|
length, is_done := ts.file.Keyframes.Length()
|
||||||
|
|
||||||
for segment := 0; segment < len(ts.file.Keyframes)-1; segment++ {
|
for segment := int32(0); segment < length-1; segment++ {
|
||||||
index += fmt.Sprintf("#EXTINF:%.6f\n", ts.file.Keyframes[segment+1]-ts.file.Keyframes[segment])
|
index += fmt.Sprintf("#EXTINF:%.6f\n", ts.file.Keyframes.Get(segment+1)-ts.file.Keyframes.Get(segment))
|
||||||
index += fmt.Sprintf("segment-%d.ts\n", segment)
|
index += fmt.Sprintf("segment-%d.ts\n", segment)
|
||||||
}
|
}
|
||||||
// do not forget to add the last segment between the last keyframe and the end of the file
|
// do not forget to add the last segment between the last keyframe and the end of the file
|
||||||
index += fmt.Sprintf("#EXTINF:%.6f\n", float64(ts.file.Info.Duration)-ts.file.Keyframes[len(ts.file.Keyframes)-1])
|
// if the keyframes extraction is not done, do not bother to add it, it will be retrived on the next index retrival
|
||||||
index += fmt.Sprintf("segment-%d.ts\n", len(ts.file.Keyframes)-1)
|
if is_done {
|
||||||
|
index += fmt.Sprintf("#EXTINF:%.6f\n", float64(ts.file.Info.Duration)-ts.file.Keyframes.Get(length-1))
|
||||||
index += `#EXT-X-ENDLIST`
|
index += fmt.Sprintf("segment-%d.ts\n", length-1)
|
||||||
|
index += `#EXT-X-ENDLIST`
|
||||||
|
}
|
||||||
return index, nil
|
return index, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,13 +414,13 @@ func (ts *Stream) prerareNextSegements(segment int32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ts *Stream) getMinEncoderDistance(segment int32) float64 {
|
func (ts *Stream) getMinEncoderDistance(segment int32) float64 {
|
||||||
time := ts.file.Keyframes[segment]
|
time := ts.file.Keyframes.Get(segment)
|
||||||
distances := Map(ts.heads, func(head Head, _ int) float64 {
|
distances := Map(ts.heads, func(head Head, _ int) float64 {
|
||||||
// ignore killed heads or heads after the current time
|
// ignore killed heads or heads after the current time
|
||||||
if head.segment < 0 || ts.file.Keyframes[head.segment] > time || segment >= head.end {
|
if head.segment < 0 || ts.file.Keyframes.Get(head.segment) > time || segment >= head.end {
|
||||||
return math.Inf(1)
|
return math.Inf(1)
|
||||||
}
|
}
|
||||||
return time - ts.file.Keyframes[head.segment]
|
return time - ts.file.Keyframes.Get(head.segment)
|
||||||
})
|
})
|
||||||
if len(distances) == 0 {
|
if len(distances) == 0 {
|
||||||
return math.Inf(1)
|
return math.Inf(1)
|
||||||
|
@ -14,7 +14,7 @@ func NewVideoStream(file *FileStream, quality Quality) *VideoStream {
|
|||||||
log.Printf("Creating a new video stream for %s in quality %s", file.Path, quality)
|
log.Printf("Creating a new video stream for %s in quality %s", file.Path, quality)
|
||||||
ret := new(VideoStream)
|
ret := new(VideoStream)
|
||||||
ret.quality = quality
|
ret.quality = quality
|
||||||
ret.Stream = NewStream(file, ret)
|
NewStream(file, ret, &ret.Stream)
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user