fq 解码 AVI:样本索引优先级、流信息提取与解码加速实战指南
【免费下载链接】fqfq - jq for binary formats. Tool, language and decoders for working with binary formats.项目地址: https://gitcode.com/gh_mirrors/fq/fq
fq 是面向二进制格式的 jq 工具,内置了对 AVI(Audio Video Interleaved,微软音视频交错容器)的完整解码支持。本篇指南以 format/riff/avi.md 为骨架,结合 format/riff/avi.go 源码与其 测试数据,系统讲解 fq 如何处理 AVI 中冗余的样本索引机制、如何提取指定流的样本数据、如何快速查看流摘要,以及如何通过关闭样本与扩展块解码来大幅加速解码。读完本文,你将能够用 fq 独立完成 AVI 文件的样本抽取、流级元数据分析和性能调优。
fq 中的 AVI 解码器概览
AVI 解码器位于format/riff包内,与 WAV(wav.go)、AIFF(aiff.go)、WebP(webp.go)等格式共享 RIFF 容器基础实现(format/riff/common.go 中的riffDecode负责递归解析 RIFF chunk 结构并处理 16 位字对齐)。
解码器在 format/riff/avi.go 中注册,核心信息如下:
- 格式标识:
format.AVI,描述为 "Audio Video Interleaved"; - 解码入口:
aviDecode,采用小端字节序(d.Endian = decode.LittleEndian); - 默认输入参数(定义于 format/format.go):
decode_samples=true:是否解码样本(samples);decode_extended_chunks=true:是否解码扩展块(extended chunks,即 OpenDML AVIX 后续 RIFF 块);
- 依赖格式组(用于样本深层解码):
format.AVC_AU(H.264 访问单元,对应视频流);format.HEVC_AU(H.265 访问单元);format.MP3_Frame(MP3 音频帧);format.FLAC_Frame(FLAC 音频帧)。
通过fq -h avi可以随时查看解码器的帮助信息,与仓库中的 format/riff/testdata/help_avi.fqtest 输出一致:
avi: Audio Video Interleaved decoder Options ======= decode_extended_chunks=true Decode extended chunks decode_samples=true Decode samples理解 AVI 的样本索引机制:为何存在多种索引
AVI 格式发展历史较长,历史上出现过多种记录媒体样本(音频帧、视频帧)位置的方式,因此一个 AVI 文件可能同时携带多套索引。fq 文档明确说明:
AVI has many redundant ways to index samples so currently
.streams[].sampleswill only include samples using the most "modern" method found in the file. That is in order of stream super index, movi ix index then idx1 index.
即:AVI 拥有许多冗余的样本索引方式,因此当前实现中.streams[].samples只会使用文件中找到的最"现代"的索引方法,优先级从高到低为:
- Stream super index(流超索引 /
indx):即 OpenDMLindx块,位于strl流列表内,属于"现代"索引,支持 64 位偏移与多级子索引; - movi ix index(
ix##块):位于movi数据区内的流索引块,chunk id 形如ix00、ix01,与具体流编号绑定; - idx1 index(
idx1块):传统 AVI 索引,位于文件末尾,采用 32 位偏移的旧式索引。
源码中对应优先级选择的逻辑在 format/riff/avi.go:解码完streams数组后,按顺序尝试三套候选区间——streamIndexSampleRanges(来自indx超索引解析出的子索引)、stream.ixSamples(来自ix块)、idx1Samples(来自idx1),只取第一套非空的作为samples输出,避免同一份样本被重复列出。
这三种索引在解码过程中被分别收集:
indx在strl列表内被解析(format/riff/avi.go),解析出的区间暂存于stream.indexes,随后在输出streams时通过aviDecodeChunkIndex逐级展开为子索引区间;ix块在movi区遇到形如ixNN的 chunk id 时解析(format/riff/avi.go),区间累积到对应流(streams[index])的ixSamples;idx1块在 RIFF 顶层解析(format/riff/avi.go),每条记录包含id(如00wb)、flags(含key_frame与list位)、offset、length,全部收集到idx1Samples。
值得一提的是,idx1的offset是相对movi数据区开始位置的偏移,因此使用idx1计算样本绝对位置时,源码会额外加上moviListPos并跳过 32 位的 chunk size 字段(+32,见 format/riff/avi.go)。
提取指定流的样本:.streams[N].samples[] | tobytes
原文档提供了提取流样本的标准命令。例如提取 1 号流(下标从 0 开始)的全部样本,并将每个样本的原始字节拼接后重定向输出:
$ fq '.streams[1].samples[] | tobytes' file.avi > stream01.mp3命令拆解:
.streams[1]:取第二个流的流对象(streams数组按strl声明顺序排列);.samples[]:按上文所述优先级挑选的样本数组展开,每个元素是一个已被深层解码的样本(如mp3_frame);| tobytes:将解码后的样本还原为原始字节串;> stream01.mp3:重定向保存,得到可直接播放的裸 MP3 流。
若文件是双流 AVI(如视频流 + 音频流),把下标换成对应流的序号即可。从仓库测试 format/riff/testdata/mp3.avi.fqtest 可以看到实际效果:该文件只有一个音频流,其streams[0]下的samples[0:3]被逐一解码为(mp3_frame),每个样本内部展开了完整的 MP3 帧结构(header{}、side_info{}、audio_data、crc_calculated),证明tobytes提取的是帧边界精确的原始数据。
注意:只有
strf声明了 fq 支持的编解码格式(见下文"支持的流格式"一节)时,样本才会被深层解码;否则样本会以FieldRawLen原始位形式呈现。
查看流摘要:grep_by 组合查询
当只需要了解每个流"是什么"而不想看全部细节时,原文档提供了流摘要命令:
$ fq -o decode_samples=false '[.chunks[0] | grep_by(.id=="LIST" and .type=="strl") | grep_by(.id=="strh") as {$type} | grep_by(.id=="strf") as {$format_tag, $compression} | {$type,$format_tag,$compression}]' *.avi这条命令同时适用于多个文件(*.avi),输出每个 AVI 内所有流的(type, format_tag, compression)三元组。逐步解释:
-o decode_samples=false:关闭样本解码,只解析容器与流头,速度更快;.chunks[0]:进入最外层 RIFF(AVI)的 chunk 数组;grep_by(.id=="LIST" and .type=="strl"):筛选出LIST类型的流列表块。grep_by是 fq 提供的按条件查找的便捷函数;grep_by(.id=="strh") as {$type}:在strl内找到流头strh块,解构出type字段(如auds/vids)。strh的解析见 format/riff/avi.go,其中type会映射为友好描述:auds(Audio stream)、mids(MIDI stream)、txts(Text stream)、vids(Video stream),映射表见 format/riff/avi.go;grep_by(.id=="strf") as {$format_tag, $compression}:在strl内找到流格式strf块,解构出音频流的format_tag(WAV 格式标签,如mp3、FLAC)或视频流的compression(BITMAPINFOHEADER 中的四字符压缩标识,如 H.264 相关标识)。strf的解析见 format/riff/avi.go;- 最终
{$type,$format_tag,$compression}组装为摘要对象,其中不存在的字段在输出中为null,可以直观区分音频流(有format_tag)与视频流(有compression)。
以仓库测试中的 format/riff/testdata/mp3.avi.fqtest 为参照,strf解码结果中format_tag: "mp3" (85)会被映射为符号名mp3;视频流则会显示compression字段。对avc.avi(format/riff/testdata/avc.avi)执行此命令,可以确认其视频流compression为 H.264 相关标识。
解码选项详解:样本与扩展块的开关
-o decode_samples=false:关闭样本深层解码
fq 在解析到movi内的媒体 chunk(如00wb、00dc、00db)时,若decode_samples=true且该流声明了受支持的格式,会调用对应格式组(MP3_Frame、FLAC_Frame、AVC_AU、HEVC_AU)做逐样本解码(见 format/riff/avi.go 与decodeSample函数 format/riff/avi.go)。样本解码计算量较大,关闭后movi数据块以data: raw bits形式呈现,仅保留容器层级结构。
-o decode_extended_chunks=false:关闭扩展块解码
OpenDML 允许 AVI 超过 1 GB,通过额外的RIFF(AVIX)块扩展数据。解码器在 format/riff/avi.go 中循环TryPeekBytes(4)检测是否还有RIFF块,并将其解析为extended_chunks数组。关闭该选项可跳过这些扩展块。
组合使用以加速解码
原文档给出的加速命令:
$ fq -o decode_samples=false -o decode_extended_chunks=false d file.avi若你只关心容器结构、索引或流头信息,而不需要样本细节和扩展块,这条命令会显著加快解码速度并减少输出体积。同理,帮助信息中也给出了等价的对象写法avi({decode_extended_chunks:false,decode_samples:false}),适用于管道场景:
$ fq -d avi -o decode_extended_chunks=true -o decode_samples=true . file样本解码的底层细节:如何划分帧边界
decodeSample(format/riff/avi.go)体现了 fq 对 AVI 样本边界的处理策略:
- 若索引区间长度为 0,直接输出剩余位作为单个
sample; - 子样本大小
subSampleSize = stream.sampleSize * 8(来自strh的sample_size字段)。若sample_size为 0,或流没有受支持格式且子样本大小不超过 64 位(8*8的启发式规则,避免把 PCM 拆成过小的块),则退化为整个区间作为一个样本; - 随后用
FramedFn(subSampleSize, ...)按固定帧大小逐帧解码;若decode_samples=true且流有受支持格式,每帧用FieldFormat交给对应格式组(MP3/FLAC/H.264/HEVC)深解,否则按原始位输出。
这套逻辑使得固定帧大小(如strh.sample_size非零)的流可以被精确分帧,而变长帧(如 MP3)则交由 MP3 帧解码器自行同步帧边界。仓库中 format/riff/testdata/mp3.avi.fqtest、format/riff/testdata/flac.avi.fqtest、format/riff/testdata/avc.avi.fqtest、format/riff/testdata/pcm.avi.fqtest 分别覆盖了 MP3、FLAC、H.264 与 PCM 样本的解码验证。
支持的流格式与格式标签
从strf解析逻辑(format/riff/avi.go)可以看出当前支持的映射:
| 流类型 | 判定字段 | 受支持取值 | 深层解码格式 |
|---|---|---|---|
音频(auds) | format_tag | mp3(WAVTagMP3) | MP3_Frame |
音频(auds) | format_tag | FLAC(WAVTagFLAC) | FLAC_Frame |
视频(vids) | compression | H.264 一族标识(H264、h264、X264、x264、avc1、DAVC、SMV2、VSSH、Q264、V264、GAVC、UMSV、tshd、INMC) | AVC_AU |
视频(vids) | compression | HEVC、H265 | HEVC_AU |
音频流的format_tag通过format.WAVTagNames映射为符号名(如 85 →mp3);视频流的compression以四字符码形式展示。这些格式标签常量定义在format包中,例如format.WAVTagMP3、format.BMPTagH264等。未在列表内的编解码(如 MPEG-4 Part 2、DV 等,源码中TODO也标注了 DV handler、palette change 等未实现项)会以原始位输出。
容器与流的完整字段速览
了解解码输出的字段有助于编写更精确的查询:
- RIFF 层:
id("RIFF")、size、type("AVI ",通过StrAssert校验,错误时报wrong or no AVI riff type found,见 format/riff/avi.go); avih主头:micro_sec_per_frame、max_bytes_per_sec、padding_granularity、flags(含must_use_index、has_index、trust_ck_type、is_interleaved、copyrighted、was_capture_file等位字段)、total_frames、initial_frames、streams、suggested_buffer_size、width、height、reserved,见 format/riff/avi.go;dmlh扩展头:total_frames(64 位宽),见 format/riff/avi.go;strh流头:type、handler、flags(disabled、pal_changes)、priority、language、initial_frames、scale、rate、start、length、suggested_buffer_size、quality、sample_size、frame(left/top/right/bottom),见 format/riff/avi.go;strf流格式:视频为 BITMAPINFOHEADER(bi_size、width、height、planes、bit_count、compression、size_image、x/y_pels_per_meter、clr_used、clr_important),音频为 WAVEFORMATEX(format_tag、channels、samples_per_sec、avg_bytes_per_sec、block_align、bits_per_sample、可选cb_size与extra);vprp视频属性、strn/INFO 元数据字符串块(ISFT等,见 format/riff/common.go 的 chunk 描述表);streams[]汇总:每个流输出type、handler、format_tag或compression,以及按优先级选出的indexes/samples;extended_chunks[]:OpenDML AVIX 扩展块数组。
测试与验证
仓库通过 fqtest 机制对 AVI 解码器做了回归验证,相关测试文件位于 format/riff/testdata/:
- mp3.avi.fqtest:MP3 音频流 AVI,验证容器头解析、
movi内00wb块、idx1索引解析,以及streams[0].samples的逐帧 MP3 深解; - avc.avi.fqtest:H.264 视频流 AVI,验证
strf中compression识别与AVC_AU样本解码; - flac.avi.fqtest、pcm.avi.fqtest:分别覆盖 FLAC 与 PCM 音频样本;
- help_avi.fqtest:
fq -h avi帮助输出,与本文介绍的命令一致。
这些测试同时也展示了d file.avi(十六进制 + 解码树视图)与dv file.avi的实际输出格式,可作为排查解码问题的参照。
参考资料
原文档末尾附有 AVI 格式的两份权威背景资料:微软官方 AVI RIFF 文件参考(AVI RIFF File Reference)以及 OpenDML AVI 文件格式扩展规范(OpenDML AVI File Format Extensions),它们是理解indx/ix/idx1三类索引与AVIX扩展块设计初衷的基础读物。结合本文介绍的 fq 命令与 format/riff/avi.go 源码,即可完整掌握用 fq 分析、提取与调优 AVI 文件的实用技能。
【免费下载链接】fqfq - jq for binary formats. Tool, language and decoders for working with binary formats.项目地址: https://gitcode.com/gh_mirrors/fq/fq
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考