初始提交:短剧库源码(含转码优化 ultrafast)
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLegacyPlaybackAndDownloadSourceKeyLocalFFmpeg(t *testing.T) {
|
||||
ffmpeg, err := exec.LookPath("ffmpeg")
|
||||
if err != nil {
|
||||
t.Skip("local FFmpeg is unavailable")
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
command := exec.CommandContext(ctx, ffmpeg, "-hide_banner", "-loglevel", "error", "-f", "lavfi", "-i", "testsrc2=size=640x360:rate=30:duration=3", "-an", "-c:v", "libx264", "-preset", "ultrafast", "-crf", "18", "-threads", "1", "-f", "mpegts", "pipe:1")
|
||||
var log bytes.Buffer
|
||||
command.Stderr = &log
|
||||
segment, err := command.Output()
|
||||
if err != nil {
|
||||
t.Fatalf("generate local video fixture: %v %s", err, log.String())
|
||||
}
|
||||
sourceKey := []byte("0123456789abcdef")
|
||||
block, err := aes.NewCipher(sourceKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
segment = pkcs7Pad(segment, aes.BlockSize)
|
||||
cipher.NewCBCEncrypter(block, make([]byte, aes.BlockSize)).CryptBlocks(segment, segment)
|
||||
playlist := "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:3\n#EXT-X-MEDIA-SEQUENCE:0\n#EXT-X-KEY:METHOD=AES-128,URI=\"/api/app/vid/sec\",IV=0x00000000000000000000000000000000\n#EXTINF:3.0,\n/fixture.ts\n#EXT-X-ENDLIST\n"
|
||||
var keyRequests, segmentRequests atomic.Int32
|
||||
d := legacyFixtureDownloader(t, func(request *http.Request) (*http.Response, error) {
|
||||
var body []byte
|
||||
switch request.URL.Path {
|
||||
case "/api/app/vid/h5/m3u8/generated-video":
|
||||
return rankingHTTPResponse(request, http.StatusOK, playlist), nil
|
||||
case "/api/app/vid/sec":
|
||||
keyRequests.Add(1)
|
||||
body = sourceKey
|
||||
case "/fixture.ts":
|
||||
segmentRequests.Add(1)
|
||||
body = segment
|
||||
default:
|
||||
t.Errorf("unexpected request: %s", request.URL.Path)
|
||||
return nil, errors.New("only generated fixture resources are allowed")
|
||||
}
|
||||
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewReader(body)), ContentLength: int64(len(body)), Header: http.Header{"Content-Type": {"application/octet-stream"}}, Request: request}, nil
|
||||
})
|
||||
d.cfg.FFmpeg, d.cfg.Retries = ffmpeg, 1
|
||||
d.cfg.Token = "own-fixture-token"
|
||||
d.cfg.InterfaceKey, d.cfg.ParamKey, d.cfg.ParamIV = fixtureLegacyProtocol.InterfaceKey, fixtureLegacyProtocol.ParamKey, fixtureLegacyProtocol.ParamIV
|
||||
task := Task{DramaID: "legacy-drama", Chapter: Chapter{ID: "episode-1", VideoURL: "generated-video"}, OutPath: filepath.Join(t.TempDir(), "download.mp4")}
|
||||
app := &UIApp{downloader: d}
|
||||
writer := httptest.NewRecorder()
|
||||
streamCtx, streamCancel := context.WithCancel(ctx)
|
||||
defer streamCancel()
|
||||
var duration float64
|
||||
err = app.streamPlayback(streamCtx, streamCancel, writer, task, "", 0, 1, func(value float64) { duration = value })
|
||||
if err != nil || duration <= 0 || writer.Code != http.StatusOK || writer.Header().Get("X-Playback-Source") != "online" || !bytes.Contains(writer.Body.Bytes(), []byte("moof")) {
|
||||
t.Fatalf("source key playback failed: %v, HTTP=%d, duration=%f", err, writer.Code, duration)
|
||||
}
|
||||
if keyRequests.Load() == 0 || segmentRequests.Load() == 0 {
|
||||
t.Fatal("playback did not fetch the source key and generated segment")
|
||||
}
|
||||
keyRequests.Store(0)
|
||||
segmentRequests.Store(0)
|
||||
var completed atomic.Bool
|
||||
err = d.DownloadEpisodeWithProgress(ctx, task, func(progress DownloadProgress) {
|
||||
if progress.Phase == "completed" && progress.Percent == 100 {
|
||||
completed.Store(true)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("source key download failed", err)
|
||||
}
|
||||
output, err := os.ReadFile(task.OutPath)
|
||||
if err != nil || len(output) < 100*1024 || !bytes.Contains(output, []byte("ftyp")) || !bytes.Contains(output, []byte("moov")) || !completed.Load() {
|
||||
t.Fatalf("download did not finish as an MP4: %v, bytes=%d", err, len(output))
|
||||
}
|
||||
if keyRequests.Load() == 0 || segmentRequests.Load() == 0 || d.cfg.AESKeyHex != "" {
|
||||
t.Fatal("download did not use the source-managed key")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user