合并先在本地做完再落到成片目录;头像支持 gif 和静态 webp
起因是核查合并落盘和头像处理时发现的两处问题:
1) 成片落盘:ffmpeg 原来是直接写目标目录(automerge.go 里那句注释「不再事后再
移动一次」)。成片目录可能是网络盘或外接盘,写到一半失败会在那里留下一个
看起来正常的半成品。现在改成先本地合成、完成后再落盘:
- 输出先写到弹幕文件所在目录(本地)的 acfunlive_chunk_merging_*
- 写完后由 placeOutput 落盘:同盘直接 os.Rename(瞬时),跨盘退回拷贝;
拷贝中途失败留下的是 *.acfunlive.part,一眼看得出不是成品
- 成功或失败都会清掉本地临时文件,失败时两个目录都不留东西
- 临时文件沿用 chunkTempPrefix,不再出现在文件管理的列表里(listFiles 过滤)
- 启动时清理上次残留;删不掉时提示一句 —— 强杀程序不会连带杀掉 ffmpeg
子进程,它可能还在写这个文件,这种情况下次启动会再清一次
2) 头像:downloadAvatar 只注册了 jpeg 和 png 解码器,AcFun 上的 gif 头像必然
解码失败,静态 webp 同样。加上 _ "image/gif"(多帧取第一帧)和
_ "golang.org/x/image/webp"(x/image 本来就是直接依赖,零新增模块)。
动画 webp 两条路都解不开(x/image 报 invalid format,ffmpeg 5.0 也不行),
保持只烧文字。解码失败时错误信息带上 Content-Type,方便排查是哪张图。
在隔离实例上用真 ffmpeg 跑了真合并验证:gif 头像和 webp 头像都进了缓存并烧进
画面(抽帧确认圆形头像);日志里 ffmpeg 的输出是 record 下的临时文件,完成后
才出现在 merged;坏视频合成失败后成片目录没有半成品、录播目录零残留;合并
中途强杀留下的临时文件在下次启动时被清掉。
新增 output_test.go(7 个)和 avatar_test.go(3 个);go test ./...、gofmt、
go vet 干净。
This commit is contained in:
+15
-2
@@ -11,6 +11,7 @@ import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
@@ -26,6 +27,7 @@ import (
|
||||
"time"
|
||||
|
||||
xdraw "golang.org/x/image/draw"
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
// 头像缓存目录名
|
||||
@@ -687,9 +689,9 @@ func downloadAvatar(uid int64, url, path string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
src, _, err := image.Decode(bytes.NewReader(data))
|
||||
src, err := decodeAvatar(data, resp.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析失败(可能是 webp 格式):%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := os.Create(path)
|
||||
@@ -700,6 +702,17 @@ func downloadAvatar(uid int64, url, path string) error {
|
||||
return png.Encode(out, circleAvatar(src, avatarCacheSize))
|
||||
}
|
||||
|
||||
// 解码头像图片。AcFun 的头像里混着 gif 和 webp:
|
||||
// gif 由标准库解码,多帧的取第一帧;webp 只支持静态的,动画 webp 会报错,
|
||||
// 这种情况下这个用户就不烧头像,只烧文字。
|
||||
func decodeAvatar(data []byte, contentType string) (image.Image, error) {
|
||||
src, _, err := image.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("图片解析不了(Content-Type: %s):%v", contentType, err)
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
||||
// uid → 头像链接的记忆,包含查不到的结果,避免同一个 uid 反复去问
|
||||
var avatarURLMemo struct {
|
||||
sync.Mutex
|
||||
|
||||
Reference in New Issue
Block a user