You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

65 lines
2.4 KiB

import 'package:eaimar/data/utils/ogg_opus_duration.dart';
import 'package:flutter_test/flutter_test.dart';
/// Ogg Opus 时长解析的金测试。
///
/// 用例是 **EaiRec 支架真机 TF 卡上那条录音的原始字节**(2026-09-10 拉下来的
/// `MIC0005.OGG`,38400 字节),不是构造出来的样本。改这里的解析逻辑前先看
/// 这个测试为什么红。
void main() {
// 文件头 48 字节:第一个 page(含 OpusHead)
const head = <int>[
0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17,
0x30, 0x89, 0x00, 0x00, 0x00, 0x00, 0x21, 0x8B,
0x55, 0x8A, 0x01, 0x13, 0x4F, 0x70, 0x75, 0x73,
0x48, 0x65, 0x61, 0x64, 0x01, 0x01, 0x00, 0x00,
0x80, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F,
];
// 最后一个 page 的头 27 字节:granulepos = 0x0006B580 = 439680
const tail = <int>[
0x4F, 0x67, 0x67, 0x53, 0x00, 0x04, 0x80, 0xB5, //
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17,
0x30, 0x89, 0x1E, 0x00, 0x00, 0x00, 0xF3, 0xEE,
0x70, 0xEC, 0x0A,
];
test('真机 MIC0005.OGG:granulepos 439680 → 9.16 秒', () {
final secs = oggOpusSecondsFrom(head, tail);
expect(secs, isNotNull);
// 439680 / 48000 = 9.16
expect(secs!, closeTo(9.16, 0.01));
});
test('⚠️ 除数必须是 48000 而不是音频的 16000', () {
// OpusHead 里写的输入采样率是 16000(head 偏移 40 起的 0x00003E80),
// 但 granulepos 恒以 48kHz 计。用 16000 去除会得到 27.48s —— 3 倍。
final secs = oggOpusSecondsFrom(head, tail)!;
expect(secs, lessThan(10));
});
test('pre-skip 要从 granulepos 里扣掉', () {
// 把 OpusHead 的 pre-skip 从 0 改成 48000(1 秒)
final h = List<int>.from(head);
h[38] = 0x80; // pre-skip 低字节
h[39] = 0xBB; // 0xBB80 = 48000
final secs = oggOpusSecondsFrom(h, tail)!;
expect(secs, closeTo(8.16, 0.01));
});
test('不是 Ogg 就返回 null,不猜', () {
// RIFF/WAV 头
const wav = <int>[0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00];
expect(oggOpusSecondsFrom(wav, tail), isNull);
});
test('granulepos 不大于 pre-skip 时返回 null(空文件/只有头)', () {
// 最后一个 page 的 granulepos 置 0
final t = List<int>.from(tail);
for (var i = 6; i < 14; i++) {
t[i] = 0;
}
expect(oggOpusSecondsFrom(head, t), isNull);
});
}