From a4a6e2f2d69003e744d081fc1b277bcd0a4196bc Mon Sep 17 00:00:00 2001 From: Deivid Soto Date: Thu, 11 Jun 2026 00:05:50 +0200 Subject: [PATCH] fix(stream): no copiar AAC multicanal en modo copy (WebKit lo rechaza igual) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El downmix estéreo del re-encode (f89396c) dejaba un agujero simétrico: una fuente cuyo audio YA es AAC 5.1 se copiaba tal cual, y WebKit rechaza el AAC multicanal en el primer segmento exactamente igual que el re-encodeado. Copy de audio ahora solo cuando la pista es AAC con ≤2 canales; cualquier otra cosa (no-AAC, AAC 5.1+, o canales desconocidos en el probe — fail-safe) re-encodea a AAC estéreo 48k. La pista multicanal original queda intacta para reproductor externo. Test smoke nuevo: fuente AAC 5.1 → re-encode. --- internal/engine/hls.go | 18 ++++++++++-------- internal/engine/hls_copy_smoke_test.go | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/internal/engine/hls.go b/internal/engine/hls.go index 4cf6576..3add225 100644 --- a/internal/engine/hls.go +++ b/internal/engine/hls.go @@ -1959,20 +1959,22 @@ func buildHLSCopyArgs(cfg HLSSessionConfig, probe *StreamProbe, tmpDir string) [ args = append(args, "-tag:v", "hvc1") } - // Audio: copy when the SELECTED track is already AAC, else AAC 192k. - // (fMP4 HLS carries AAC universally; EAC3/DTS/TrueHD do not.) + // Audio: copy ONLY when the selected track is AAC with ≤2 channels — + // WebKit/Apple HLS rejects multichannel AAC at the first media segment + // (observed via the Safari access log: master → index → init → seg-0 + // fetched twice, then silence — every 5.1 movie failed on iPhone while + // stereo-AAC episodes played). Anything else (non-AAC, or AAC 5.1+) is + // re-encoded mirroring the encode path exactly: AAC stereo 48k. The + // original multichannel track stays intact for external players. audioCodec := probe.AudioCodec + audioChannels := 0 if audioIdx < len(probe.AudioTracks) { audioCodec = probe.AudioTracks[audioIdx].Codec + audioChannels = probe.AudioTracks[audioIdx].Channels } - if strings.EqualFold(audioCodec, "aac") { + if strings.EqualFold(audioCodec, "aac") && audioChannels > 0 && audioChannels <= 2 { args = append(args, "-c:a", "copy") } else { - // Mirror the encode path exactly: AAC stereo 48k. WITHOUT -ac 2 a 5.1 - // source produces 6-channel ffmpeg-native AAC, which WebKit/Apple HLS - // rejects at the first media segment (observed via Safari access log: - // master → index → init → seg-0 fetched twice, then silence — every - // 5.1 movie failed on iPhone while stereo-AAC episodes played). args = append(args, "-c:a", "aac", "-b:a", "192k", "-ar", "48000", "-ac", "2") } diff --git a/internal/engine/hls_copy_smoke_test.go b/internal/engine/hls_copy_smoke_test.go index 8bc867c..df15eb4 100644 --- a/internal/engine/hls_copy_smoke_test.go +++ b/internal/engine/hls_copy_smoke_test.go @@ -205,6 +205,25 @@ func TestHLSCopy_Hevc10Eac3_IncidentShape(t *testing.T) { } } +func TestHLSCopy_Aac51MustReencode(t *testing.T) { + // AAC is NOT copy-safe when multichannel: WebKit rejects 6-channel AAC at + // the first media segment exactly like re-encoded 5.1. Source AAC 5.1 → + // must re-encode to stereo, never copy. + rt := copyTestRuntime(t) + src := genSource(t, rt, "aac51.mkv", + []string{"-c:v", "libx264", "-preset", "ultrafast", "-pix_fmt", "yuv420p"}, + []string{"-c:a", "aac", "-ac", "6", "-b:a", "256k"}, 8) + s, pl := runCopySession(t, rt, src, 0) + assertCopyOutput(t, rt, s, pl, "h264", "aac", 8) + args := buildHLSCopyArgs(s.cfg, s.probe, s.tmpDir) + if containsSeq(args, "-c:a", "copy") { + t.Errorf("AAC 5.1 must NOT be copied (WebKit rejects multichannel AAC), args: %v", args) + } + if !containsSeq(args, "-ac", "2") { + t.Errorf("AAC 5.1 must re-encode to stereo, args: %v", args) + } +} + func TestHLSCopy_ResumeStartSec(t *testing.T) { rt := copyTestRuntime(t) src := genSource(t, rt, "resume.mkv",