ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

stb_vorbis.c 如何用 stb_vorbis_seek 与 stb_vorbis_seek_frame 定位解码位置

stb_vorbis.c 如何用 stb_vorbis_seek 与 stb_vorbis_seek_frame 定位解码位置 stb_vorbis.c 如何用 stb_vorbis_seek 与 stb_vorbis_seek_frame 定位解码位置【免费下载链接】stbstb single-file public domain libraries for C/C项目地址: https://gitcode.com/GitHub_Trending/st/stb在用 stb 的 stb_vorbis.c仓库当前版本 v1.22公共领域许可解码 Ogg Vorbis 音频时常见的任务不是从头解码整个文件而是跳到指定采样位置再继续解码——比如播放器跳转到某个时间点、只抽取文件的某一段。stb_vorbis 的 Pulling Input API 提供了两个 seek 入口stb_vorbis_seek定位到精确采样stb_vorbis_seek_frame定位到包含目标采样的那一帧。本文基于文件头部的 API 注释stb_vorbis.c 第 298–308 行和仓库自带的 tests/vorbseek/vorbseek.c 测试说明如何完成打开文件 → 计算目标采样 → seek → 解码并核对结果这条路径。两个 seek 函数的区别与选择API 注释给出的规则很明确int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number)把解码位置移到大约sample_number。调用成功后下一次stb_vorbis_get_frame_*()返回的帧会包含指定采样但帧起点可能略早于目标值。int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number)精确版本。调用成功后下一次stb_vorbis_get_samples_*()的输出从指定采样开始。注释原话如果你使用get_samples_*但不要求恰好定位到目标采样也可以直接用seek_frame()——即按帧对齐场景下seek_frame是更省事的调用。stb_vorbis_seek_start(f)等价于stb_vorbis_seek(f, 0)用于回到文件开头。因此选择依据是解码接口你按帧取数据stb_vorbis_get_frame_float等且能接受帧边界对齐就用stb_vorbis_seek_frame你用stb_vorbis_get_samples_short_interleaved、stb_vorbis_get_samples_float_interleaved这类按采样取数的接口并需要精确起点就用stb_vorbis_seek。两个函数都只在 Pulling API 下可用内部实现检测到 Pushdata 模式句柄时直接报VORBIS_invalid_api_mixing错误。Pushdata 模式下的seek是另一套机制seek 你的数据源后调用stb_vorbis_flush_pushdata()再靠stb_vorbis_get_sample_offset判断位置不属于本文的采样精确 seek 范畴。准备条件编译与打开文件stb_vorbis 是单文件库头文件部分和实现都在 stb_vorbis.c 一个文件里。仓库自己的构建方式见 tests/Makefile是把stb_vorbis.c直接作为编译单元加入$(CC) $(INCLUDES) $(CFLAGS) ../stb_vorbis.c test_c_compilation.c ... -lm多文件项目可以沿用仓库测试 tests/vorbseek/vorbseek.c 的写法在只需要声明的翻译单元中#define STB_VORBIS_HEADER_ONLY #include stb_vorbis.c打开 Vorbis 文件用 Pulling API 的任一入口参数alloc_buffer传NULL即使用默认malloc()/alloca()分配stb_vorbis *f stb_vorbis_open_filename(input.ogg, error, NULL);stb_vorbis_open_filename通过fopen()打开文件失败返回NULL并设置*error可能为VORBIS_file_open_failure。stb_vorbis_open_memory(data, len, error, NULL)从内存解码要求传入完整的数据流。stb_vorbis_open_file(FILE *f, ...)注意 stb_vorbis 会拥有这个流你在调用之间手动 seek 它会导致库状态错乱且文档说明它对 seek 操作的假设是从起始点之后拥有整个文件的剩余部分想限定范围应改用stb_vorbis_open_file_section。主路径计算目标采样、执行 seek、继续解码目标位置的单位是采样号不是字节、不是帧号。用stb_vorbis_get_info(f)拿到sample_rate和channels用stb_vorbis_stream_length_in_samples(f)/stb_vorbis_stream_length_in_seconds(f)拿总长然后按目标秒数 × sample_rate换算出sample_number该值必须是unsigned int可表示的范围见后文限制。完整最小示例单个 C 文件编译时与 stb_vorbis.c 同目录或加入编译#include stdio.h #include stb_vorbis.c int main(int argc, char **argv) { int error, channels, n; float **output; stb_vorbis_info info; unsigned int target; stb_vorbis *f; f stb_vorbis_open_filename(argv[1], error, NULL); if (f NULL) { fprintf(stderr, open failed, error %d\n, error); return 1; } info stb_vorbis_get_info(f); // 跳到 10 秒处换算成采样号 target (unsigned int)(10 * info.sample_rate); if (target stb_vorbis_stream_length_in_samples(f)) { stb_vorbis_close(f); return 1; // 超出总长会报 VORBIS_seek_invalid } // 需要精确采样起点且随后用 get_samples_* 取数 if (!stb_vorbis_seek(f, target)) { fprintf(stderr, seek failed, error %d\n, stb_vorbis_get_error(f)); stb_vorbis_close(f); return 1; } // 从 target 采样开始取 1024 帧短整型交织数据 n stb_vorbis_get_samples_short_interleaved(f, info.channels, output_buf, output_size); // ... 使用 n 帧数据 ... stb_vorbis_close(f); return 0; }output_buf/output_size是调用方提供的输出缓冲区及其容量以 short 或 float 个数计按各接口注释传入即可返回值是每通道写入的采样数文件尾部可能少于请求量无数据时返回 0。按帧解码的写法只需把两处换掉seek 换成stb_vorbis_seek_frame(f, target)取数换成stb_vorbis_get_frame_float(f, channels, output)——注意get_frame_*返回的缓冲区由 stb_vorbis 内部持有下次调用前会被覆盖需要及时取走数据。另有一条注释提醒不要混用get_frame_*()与get_samples_*()系列因为后者内部会调用前者。判断 seek 是否成功、失败时读什么错误码两个 seek 函数返回非 0 表示成功0 表示失败vorbseek.c 第 20 行的判法就是if (!stb_vorbis_seek(v, pos))记为一次失败。失败时用stb_vorbis_get_error(f)取出最后错误该函数取走后会清空。与 seek 直接相关的错误码定义在 stb_vorbis.c 的STBVorbisError枚举中VORBIS_seek_invalidseek 目标超过流总长实现中sample_number stream_length时返回该错误VORBIS_seek_without_length文件总长未知无法定位VORBIS_seek_failed页级定位或帧级定位过程失败seek 失败路径会把文件恢复到起点再报错;VORBIS_invalid_api_mixing句柄是 Pushdata 模式不能使用这两个 seekVORBIS_invalid_stream、VORBIS_unexpected_eof等文件本身损坏或被截断时出现。注意stb_vorbis_get_sample_offset(f)不能用来核对 seek 后的位置头文件注释明确标注它在 Pulling API 下 seek 之后 NOT WORKING YET。seek 后位置正确与否要用下一节的解码比对法验证。验证方式采样精确性比对仓库提供了专门的测试 tests/vorbseek/vorbseek.c其用法文档化的命令行是Usage: vorbseek {vorbisfile} [{vorbisfile]*] Tests various seek offsets to make sure theyre sample exact.它的验证思路可以照搬到自己项目里先用stb_vorbis_decode_filename(file, num_chan, samprate, output)把整个文件解一遍作为基准数据重新stb_vorbis_open_filename打开stb_vorbis_seek(v, pos)到目标位置解码一段测试用 3000 采样用memcmp与基准数据从pos*2偏移处开始比对同时检查解码数量不超出num_samples - pos防止越界解码。该测试还限制 Vorbis 文件必须是2 声道if (num_chan ! 2)直接报错退出。另外要说明当前仓库中vorbseek.c的main在整文件解码之后有一个break第 88 行seek 测试循环实际上未运行这份源码的价值在于给出完整可参考的try_seeking验证逻辑而不是拿来直接当回归工具。编译方式可参照 tests/Makefile 的做法$(CC)加-I..并把stb_vorbis.c作为编译单元一起加入。另一个更基础的首次使用验证在 tests/test_vorbis.cSTB_VORBIS_HEADER_ONLY 包含头stb_vorbis_decode_memory整文件解码后检查samples返回值可作为你接入 stb_vorbis 后先跑通的冒烟测试。边界与限制来自文件头部 Limitations 一节和 API 注释seek 时必须知道这些边界不支持 floor 02004 年以前的老 Ogg Vorbis 文件若使用 floor 0打开时即报VORBIS_feature_not_supported根本走不到 seek采样号是 32 位192 kHz 的可 seek 时长上限约 6 小时Ogg 本身支持 64 位文件开头忽略无损截断lossless sample-truncation at beginning ignoredseek 依赖流总长已知总长未知时报VORBIS_seek_without_length单个stb_vorbis*句柄不是线程安全的不能多线程同时从同一句柄解码需要并发时开多个句柄分别解码。需要精确到采样就用stb_vorbis_seekget_samples_*按帧取数且容忍帧边界就用stb_vorbis_seek_frameget_frame_*两条路径共用同一套错误码判定并用整文件解码基准 memcmp比对来确认 seek 后的解码起点确实对准了目标采样。【免费下载链接】stbstb single-file public domain libraries for C/C项目地址: https://gitcode.com/GitHub_Trending/st/stb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表