VCD转录报告.md

VCD 转录报告 — 光盘卷标 TOX_VCD(两盘一套:VCD1 / VCD2)


一、总览

VCD1VCD2
卷标TOX_VCDTOX_VCD
制作Easy CD Creator 4.0,2000-02-21同上(同批)
容量534.9 MiB / 273,858 扇区496.2 MiB / 242,305 扇区
视频段落MUSIC01.DAT + MUSIC02.DATMUSIC01.DAT + MUSIC02.DAT
总时长1:00:35(26:11 + 34:24)53:34(31:44 + 21:50)
可读性0 个坏扇区0 个坏扇区
画面MPEG-1 352×288 PAL、25 fps、逐行同左
声音MPEG-1 Layer II 224 kbps 立体声 44.1 kHz同左
内容学校艺术节演出实况(鼓乐群舞/独唱/合唱/铜管/群舞/重唱/颁奖/谢幕)同类演出实况(主持串场/群舞/钢琴/对唱/独唱/谢幕)
结尾背景板金字「飞翔 青春」+学生举 "SEIZE THE DAY" 字牌红底横幅「华南师大附中第23届青春旋律艺术节」

两盘均为同一家店(TERAOPTIX)同一时期制作的 TOX_VCD,盘面没有任何曲目/标题元数据(VCD/INFO.VCD、ENTRIES.VCD 内只有版本号,无文本字段),活动名是从画面上读出来的。

关于两盘关系:经用户确认,两盘属于同一届(华南师大附中第23届青春旋律艺术节),是同一届艺术节的两次录像(不是同一场简单分卷)。因此两盘文件名统一采用该活动名。

盘面上能看到的差别:两盘结尾谢幕场景不同——VCD1 是蓝色运动服学生举 "SEIZE THE DAY" 红字牌、背景板为金色主题字「飞翔 青春」;VCD2 是各色演出服全体谢幕、背景板为红底白字校名届次横幅。VCD1 画面上没有出现校名届次横幅,其届次无法从盘面自行读出(曾放大颁奖展板等文字,VCD 分辨率下不可辨)——文件名中的届次依据用户确认,本报告如实记录这一来源。


二、产出文件

文件时长大小
VCD1-Part1-华南师大附中第23届青春旋律艺术节-26分11秒.mp426:10.9224 MB
VCD1-Part2-华南师大附中第23届青春旋律艺术节-34分24秒.mp434:23.7279 MB
VCD1-完整版-华南师大附中第23届青春旋律艺术节-60分35秒.mp460:34.6503 MB
VCD2-Part1-华南师大附中第23届青春旋律艺术节-31分44秒.mp431:44.2227 MB
VCD2-Part2-华南师大附中第23届青春旋律艺术节-21分50秒.mp421:49.6158 MB
VCD2-完整版-华南师大附中第23届青春旋律艺术节-53分34秒.mp453:33.9384 MB
VCD1-华南师大附中第23届青春旋律艺术节-内容总览.png—VCD1 内容总览图(12 格带时间码)
VCD2-华南师大附中第23届青春旋律艺术节-内容总览.png—VCD2 内容总览图(12 格带时间码)
VCD2-华南师大附中第23届青春旋律艺术节-背景板横幅.png—活动名证据(背景板放大 4 倍)

命名规则统一为 VCD<碟号>-<PartN|完整版>-华南师大附中第23届青春旋律艺术节-<时长>.mp4。

「完整版」是把该盘两段用 -c copy 无损拼接(不重新编码),拼接点前后解码 0 错误。


三、内容说明

VCD1(1:00:35,MUSIC01.DAT + MUSIC02.DAT)

时间内容
00:00彩条测试图(录像母带常规开头)
00:00–05:00儿童鼓乐+扇舞群舞(红鼓、粉装)
~05:00女声独唱(绿色花饰演出服)
~10:00合唱(男生白衬衫领结)
~15:00少年铜管独奏
~20:00群舞
~25:00女声独唱(白色长裙)+伴舞
~30:00男女重唱(折扇)
~40:00蓝色布光下的双人演出
~45:00童声合唱(黑白制服)
~50:00颁奖环节(学生举奖杯展板)
结尾全体谢幕:学生举红牌拼出 "SEIZE THE DAY",背景板金字「飞翔 青春」

VCD2(53:34,MUSIC01.DAT + MUSIC02.DAT)

时间内容
00:00女声独唱(红装)
~04:28舞蹈
~08:56主持人串场
~13:24群舞(粉色伞/扇)
~17:52钢琴独奏
~26:48校服男女对唱/朗诵
~31:16双人舞
~35:44男女对唱(黄裙)
~40:12男声独唱
~44:40蓝色布光下的群像节目
结尾全体谢幕,背景板红底横幅「华南师大附中第23届青春旋律艺术节」

(时间点为每 4 分半抽一帧的粗定位,用于快速跳转;两张「内容总览.png」即这些帧的拼图。)


四、技术难点与解决过程(关键部分)

这两张光盘不能直接用 mount + cp/ffmpeg 转录,遇到三层问题,逐层解决如下。两盘情况完全相同。

问题 1:内核无法读取数据,报 Illegal mode for this track

现象:mount -t iso9660 /dev/sr0 能挂载、能列目录,但一读 MPEGAV 里的大文件就立刻失败:

$ dd if=/mnt/vcd/... bs=2048 count=200 conv=noerror,sync
dd: 读取时出错: 输入/输出错误        # 每个块都失败,出来的全是 0 字节

dmesg 关键信息:

sr 6:0:0:0: [sr0] tag#0 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_OK
Sense Key : Illegal Request [current]
Add. Sense: Illegal mode for this track          ← ASC 0x64
CDB: Read(10) 28 00 00 00 04 00 00 00 02 00 00 00

注意 Add. Sense: Illegal mode for this track(ASC/ASCQ = 0x64/0x00)不是坏道(坏道会报 Medium error / Unrecoverable read error,ASC 0x11)。它表示驱动器拒绝了这个读取命令的模式,且 100% 复现。

原因:VCD 的视频/音频数据存放在 CD-ROM XA Mode 2 Form 2 扇区里——每个 2352 字节扇区中用户数据是 2324 字节(12 字节同步头 + 4 字节地址头 + 8 字节 subheader + 2324 字节数据 + 4 字节 EDC)。 而 Linux 的 sr 驱动只会用 READ(10) 按 2048 字节(Mode 1 / Mode 2 Form 1) 读,驱动器发现扇区实际是 Form 2,直接回 Illegal mode for this track。于是:

验证:用 SG_IO 绕过内核,对比两种 SCSI 命令

写 sgio.py 用 ioctl(SG_IO) 直接下发 SCSI 命令,在同一个 LBA 上对比(两盘结果一致):

LBAREAD(10)(2048 B,内核用的)READ CD(0xBE,原始 2352 B)
0 / 16OK(16 是 PVD)OK
1000、8850、53850、123450、200000、270000…✗ CHECK CONDITIONOK 2352 B

结论明确:READ(10) 全盘失败,READ CD 原始读取全盘成功。

解决:raw 抓取 + 按扇区取 2324 字节负载

  1. 用 READ CD (0xBE) 带 SYNC|HEADER|USERDATA|EDC/ECC 标志(CDB byte9 = 0xF8),把全盘按 2352 字节/扇区镜像成 *-disc.img(VCD1 644 MB、VCD2 570 MB);
  2. 按 ISO9660 解析出各 .DAT 的起始 LBA 与扇区数;
  3. 对每个扇区跳过 24 字节(同步头+地址头+subheader),取 2324 字节数据拼接,得到标准 MPEG-1 Program Stream。

口径自校验:提取出的流以 00 00 01 BA(MPEG pack start code)开头;码率反算 VCD1 为 273,960,092 B ÷ 1570.8 s = 174.3 KB/s = 1.394 Mbps,与 VCD 标准(1.15 Mbps 视频 + 224 kbps 音频 ≈ 1.374 Mbps)吻合 → 证明按 2324 取字节是对的。

问题 2:单次 SG_IO 传输有 120 KB 上限

一开始按 64 扇区/次(150 KB)读取,结果每个块都失败并退化成逐扇区读,速度只有 0.18 MB/s(预计要 53 分钟)。查明原因:

$ cat /sys/block/sr0/queue/max_sectors_kb
120

实测阈值正好卡在这里:52 扇区(122,304 B)成功,53 扇区(124,656 B)报 EIO。改成每次 52 扇区后,速度升到 2.6–2.9 MB/s(24x),每盘约 3.7 分钟读完。

问题 3:TOC 报的 lead-out 是错的,不能作为抓取边界

两盘的 TOC 都不可信:

TOC 报告的 lead-out实际最后一个文件末端设备容量
VCD1LBA 240,825LBA 273,737LBA 273,858
VCD2LBA 237,975LBA 242,184LBA 242,305

若按 VCD1 的 lead-out 截断,会丢掉 MUSIC02.DAT 结尾 21 分钟内容。所以抓取范围取 max(所有文件末端, 设备容量),两盘都 100% 读取成功。

附带发现:挂载看到的目录树是错的

mount 后 ls 得到的是「mpegav/ 空、vcd/music01.dat、segment/entries.vcd」,而直接从原始镜像解析 ISO9660 得到的正确结构是「MPEGAV/MUSIC01.DAT、VCD/INFO.VCD、SEGMENT/ 空」——挂载视图的目录内容整体错位(与 READ(10) 的异常同源),且该盘无 Joliet / Rock Ridge,本不该出现小写文件名。所以本次一切结论都以自行解析 raw 镜像为准,不采信挂载结果。


五、转码参数与质量校验

源:MPEG-1 video 352×288、SAR 178:163(= 4:3)、25 fps、逐行、1130.8 kbps + MPEG-1 Layer II audio 224 kbps 立体声 44.1 kHz

命令:

ffmpeg -i musicNN.mpg \
  -vf "scale=384:288:flags=lanczos,setsar=1" \
  -c:v libx264 -preset slow -crf 18 -pix_fmt yuv420p \
  -c:a aac -b:a 192k -ac 2 \
  -movflags +faststart out.mp4

参数理由:

校验结果:

检查项VCD1VCD2
提取出的 MPEG 全流解码(-err_detect explode -f null -)0 错误0 错误
成品时长 vs 源时长分卷 1570.92 / 2063.68 s,合并 3634.62 s分卷 1904.20 / 1309.64 s,合并 3213.86 s
合并版拼接点解码0 错误0 错误
成品规格H.264 High / yuv420p / 384×288 DAR 4:3 / AAC-LC 44.1 kHz 立体声同左

六、原始素材与脚本(DSH 机 ~/Downloads/vcd-rip/)

无损母版全部保留,便于日后重做(更高码率、加字幕、刻盘回 VCD 等):

路径内容
disc.img (644 MB) / VCD2-disc.img (570 MB)整盘 raw 镜像,2352 B/扇区
mpeg/music01.mpg (274 MB)、mpeg/music02.mpg (360 MB)VCD1 无损 MPEG-PS 母版
mpeg-VCD2/music01.mpg (332 MB)、music02.mpg (228 MB)VCD2 无损 MPEG-PS 母版
sgio.pySG_IO 底层工具(TOC 读取 / READ CD 原始读扇区 / 分块与重试)
disc_scan.py直接从光盘读 ISO9660 元数据,推算抓取范围
rip_raw.py整盘 raw 抓取(分块、重试、错误扇区记录)
iso_parse.py从 raw 镜像解析 ISO9660(Form 1 负载在偏移 24)
extract_vcd.py按扇区取 2324 字节负载,拼出 MPEG
run_disc.sh阶段一:扫描 → 抓取 → 提取 → 打印流信息(bash run_disc.sh VCD2)
encode_disc.sh阶段二:转 MP4 + 合并完整版(bash encode_disc.sh VCD2)
convert_mp4.shVCD1 最早的手工转码脚本(保留备查)
disc.err.json / VCD2-disc.err.json坏扇区清单(两盘内容均为 "errors": [])

七、复现步骤(以后遇到同类 VCD 一条龙)

cd ~/Downloads/vcd-rip

# 阶段一:扫描光盘 → 整盘 raw 抓取 → 提取 MPEG-PS → 打印流信息
bash run_disc.sh VCD3          # 换成你要的前缀

# (可选)抽帧确认内容、确认是否隔行
ffmpeg -ss 600 -i mpeg-VCD3/music01.mpg -frames:v 1 -q:v 2 -y /tmp/f.jpg
ffmpeg -ss 600 -i mpeg-VCD3/music01.mpg -vf idet -frames:v 300 -f null -

# 阶段二:转 MP4(384x288 / H.264 CRF18 / AAC 192k)+ 合并完整版
bash encode_disc.sh VCD3

通用经验(下次遇到同类盘可直接套用)

  1. Add. Sense: Illegal mode for this track(ASC 0x64)≠ 坏盘,是扇区模式不匹配;VCD/SVCD/CD-i 的 MPEG 数据是 Mode 2 Form 2,普通 mount + cp 一定读不出来。
  2. 不要依赖 mount 的目录树,这类盘必须从 raw 镜像自己解析 ISO9660(本次挂载结果整体错位)。
  3. 不要依赖 TOC 的 lead-out 作为抓取终点(本次两盘都错,VCD1 差 33,000 扇区);用「设备容量」与「ISO 声明的文件末端」取大者。
  4. READ CD (0xBE) 原始 2352 字节读取是这类盘的万能钥匙;系统没有 readcd/cdrdao 时,用 Python + SG_IO 几十行就能实现。
  5. 单次 SG_IO 传输受 max_sectors_kb 限制(本机 120 KB);超限直接报 EIO 而不自动截断,极易被误判成坏道。
  6. VCD 的 .DAT 在 ISO 里声明的大小是「扇区数 × 2048」,但每个扇区实际携带 2324 字节——拼接时必须按 2324 取,否则流会错乱。
  7. VCD 画面像素是非方形的(352×288 实际显示为 384×288),转录时要么保留 SAR,要么缩成 384×288 方形像素,否则画面会被压扁。

八、下载地址

下载此文件