feat(media): C1 转码 worker(独立进程消费 transcode_task,产出四档 HLS + 封面)
为什么:codex-研发任务清单阶段 C 第一项要求消费 transcode_task 产出多码率 HLS + 截图; 勘察发现除 worker 外还缺两件——TranscodeService.enqueue 没有任何调用方(队列永远空)、 media_asset.hls_url/poster_url 没人回写,所以一并补齐,已有能力(条件更新认领 / 3 次重试)直接复用。 - 新增 TranscodeRunner 端口 + FfmpegTranscodeRunner(每档一条 ffmpeg 出 VOD HLS + 手写 master.m3u8 + 抽封面 + ffprobe 读时长) - 新增 TranscodeWorker:claimNext → 转码 → 登记 media_rendition → 媒资 ready;失败记原因、第 3 次才落 FAILED 并把媒资置 failed - 存储统一走 MediaStorageService(新增 fileFor / objectKeyOf,含目录穿越校验),worker 不自己拼磁盘路径 - 队列隔离:app-api/admin-api 默认 worker-enabled=false,新增独立模块 fruitshorts-transcode-worker(随机端口) - MediaService 上传完成对视频素材入队并置 transcoding(图片仍直接 ready) - 实测抓到并修掉两个真问题:① scale 未加 force_divisible_by=2 时 854x480 算出奇数宽 853,libx264 拒绝转码 ② 只写 -hls_time 不强制关键帧时 8 秒片源只出 1 个分片,补 -force_key_frames 后按 6s 正确切分 - README 开发环境补 ffmpeg 9.0.2(只有 worker 需要) - 实测:mvn test 83 条全过、0 skipped(新增 worker 12 条,含真实 ffmpeg smoke); 端到端:正常源 SUCCESS + 4 档 rendition + 媒资 ready;坏源重试 3 次后 FAILED
This commit is contained in:
1 parent
1dc97b4bf6
commit
9f76302e53
15 files changed
+1136
-3
No files matched your search
+26
@@ -0,0 +1,26 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
package com.fruitshorts.worker;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 转码 worker 独立进程。
|
||||
*
|
||||
* <p>为什么单独一个进程:`backend-design-phase1.md` §8.5 要求"转码队列与在线服务隔离,避免抢占资源"。
|
||||
* app-api / admin-api 里的 `fruitshorts.media.transcode.worker-enabled` 保持 false,
|
||||
* 只有本进程把它打开,所以**跑不跑转码不影响 /app/** 的联调与播放**。
|
||||
*
|
||||
* <p>启动:`java -jar fruitshorts-transcode-worker/target/fruitshorts-transcode-worker-1.0.0-SNAPSHOT.jar`
|
||||
* (需要 ffmpeg / ffprobe 在 PATH 里,或用 `fruitshorts.media.transcode.ffmpeg-path` 指定绝对路径)。
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = "com.fruitshorts")
|
||||
@MapperScan("com.fruitshorts.**.dal.mapper")
|
||||
public class TranscodeWorkerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TranscodeWorkerApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
# Author: fruit.com.cn
|
||||
#
|
||||
# 转码 worker 独立进程:只做队列消费,不对外提供接口。
|
||||
# 端口用 0(随机)——本进程不需要被访问,也不会跟 8080/8081 抢端口。
|
||||
server:
|
||||
port: 0
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: fruitshorts-transcode-worker
|
||||
autoconfigure:
|
||||
exclude: org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
|
||||
datasource:
|
||||
driver-class-name: org.postgresql.Driver
|
||||
url: jdbc:postgresql://127.0.0.1:5432/fruitshorts
|
||||
username: ${DB_USER:fruitshorts}
|
||||
password: ${DB_PASSWORD:fruitshorts}
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
global-config:
|
||||
banner: false
|
||||
db-config:
|
||||
# 与两个入口保持一致:主键由应用侧雪花生成(S0),否则 worker 插入 media_rendition 会没有 id
|
||||
id-type: assign_id
|
||||
logic-delete-field: deleted
|
||||
logic-delete-value: 1
|
||||
logic-not-delete-value: 0
|
||||
sequence:
|
||||
# 单机固定值;多实例部署前改成按实例分配
|
||||
worker-id: 2
|
||||
datacenter-id: 1
|
||||
|
||||
fruitshorts:
|
||||
media:
|
||||
# 必须与 app-api/admin-api 指向同一个存储根,worker 才找得到上传后的源文件
|
||||
root-dir: ./data/uploads
|
||||
public-prefix: /files
|
||||
transcode:
|
||||
# 只有本进程打开转码(§8.5 隔离要求)
|
||||
worker-enabled: true
|
||||
poll-delay-ms: 3000
|
||||
ffmpeg-path: ${FFMPEG_PATH:ffmpeg}
|
||||
ffprobe-path: ${FFPROBE_PATH:ffprobe}
|
||||
segment-seconds: 6
|
||||
poster-second: 1
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.fruitshorts: info
|
||||
Reference in new issue
Block a user