108 lines
4.7 KiB
Go
108 lines
4.7 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestMergeRealHEAACAudioPreservesVideo(t *testing.T) {
|
|
fixture := os.Getenv("JUKU_MERGE_HEAAC_FIXTURE")
|
|
if fixture == "" {
|
|
t.Skip("set JUKU_MERGE_HEAAC_FIXTURE to a local HE-AAC or HE-AACv2 sample; no network downloads")
|
|
}
|
|
ffmpeg, err := exec.LookPath("ffmpeg")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
work := t.TempDir()
|
|
base, minority := filepath.Join(work, "he-aac.mp4"), filepath.Join(work, "lc.mp4")
|
|
mergeFixtureCommand(t, ffmpeg, "-f", "lavfi", "-i", "testsrc2=size=320x180:rate=25:duration=2", "-i", fixture,
|
|
"-map", "0:v:0", "-map", "1:a:0", "-t", "2", "-c:v", "libx264", "-preset", "veryfast", "-threads:v", "2", "-pix_fmt", "yuv420p", "-c:a", "copy", base)
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
defer cancel()
|
|
info, err := probeMergeMedia(ctx, ffmpeg, base)
|
|
if err != nil || !strings.HasPrefix(info.audioProfile, "HE-AAC") {
|
|
t.Fatalf("fixture is not real HE-AAC audio: %s %v", info.audioProfile, err)
|
|
}
|
|
mergeFixtureCommand(t, ffmpeg, "-i", base, "-c:v", "copy", "-c:a", "aac", "-profile:a", "aac_low", minority)
|
|
baseFrames := mergeFixtureFrames(t, ffmpeg, base)
|
|
for _, mixed := range []bool{false, true} {
|
|
name := "homogeneous"
|
|
paths := []string{base, base, base}
|
|
profile := info.audioProfile
|
|
if mixed {
|
|
name, paths[1], profile = "mixed", minority, "LC"
|
|
}
|
|
t.Run(name, func(t *testing.T) {
|
|
output := filepath.Join(work, name+"-merged.mp4")
|
|
method, err := mergeMediaFiles(ctx, ffmpeg, paths, output, nil)
|
|
if err != nil || strings.Contains(method, "转视频") || strings.Contains(method, "统一音轨为 AAC-LC") != mixed {
|
|
t.Fatal("real HE-AAC merge failed or unnecessarily encoded video", method, err)
|
|
}
|
|
merged, err := probeMergeMedia(ctx, ffmpeg, output)
|
|
if err != nil || merged.audioProfile != profile || merged.sampleRate != info.sampleRate {
|
|
t.Fatalf("unexpected merged audio: %+v %v", merged, err)
|
|
}
|
|
want := append(append(append([]string(nil), baseFrames...), baseFrames...), baseFrames...)
|
|
if frames := mergeFixtureFrames(t, ffmpeg, output); !reflect.DeepEqual(frames, want) {
|
|
t.Fatalf("copied video changed: got %d frames, want %d", len(frames), len(want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMergeMixedHEAACUsesUniformLCAudioAndCopiesVideo(t *testing.T) {
|
|
for _, profile := range []string{"HE-AAC", "HE-AACv2"} {
|
|
t.Run(profile, func(t *testing.T) {
|
|
base := mergeMediaInfo{width: 1080, height: 1920, videoCodec: "h264", videoProfile: "High",
|
|
pixelFormat: "yuv420p", sar: "1/1", videoTB: "1/90000", videoExtra: "unchanged-video",
|
|
audio: true, audioCodec: "aac", audioProfile: profile, audioExtra: "he-configuration",
|
|
sampleRate: 48000, layout: "stereo", audioTB: "1/48000", duration: time.Minute}
|
|
minority := base
|
|
minority.audioProfile, minority.audioExtra = "LC", "lc-configuration"
|
|
infos := []mergeMediaInfo{base, minority, base}
|
|
plan := planMergeMedia(infos)
|
|
if plan.videoCount != 0 || plan.audioCount != len(infos) || !plan.audioFallback || plan.audio.audioProfile != "LC" {
|
|
t.Fatalf("mixed HE-AAC was not normalized without video encoding: %+v", plan)
|
|
}
|
|
for index, step := range plan.steps {
|
|
args, err := plan.prepareArgs("input.mp4", "output.mp4", infos[index], step)
|
|
joined := strings.Join(args, " ")
|
|
if err != nil || !strings.Contains(joined, "-c:v copy") || !strings.Contains(joined, "-profile:a aac_low") ||
|
|
strings.Contains(joined, "libx264") || strings.Contains(joined, "-vf") {
|
|
t.Fatal("audio fallback encoded video or kept mixed AAC profiles", joined, err)
|
|
}
|
|
}
|
|
same := planMergeMedia([]mergeMediaInfo{base, base, base})
|
|
if same.audioCount != 0 || same.videoCount != 0 || same.audioFallback {
|
|
t.Fatal("homogeneous HE-AAC should stay lossless", same.description())
|
|
}
|
|
minority.audio = false
|
|
if silent := planMergeMedia([]mergeMediaInfo{base, minority, base}); silent.audioCount != 3 || silent.videoCount != 0 {
|
|
t.Fatal("silent episode did not normalize the full audio track")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMergeFailureRetainsProgress(t *testing.T) {
|
|
app := &UIApp{statePath: filepath.Join(t.TempDir(), "ui-state.json")}
|
|
result := uiMergeResult{DramaID: historyFixtureDramaID, DramaTitle: "合成进度测试"}
|
|
app.setMergeState(result, "running", 41, false, false)
|
|
result.Error = "fixture failure"
|
|
app.setMergeState(result, "failed", 0, false, false)
|
|
if state := app.merges[result.DramaID]; state.Progress != 41 || state.Status != "failed" || state.Error == "" {
|
|
t.Fatal("failure reset the completed work", state)
|
|
}
|
|
app.setMergeState(result, "running", 1, false, false)
|
|
if app.merges[result.DramaID].Progress != 1 {
|
|
t.Fatal("a new attempt inherited the previous progress")
|
|
}
|
|
}
|