tiktak: откатить -threads 1, развести бюджеты probe и ffmpeg
MUMBLE "couldn't create video" -- моя регрессия из 576baa3/753d21c. В логах у части загрузок есть Stream mapping и "Press [q]", но нет "Output #0": ffmpeg не доработал, его снял дедлайн. Две причины: - "-threads 1" душил многопоточное декодирование vp9. 12-секундное 640x360 в 2 секунды одним потоком не влезает. - checkGeometry вызывал ffprobe ВНУТРИ того же 2-секундного контекста, что и кодек, отбирая у него время. - убран -threads 1, -max_alloc оставлен (на скорость не влияет); - probe получил свой бюджет ProbeDeadline=1s, ffmpeg -- полные ProcessingDeadline=2.5s. Сумма с GetDuration укладывается в 7с контекста handleCreate с запасом; - семафор 16 -> 48: под флудом 16 слотов создавали очередь, и чекер выпадал по таймауту ещё до обработки. Бомбы теперь отсекаются дёшево, слоты освобождаются быстро. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,21 +14,19 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ProcessingDeadline = 2 * time.Second
|
||||
// 75 concurrent ffmpeg processes are enough to swap the box out on their
|
||||
// own, bomb or no bomb.
|
||||
semaphore = make(chan struct{}, 16)
|
||||
// handleCreate даёт 7с на всё: probe + ffmpeg + GetDuration для субтитров.
|
||||
// 1.0 + 2.5 + 2.5 = 6.0, секунда про запас.
|
||||
ProcessingDeadline = 2500 * time.Millisecond
|
||||
// Header-only ffprobe; cheap, но не должен отъедать бюджет у кодека.
|
||||
ProbeDeadline = 1 * time.Second
|
||||
semaphore = make(chan struct{}, 48)
|
||||
)
|
||||
|
||||
func generatePreview(ctx context.Context, inp string, out string) error {
|
||||
// Reject decompression bombs before ffmpeg expands a frame into memory.
|
||||
if err := checkGeometry(ctx, inp); err != nil {
|
||||
return err
|
||||
}
|
||||
// -max_alloc caps a single ffmpeg allocation: a 1080p frame needs ~8 MB, a
|
||||
// 16383x16383 bomb needs ~1 GB. Second line of defence behind checkGeometry,
|
||||
// and it works even when the probe told us nothing.
|
||||
cmd := exec.CommandContext(ctx, "ffmpeg", "-y", "-threads", "1",
|
||||
cmd := exec.CommandContext(ctx, "ffmpeg", "-y",
|
||||
"-max_alloc", "134217728", "-i", inp, "-vframes", "1", out)
|
||||
s := strings.Builder{}
|
||||
cmd.Stdout = &s
|
||||
@@ -43,12 +41,20 @@ func generatePreview(ctx context.Context, inp string, out string) error {
|
||||
func GeneratePreview(ctx context.Context, inp string, out string) error {
|
||||
select {
|
||||
case semaphore <- struct{}{}:
|
||||
ctx, cancel := context.WithTimeout(ctx, ProcessingDeadline)
|
||||
defer func() {
|
||||
cancel()
|
||||
<-semaphore
|
||||
}()
|
||||
return generatePreview(ctx, inp, out)
|
||||
defer func() { <-semaphore }()
|
||||
|
||||
// Probe gets its OWN budget. Sharing ProcessingDeadline with ffmpeg
|
||||
// starved the encode and killed legit uploads ("cannot create video").
|
||||
pctx, pcancel := context.WithTimeout(ctx, ProbeDeadline)
|
||||
err := checkGeometry(pctx, inp)
|
||||
pcancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fctx, fcancel := context.WithTimeout(ctx, ProcessingDeadline)
|
||||
defer fcancel()
|
||||
return generatePreview(fctx, inp, out)
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user