0.2.69 - Homepage loading improvements pass
This commit is contained in:
@@ -122,21 +122,51 @@ func (s *Sampler) Sample(
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
stats, err := s.samplePass(ctx, url, from, to, interval, false)
|
||||
if err == nil || !decoderCrashed(err) || ctx.Err() != nil {
|
||||
return stats, err
|
||||
}
|
||||
// A decoder crash is local to the ffmpeg process, not evidence that the media is
|
||||
// unreadable. Retry once with conservative decoder settings: single-threaded decoding
|
||||
// avoids the most common native-code race, while corrupt packets are discarded rather
|
||||
// than handed back through the failing path. Ordinary HTTP and authentication failures
|
||||
// are never retried here.
|
||||
stats, retryErr := s.samplePass(ctx, url, from, to, interval, true)
|
||||
if retryErr != nil {
|
||||
return stats, fmt.Errorf("credits: conservative ffmpeg retry: %w", retryErr)
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (s *Sampler) samplePass(
|
||||
ctx context.Context, url string, from, to time.Duration, interval time.Duration,
|
||||
conservative bool,
|
||||
) ([]frameStats, error) {
|
||||
|
||||
// -ss ahead of -i is the whole optimisation: it seeks in the container before opening a
|
||||
// decoder, so the input starts near the credits. Behind -i it would decode from zero and
|
||||
// discard, which is the full read this package exists to avoid.
|
||||
args := []string{
|
||||
"-hide_banner", "-loglevel", "error", "-nostdin",
|
||||
"-ss", formatSeconds(from),
|
||||
}
|
||||
if conservative {
|
||||
args = append(args,
|
||||
"-threads", "1",
|
||||
"-fflags", "+discardcorrupt",
|
||||
"-err_detect", "ignore_err",
|
||||
)
|
||||
}
|
||||
args = append(args,
|
||||
"-i", url,
|
||||
"-t", formatSeconds(to - from),
|
||||
"-t", formatSeconds(to-from),
|
||||
"-an", "-sn", "-dn",
|
||||
"-vf", fmt.Sprintf("fps=%s,scale=%d:%d,format=gray",
|
||||
formatRate(interval), sampleWidth, sampleHeight),
|
||||
"-frames:v", strconv.Itoa(maxFrames),
|
||||
"-f", "rawvideo", "-pix_fmt", "gray",
|
||||
"pipe:1",
|
||||
}
|
||||
)
|
||||
|
||||
cmd := exec.CommandContext(ctx, s.binary(), args...)
|
||||
// Cancel and WaitDelay together are what stop an orphan. CommandContext's default is to
|
||||
@@ -181,6 +211,24 @@ func (s *Sampler) Sample(
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func decoderCrashed(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
message := strings.ToLower(err.Error())
|
||||
for _, signature := range []string{
|
||||
"segmentation fault",
|
||||
"signal: aborted",
|
||||
"signal: bus error",
|
||||
"access violation",
|
||||
} {
|
||||
if strings.Contains(message, signature) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// readFrames pulls fixed-size grayscale frames off the pipe and reduces each one as it
|
||||
// arrives. The frame buffer is allocated once and reused, so a four-hundred-frame pass
|
||||
// allocates fourteen kilobytes rather than five and a half megabytes.
|
||||
|
||||
Reference in New Issue
Block a user