delete guess expected size feature

it it doing some really bad guesses for opus files
This commit is contained in:
sentriz
2022-04-20 23:33:10 +01:00
parent 4658d07273
commit 6bebceccd9
20 changed files with 44 additions and 526 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -1,3 +0,0 @@
go test fuzz v1
byte('Y')
byte('\x05')

View File

@@ -1,3 +0,0 @@
go test fuzz v1
byte('\x15')
byte('}')

View File

@@ -1,3 +0,0 @@
go test fuzz v1
byte('\a')
byte('\x02')

View File

@@ -15,7 +15,7 @@ import (
)
type Transcoder interface {
Transcode(ctx context.Context, profile Profile, in string) (io.ReadCloser, error)
Transcode(ctx context.Context, profile Profile, in string, out io.Writer) error
}
var UserProfiles = map[string]Profile{
@@ -106,24 +106,3 @@ func parseProfile(profile Profile, in string) (string, []string, error) {
return name, args, nil
}
// GuessExpectedSize guesses how big the transcoded file will be in bytes.
// Handy if we want to send a Content-Length header to the client before
// the transcode has finished. This way, clients like DSub can render their
// scrub bar and duration as the track is streaming.
//
// The estimate should overshoot a bit (2s in this case) otherwise some HTTP
// clients will shit their trousers given some unexpected bytes.
func GuessExpectedSize(profile Profile, length time.Duration) int {
if length == 0 {
return 0
}
bytesPerSec := int(profile.BitRate() * 1000 / 8)
var guess int
guess += bytesPerSec * int(length.Seconds()-profile.seek.Seconds())
guess += bytesPerSec * 2 // 2s pading
guess += 10000 // 10kb byte padding
return guess
}

View File

@@ -1,47 +0,0 @@
//go:build go1.18
// +build go1.18
package transcode_test
import (
"context"
"io"
"testing"
"time"
"github.com/matryer/is"
"go.senan.xyz/gonic/transcode"
)
// FuzzGuessExpectedSize makes sure all of our profile's estimated transcode
// file sizes are slightly bigger than the real thing.
func FuzzGuessExpectedSize(f *testing.F) {
var profiles []transcode.Profile
for _, v := range transcode.UserProfiles {
profiles = append(profiles, v)
}
type track struct {
path string
length time.Duration
}
var tracks []track
tracks = append(tracks, track{"testdata/5s.mp3", 5 * time.Second})
tracks = append(tracks, track{"testdata/10s.mp3", 10 * time.Second})
tr := transcode.NewFFmpegTranscoder()
f.Fuzz(func(t *testing.T, pseed uint8, tseed uint8) {
is := is.New(t)
profile := profiles[int(pseed)%len(profiles)]
track := tracks[int(tseed)%len(tracks)]
sizeGuess := transcode.GuessExpectedSize(profile, track.length)
reader, err := tr.Transcode(context.Background(), profile, track.path)
is.NoErr(err)
actual, err := io.ReadAll(reader)
is.NoErr(err)
is.True(sizeGuess > len(actual))
})
}

View File

@@ -7,8 +7,6 @@ import (
"io"
"os"
"path/filepath"
"go.senan.xyz/gonic/iout"
)
const perm = 0644
@@ -24,14 +22,14 @@ func NewCachingTranscoder(t Transcoder, cachePath string) *CachingTranscoder {
return &CachingTranscoder{transcoder: t, cachePath: cachePath}
}
func (t *CachingTranscoder) Transcode(ctx context.Context, profile Profile, in string) (io.ReadCloser, error) {
func (t *CachingTranscoder) Transcode(ctx context.Context, profile Profile, in string, out io.Writer) error {
if err := os.MkdirAll(t.cachePath, perm^0111); err != nil {
return nil, fmt.Errorf("make cache path: %w", err)
return fmt.Errorf("make cache path: %w", err)
}
name, args, err := parseProfile(profile, in)
if err != nil {
return nil, fmt.Errorf("split command: %w", err)
return fmt.Errorf("split command: %w", err)
}
key := cacheKey(name, args)
@@ -39,18 +37,21 @@ func (t *CachingTranscoder) Transcode(ctx context.Context, profile Profile, in s
cf, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return nil, fmt.Errorf("open cache file: %w", err)
return fmt.Errorf("open cache file: %w", err)
}
defer cf.Close()
if i, err := cf.Stat(); err == nil && i.Size() > 0 {
return cf, nil
_, _ = io.Copy(out, cf)
return nil
}
out, err := t.transcoder.Transcode(ctx, profile, in)
if err != nil {
return nil, fmt.Errorf("internal transcode: %w", err)
if err := t.transcoder.Transcode(ctx, profile, in, io.MultiWriter(out, cf)); err != nil {
os.Remove(path)
return fmt.Errorf("internal transcode: %w", err)
}
return iout.NewTeeCloser(out, cf), nil
return nil
}
func cacheKey(cmd string, args []string) string {

View File

@@ -2,6 +2,7 @@ package transcode
import (
"context"
"errors"
"fmt"
"io"
"os/exec"
@@ -17,23 +18,25 @@ func NewFFmpegTranscoder() *FFmpegTranscoder {
var ErrFFmpegExit = fmt.Errorf("ffmpeg exited with non 0 status code")
func (*FFmpegTranscoder) Transcode(ctx context.Context, profile Profile, in string) (io.ReadCloser, error) {
func (*FFmpegTranscoder) Transcode(ctx context.Context, profile Profile, in string, out io.Writer) error {
name, args, err := parseProfile(profile, in)
if err != nil {
return nil, fmt.Errorf("split command: %w", err)
return fmt.Errorf("split command: %w", err)
}
preader, pwriter := io.Pipe()
cmd := exec.CommandContext(ctx, name, args...)
cmd.Stdout = pwriter
cmd.Stdout = out
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("starting cmd: %w", err)
return fmt.Errorf("starting cmd: %w", err)
}
go func() {
_ = pwriter.CloseWithError(cmd.Wait())
}()
return preader, nil
var exitErr *exec.ExitError
if err := cmd.Wait(); err != nil && !errors.As(err, &exitErr) {
return fmt.Errorf("waiting cmd: %w", err)
}
if code := cmd.ProcessState.ExitCode(); code > 1 {
return fmt.Errorf("%w: %d", ErrFFmpegExit, code)
}
return nil
}

View File

@@ -2,6 +2,7 @@ package transcode
import (
"context"
"fmt"
"io"
"os"
)
@@ -14,6 +15,14 @@ func NewNoneTranscoder() *NoneTranscoder {
return &NoneTranscoder{}
}
func (*NoneTranscoder) Transcode(ctx context.Context, _ Profile, in string) (io.ReadCloser, error) {
return os.Open(in)
func (*NoneTranscoder) Transcode(ctx context.Context, _ Profile, in string, out io.Writer) error {
file, err := os.Open(in)
if err != nil {
return fmt.Errorf("open file: %w", err)
}
defer file.Close()
if _, err := io.Copy(out, file); err != nil {
return fmt.Errorf("copy file: %w", err)
}
return nil
}