Skip to main content

Chapter 115: Decode, Encode, Render, Mux, Demux

本章讨论一个视频文件在移动系统中被播放、预览、剪辑、录制和导出时,数据如何在 demuxdecoderenderencodemux 之间流动。读完本章后,读者应能拿到一个 App 可见行为,例如“播放一个 MP4”“给视频加水印后导出”“把 MOV 转成 MP4”,定位它经过了哪些媒体阶段、每个阶段持有什么数据、哪些阶段会改变画质、哪些阶段只改变封装和索引。

贯穿材料是一段 10 秒短视频编辑流程:App 打开一个 MP4,读取一条 H.264 视频轨和一条 AAC 音频轨,预览画面,给前 2 秒叠加标题水印,最后导出一个新的 MP4。这个流程同时包含播放、渲染、重新编码和重新封装,足够覆盖本章的全部阶段。简单播放可以看成这条路径的前半段;录制和导出可以看成这条路径的后半段。

移动 OS 把媒体处理拆成多个边界,是为了同时控制格式兼容、硬件编解码器、图形缓冲区、电源和文件完整性。Android 公开 API 中的 MediaExtractorMediaCodecMediaMuxer,以及 Apple 公开文档中的 AVFoundationCore MediaVideoToolbox,都把媒体文件、样本、时间、编解码和输出文件拆成分层对象。具体类名属于平台 API,本章关心的是这些类名背后的系统路径。

本章结论先给出:demux 负责从容器中取出带时间戳的编码样本,decode 负责把编码样本还原成原始音视频帧,render 负责把视频帧放入可显示的 Surface 或 Layer,encode 负责把原始帧压回编码码流,mux 负责把编码样本和元数据写回容器。判断一个媒体任务成本高低,先看它是否生成 raw frame;一旦生成 raw frame,通常会进入解码、渲染或滤镜、重新编码的高成本路径。

下面的图只表达本章贯穿材料的数据边界。它省略平台私有进程名,把注意力放在每个阶段的输入、输出和责任主体上。

图中的关键边界是编码样本和 raw frame 的分界。编码样本适合存储、传输和快速重封装;raw frame 适合显示、像素处理、滤镜、水印、颜色转换和重新编码。移动系统会尽量让视频帧在 Surface、Layer、Pixel Buffer、Graphic Buffer 或类似对象中流动,因为这些对象可以交给硬件编解码器、GPU 和合成器共享,减少 CPU 侧内存拷贝。

115.1 Demux:Container 到编码流的拆分

Demux 的工作对象是媒体容器。容器是把视频轨、音频轨、字幕、封面、旋转信息、色彩信息、时间索引和文件级元数据放在一起的文件格式,例如 MP4、MOV、WebM。Demux 阶段的输出通常仍然是编码数据,视频仍然是 H.264、HEVC、AV1 之类的压缩样本,音频仍然是 AAC、Opus 之类的压缩样本。

在贯穿材料中,App 打开 MP4 后,extractor 首先读取容器的 track 表。每条 track 会给出格式描述,例如 MIME 类型、宽高、采样率、通道数、codec-specific data、色彩信息和时间基准。然后 extractor 按样本读取数据。一个 sample 是带结构的媒体单位,它携带 track index、sample size、presentation timestamp、flags 和 sync sample 标记。播放器用这些信息把样本送入正确的解码器,剪辑器用这些信息决定从哪个关键样本开始读取。

Demux 阶段回答三个问题。第一,文件里有哪些轨道。第二,每个样本在时间轴上何时呈现。第三,样本是否可以作为随机访问入口。视频 seek 到第 5 秒时,系统通常需要找到靠近目标时间的 sync sample,也就是可独立解码的关键样本,再从那里向后解码到目标帧。这个约束来自编码结构,demux 负责暴露索引和标记,任意压缩样本仍然要服从编码预测关系。

Demux 的失败表现也很清晰。容器损坏时,extractor 可能无法读取 track 表或样本索引。格式描述缺失时,解码器无法获得 profile、level、SPS、PPS、AudioSpecificConfig 等初始化材料。时间戳异常时,后续同步和 mux 会出现音画错位、倒退时间戳或输出文件不可寻址。加密样本还需要把加密信息交给解密或 DRM 路径,普通 sample data 本身不足以完成播放。

对移动 OS 来说,demux 处在文件访问和编解码之间。App 通过 framework API 指定 data source,系统媒体层解析容器并返回样本,后续 decoder、renderer 或 muxer 根据这些样本继续工作。这个阶段通常计算成本较低,主要消耗 I/O、索引解析和少量内存队列;真正的 CPU、GPU、NPU 或硬件 codec 压力通常出现在后面的 decode、render 和 encode。

115.2 Decode:Encoded Stream 到 Raw Frame

Decode 的工作对象是编码流。它把 H.264、HEVC、AV1、AAC 等压缩样本还原成原始视频帧或原始音频帧。视频 raw frame 通常以 YUV 类 pixel format 表示,例如 NV12、I420、YUV420 flexible,音频 raw frame 通常以 PCM 表示。解码后的数据体积远大于编码样本,因此它直接决定内存带宽、buffer 数量和功耗压力。

贯穿材料中的视频样本从 demux 阶段进入 decoder input buffer。系统需要先配置 decoder:输入格式来自 track format,初始化数据来自 codec-specific data,输出格式由 decoder 和平台能力共同决定。开始运行后,App 或系统媒体管线把编码样本排入 input buffer,decoder 在硬件 codec 或软件 codec 中处理,然后把 decoded frame 放入 output buffer、Surface、Image、Pixel Buffer 或类似对象。

Decoder 的核心边界是 buffer ownership。一个 input buffer 交给 codec 后,调用方要等 codec 归还或发出新 buffer 可用信号;一个 output buffer 被调用方持有时,codec 必须等待调用方释放后再复用。移动系统用这种所有权协议控制并发访问,减少跨进程和跨硬件单元的数据竞争。Android MediaCodec 文档把 codec 描述为输入 buffer 到输出 buffer 的异步处理组件,这个抽象正好对应这里的 ownership 模型。

硬件 decoder 的优势来自专用电路和专用内存路径。对于 4K HEVC、HDR 或高帧率视频,CPU 软件解码会迅速推高功耗和温度,硬件 decoder 可以在更低能耗下输出可供显示或进一步处理的帧。代价是 codec capability 受到设备限制:profile、level、resolution、bit depth、HDR、color format、low-latency mode 和 secure content 都可能决定这条路径是否可用。

Decode 还承担错误恢复。视频编码通常依赖参考帧,某个压缩样本损坏后,后续若干帧也可能被污染。系统常见处理是丢弃损坏样本、等待下一个关键帧、重置 decoder 或向上层报告 codec error。播放器倾向于尽快恢复可见画面;离线导出倾向于暴露错误并停止输出,把文件写入限制在时间戳和画面内容一致的范围内。

Decode 阶段的可见结果取决于下一步。播放路径会把 decoded frame 交给 render;转码路径会把 decoded frame 交给 filter、GPU、encoder;分析路径可能把 raw frame 交给机器视觉或图像处理。只要任务进入 raw frame,系统就要开始关注像素格式、stride、crop、rotation、color space 和 buffer 生命周期。

115.3 Render:Video Frame 到 Surface / Layer

Render 的工作对象是可显示的视频帧。它把 decoded frame 放到屏幕输出路径中,通常通过 Surface、Layer、Texture、Drawable、Pixel Buffer 或类似对象进入 GPU、合成器和显示控制器。Render 阶段会决定帧何时可见、是否按正确方向显示、是否经过颜色转换、是否与音频时钟同步。

在贯穿材料中,预览页面需要把 decoded frame 渲染到视频编辑界面,并在前 2 秒叠加标题水印。这里的 render 同时承担两个动作:一个是显示预览,一个是生成带水印的中间帧。预览路径可以把 decoder output surface 直接交给合成器;带水印导出路径通常需要 GPU 或图形管线把原始视频帧、文字图层、缩放矩阵和色彩转换合成成新的 raw frame,再交给 encoder。

Render 的关键点是显示时间。Decoder 输出一帧只表示这帧可供消费,屏幕可见还要等待 surface queue、fence、compositor、VSync 和 display timing。若视频帧到达太早,系统会排队等待;若帧到达太晚,播放器可能丢帧或显示上一帧;若音频时钟领先,视频调度器会根据时钟差决定跳过、延后或补偿。

Surface 和 Layer 还承担移动系统的零拷贝边界。视频帧如果从硬件 decoder 输出到 CPU ByteBuffer,再上传到 GPU texture,内存带宽和功耗都会增加。更稳定的路径是让 decoder 输出到可被图形系统消费的 buffer,再由 GPU 或显示合成器处理。Android 中的 Surface、Apple 体系中的 Core Video Pixel Buffer、IOSurface、Core Animation Layer 和 Metal texture 都服务于这个方向;具体对象不同,系统目标相同:让媒体帧在硬件单元之间以受控所有权流动。

Render 的失败表现通常直接可见。rotation metadata 没有应用,视频会横竖方向错误。color space 或 HDR metadata 丢失,画面会发灰、过曝或色彩漂移。Surface 队列阻塞,播放会卡顿。预览和导出使用不同渲染路径,水印位置、缩放和颜色可能与用户看到的预览不一致。移动端视频编辑器要把预览 render 和导出 render 绑定到同一组时间、坐标和颜色规则。

115.4 Encode:Raw Frame 到 Encoded Bitstream

Encode 的工作对象是 raw frame。它把原始视频帧压缩成 H.264、HEVC、AV1 等编码样本,把原始音频帧压缩成 AAC、Opus 等编码样本。Encode 是媒体管线中最容易影响画质、文件大小、延迟、功耗和温度的阶段,因为它要在有限码率下决定哪些细节保留、哪些运动信息预测、哪些帧作为关键帧。

贯穿材料中的水印改变了视频像素,因此视频轨需要重新编码。Encoder input 可以来自 CPU 填充的 raw frame,也可以来自 encoder input surface。后一种路径更适合移动端视频编辑:GPU 先把原视频帧和水印图层合成到一个 surface,hardware encoder 再从这个 surface 读取帧并生成压缩样本。这样可以减少 CPU 侧读写 raw frame 的次数。

Encode 配置至少包含 codec type、width、height、frame rate、bitrate、profile、level、keyframe interval、rate control mode 和 color format。GOP 决定关键帧和预测帧的组织方式,keyframe interval 决定随机访问、seek 精度和错误恢复成本,bitrate 和 rate control 决定质量与文件大小的取舍。低码率会压缩更多细节,高码率会增加文件大小和网络传输成本;实时录制还要把 encoder latency 放入交互预算。

硬件 encoder 同样受到设备 capability 约束。某些设备支持 4K HEVC 解码,但编码分辨率、帧率、profile 或 HDR 输出范围更窄。某些 encoder 输入只接受 surface,某些支持 flexible YUV buffer。某些平台会在后台、低电量、温度升高或录屏保护场景中限制编码行为。App 层看到的是 configure 失败、dequeue 超时、输出格式变化、码率偏离预期或导出速度下降;系统层原因可能来自 codec 能力、资源竞争、电源策略和 thermal policy。

Encode 的输出仍然需要时间戳和 flags。每个 encoded sample 要带 presentation time,关键帧要有标记,结束时要发送 end-of-stream。Muxer 依赖这些信息建立输出文件索引。若 encoder 输出时间戳不单调,mux 可能拒绝写入或生成播放异常的文件。若第一帧缺少可用关键帧,后续播放器可能需要等待很久才显示画面。

115.5 Mux:Encoded Sample 写入输出容器

Mux 的工作对象是编码样本和输出容器。它把视频 encoded sample、音频 encoded sample、字幕样本、metadata、track format、时间索引和文件级结构写成一个可播放、可 seek、可被系统识别的文件。Mux 阶段通常不改变视频像素和音频波形,它负责让这些样本在容器中变成一致的时间结构。

在贯穿材料中,导出文件需要写入一条重新编码后的视频轨,以及一条可以直接复用的 AAC 音频轨。Muxer 会先添加 track,记录每条轨的 format description,然后在运行期交替写入各轨 sample data 和 sample metadata。每个 sample 的 presentation timestamp、size、flags 和 track index 都会影响输出文件的索引。音频和视频是否同步,最终由 mux 阶段写入的时间轴共同决定。

Mux 的一个关键动作是 finalize。许多容器需要在文件结束时写入或更新全局索引、duration、track table 和 metadata。导出过程中如果进程崩溃、存储空间耗尽或用户取消,文件可能已经包含部分 sample data,但缺少完整索引。用户看到的结果可能是文件无法打开、时长显示为 0、只能顺序播放、无法 seek 或末尾音画丢失。工程上常见做法是先写临时文件,mux finalize 成功后再原子替换目标文件。

Mux 也会暴露格式兼容边界。容器支持哪些 codec、哪些 time scale、哪些 metadata、是否支持 B-frame、是否支持旋转矩阵、是否支持 HDR metadata,都取决于容器规范和平台实现。把 HEVC、AV1、Dolby Vision 或多声道音频写入某个容器时,样本本身有效还不够,容器也要能表达对应的格式描述和兼容标记。

Mux 的输入通常来自两类路径。一类是 encoder 输出的全新样本,适合水印、滤镜、变速、转码、压缩导出。另一类是 demux 后原样读取的样本,适合剪切、换容器、修复 metadata 或合并轨道。前者成本高但可以改变内容,后者成本低但受关键帧、时间戳和容器兼容限制。

115.6 Transcode、Remux、Rewrap 的系统差异

TranscodeRemuxRewrap 都可能让用户看到“导出一个新文件”,但它们经过的媒体阶段差异很大。判断差异的核心问题是:新文件是否需要重新生成 raw frame,是否改变 codec bitstream,是否只改变容器表达。

任务典型路径是否生成 raw frame质量影响成本适用条件
Transcodedemux → decode → render 或 filter → encode → mux可能产生重新压缩损失改 codec、改分辨率、加水印、调色、压缩码率、改变帧率
Remuxdemux → mux通常保持原始编码质量codec 和目标容器兼容,时间戳和样本边界可直接复用
Rewrapdemux → 容器结构重写 → mux否或极少量样本转换通常保持原始编码质量低到中主要更换文件外壳、修复索引、调整 metadata、保持编码流主体

Transcode 是内容级处理。贯穿材料里加水印会改变视频像素,视频轨就进入 transcode 路径:先解码成 raw frame,再 render 水印,再重新编码。画质变化来自第二次压缩,耗时来自 decode、GPU 合成和 encode,功耗来自硬件 codec、GPU、内存带宽和存储写入。若同时改变分辨率、HDR 到 SDR、帧率或降噪算法,transcode 还会引入颜色管理和采样策略问题。

Remux 是封装级处理。一个 H.264/AAC 的 MP4 片段如果只需要换成另一个兼容容器,系统可以读取原有 encoded samples,再按新容器写出。它保留原编码数据,因此速度通常接近 I/O 和索引处理速度。边界来自容器兼容性和样本组织:目标容器要能表达这些 codec、时间戳、关键帧、metadata 和必要的 codec-specific data。

Rewrap 在工程语境中强调“换外壳”。它常见于把同一组 encoded samples 放入另一个容器,或者重建一个损坏、不利于流式播放、metadata 不完整的文件结构。它和 remux 的边界会因工具和平台用词而变化;本书把 rewrap 视作 remux 的一种侧重:编码流主体保持稳定,主要改容器组织、索引位置和 metadata 表达。

用贯穿材料判断,视频轨因为水印进入 transcode,音频轨如果没有裁剪到非 sample 边界、没有变速、没有混音、没有响度处理,可以走 remux。最终 muxer 把“重新编码的视频样本”和“复用的音频样本”写入同一个输出 MP4。一个导出任务可以同时包含 transcode 和 remux,判断对象应按 track 分开。

115.7 Decode / Encode / Mux / Demux 在媒体 Pipeline 中的位置

媒体 Pipeline 的稳定阅读方法是先确定用户意图,再确定数据边界。用户说“播放”,系统通常要 demux、decode、render。用户说“录制”,系统通常要从 camera 或 microphone 获得 raw frame,再 encode、mux。用户说“剪辑导出”,系统要按每条 track 判断是否需要 decode 和 encode。用户说“换格式”,要先确认换的是 codec 还是 container。

用户可见行为主要阶段关键判断常见失败表现
播放本地视频demux → decode → rendercodec 是否可解,时间戳是否正常,Surface 是否按时显示黑屏、花屏、音画不同步、seek 卡顿
录制视频raw capture → encode → muxencoder capability、码率、关键帧、存储写入速度掉帧、文件损坏、码率异常、录制中断
加水印导出demux → decode → render → encode → mux水印改变像素,视频轨需要 raw frame 和重新编码导出慢、画质下降、水印位置不一致
只换容器demux → muxcodec 与目标容器兼容,样本时间戳可复用输出文件无法打开、metadata 丢失、seek 异常
压缩体积demux → decode → encode → mux目标码率和 profile 是否满足质量目标细节损失、块效应、导出耗电和发热
裁剪片段demux → mux 或 demux → decode → encode → mux裁剪点是否落在关键帧或可接受重新编码首帧等待、起点不准、音画偏移

这个表把本章的方法压缩成一个判断顺序。第一步看输出是否需要改变像素或波形;需要改变就会进入 raw frame 路径。第二步看 codec 是否变化;codec 变化通常需要 decode 和 encode。第三步看 container 是否变化;container 变化至少需要 mux。第四步看时间戳、关键帧和 metadata 是否可复用;这些信息决定剪辑、seek、同步和输出文件完整性。

从系统责任链看,App 发起媒体请求,framework API 暴露 extractor、codec、surface、writer 或 muxer 对象,系统媒体服务和硬件 codec 负责资源分配,图形系统负责可见帧合成,文件系统负责输出持久化。权限和策略也会进入路径:读取外部媒体可能受照片或媒体权限约束,录屏和受保护内容可能限制输出,后台导出可能受电源和温控策略影响。

本章建立的是跨平台共用模型。后续 Android 和 Apple 章节会把这个模型落到具体 API、进程和 buffer 类型上。当前章的判断不依赖某个类名:只要看到 encoded sample、raw frame、surface/layer、codec configuration、track format、presentation timestamp、finalize,就能定位媒体阶段和责任边界。

最小自检任务

一个视频编辑 App 接收 30 秒 MP4,内部有 H.264 视频轨和 AAC 音频轨。用户裁剪出 8 秒片段,并在前 2 秒加标题水印,音频保持原声,输出仍然是 MP4。请写出视频轨和音频轨分别经过哪些阶段,指出哪些阶段会改变内容,哪些阶段主要改变容器结构,并列出三个可能导致导出失败或结果异常的边界。

答案要点

视频轨需要先 demux 出 H.264 encoded samples,再 decode 成 raw video frame。前 2 秒水印会改变视频像素,因此这段视频必须经过 render 或图形合成,再 encode 成新的 H.264 encoded samples,最后 mux 到输出 MP4。若裁剪点和关键帧位置不匹配,系统可能从更早的 sync sample 开始解码,再按目标时间截取输出帧。

音频轨如果保持 AAC 编码、没有混音、没有变速、没有响度处理,并且裁剪边界可以用现有音频样本表达,可以走 demux → mux 的低成本路径。音频内容没有重新生成 raw PCM,主要变化是 sample 时间戳、起始时间和输出容器索引。若裁剪点要求精确到音频采样级,系统可能需要解码、裁剪 PCM、重新编码。

导出失败或异常的边界至少包括三类。第一,codec capability 不满足目标分辨率、profile、帧率或 color format,导致 encoder 配置失败或输出质量偏离预期。第二,timestamp、duration 或 track 起始时间处理错误,导致音画不同步、首帧等待或 muxer 拒绝写入。第三,mux finalize 没有完成,导致输出 MP4 缺少完整索引、时长错误或文件无法打开。水印预览和导出使用不同坐标、旋转或色彩规则时,还会出现用户看到的预览和最终文件不一致。

本章知识点总结

  • Demux 边界:Demux 从容器中取出带 track、timestamp、format 和 flags 的编码样本。
  • Track 信息:Track format 提供解码器初始化、容器兼容和 mux 写入所需的格式描述。
  • Sync Sample:关键样本决定随机访问、裁剪起点、错误恢复和 seek 成本。
  • Decode 输出:Decode 把编码样本还原成 raw frame,并把 buffer ownership 交给后续消费者。
  • 硬件 Codec:硬件编解码器降低功耗和 CPU 压力,同时受到 profile、level、分辨率和 color format 约束。
  • Render 边界:Render 把 decoded frame 放入 Surface、Layer 或 texture 路径,并受显示时钟和合成器调度影响。
  • 零拷贝路径:移动系统倾向于让视频帧在硬件共享 buffer 中流动,以减少 CPU 内存拷贝和带宽消耗。
  • Encode 取舍:Encode 用码率、GOP、keyframe、profile 和 rate control 在画质、体积、延迟和功耗之间取舍。
  • Mux 责任:Mux 把 encoded samples、track、timestamp 和 metadata 写成完整容器,并在 finalize 阶段完成索引。
  • 文件完整性:导出成功取决于 sample 写入和容器 finalize 同时完成。
  • Transcode 成本:Transcode 经过 raw frame 路径,会改变内容并带来重新压缩、功耗和耗时成本。
  • Remux 成本:Remux 复用编码样本,主要改变容器和索引,成本通常接近 I/O 和元数据处理。
  • Rewrap 含义:Rewrap 强调更换文件外壳或重建容器结构,编码流主体通常保持稳定。
  • 判断顺序:先看是否改变像素或波形,再看 codec 是否变化,再看 container 是否变化,最后检查时间戳、关键帧和 finalize。