Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第九 视频

视频是现代社会最重要的信息媒介之一。从电影电视到短视频直播,从视频会议到监控安防,视频技术深刻改变了人们获取和传播信息的方式。与静态图片不同,视频是随时间连续变化的图像序列,同时伴随音频流。本章将探讨视频的本质、编码原理、主流编码格式,以及如何使用 Rust 进行视频处理。

9.1 视频的本质

视频本质上是一系列静态图像(称为帧)按照一定速率连续播放,利用人眼的视觉暂留效应产生运动的错觉。衡量视频质量的核心参数有三个:帧率、分辨率和码率。

9.1.1 帧率

**帧率(Frame Rate)**表示每秒显示的图像帧数,单位是 FPS(Frames Per Second)。

  • 24 FPS:电影标准,画面具有电影感。
  • 25 FPS / 30 FPS:传统电视广播标准(PAL / NTSC)。
  • 60 FPS:游戏和高清视频常用,运动画面更流畅。
  • 120 FPS / 240 FPS:高帧率视频,用于慢动作回放和电竞。

帧率越高,运动画面越流畅,但数据量也越大。对于普通观影,24~30 FPS 已足够;对于快速运动场景(如体育赛事、游戏),60 FPS 能显著提升观感。

9.1.2 分辨率

分辨率表示视频画面的像素尺寸,决定了画面的清晰度。

分辨率像素尺寸说明
SD(标清)720 × 480 / 720 × 576传统 DVD 标准
HD(高清)1280 × 720720p
Full HD1920 × 10801080p,目前主流
2K2560 × 14401440p
4K UHD3840 × 2160超高清,四倍于 1080p
8K UHD7680 × 4320超高分辨率,未来趋势

分辨率越高,画面细节越丰富,但像素数量呈指数增长。4K 的像素数是 1080p 的 4 倍,数据量也相应大幅增加。

9.1.3 码率

**码率(Bitrate)**表示视频每秒传输的数据量,单位通常是 kbps 或 Mbps。码率直接决定视频的画质和文件大小。

  • 恒定码率(CBR):码率保持不变,适合网络流媒体。
  • 可变码率(VBR):根据画面复杂度动态调整码率,相同平均码率下画质更好。

以 1080p 视频为例:

码率画质一小时文件大小
5 Mbps一般约 2.2 GB
10 Mbps良好约 4.4 GB
25 Mbps优秀约 11 GB
50 Mbps接近无损约 22 GB

未经压缩的 1080p@60FPS 视频数据量极为惊人:

1920 × 1080 × 3(RGB)× 60 FPS = 373,248,000 字节/秒 ≈ 356 MB/s
一小时约 1.25 TB

这正是视频压缩编码技术存在的根本原因。

9.2 视频编码原理

视频编码的核心目标是在尽可能保持画质的前提下,大幅减少数据量。主要利用两种冗余:空间冗余和时间冗余。

9.2.1 帧内压缩

**帧内压缩(Intra-frame Compression)**利用单帧图像内部的空间冗余。相邻像素通常具有相似的颜色值,通过以下技术实现压缩:

  • 色彩空间转换:将 RGB 转换为 YUV(亮度+色度),利用人眼对色度不敏感的特性降低色度采样精度(如 4:2:0)。
  • 离散余弦变换(DCT):将图像块从空间域转换到频率域,能量集中在低频分量。
  • 量化:对高频分量进行较大量化,丢弃人眼不敏感的细节。
  • 熵编码:使用哈夫曼编码或算术编码进一步压缩数据。

只使用帧内压缩的帧称为 I 帧(Intra-coded Frame),可以独立解码,是视频随机访问的锚点。

9.2.2 帧间压缩

**帧间压缩(Inter-frame Compression)**利用相邻帧之间的时间冗余。视频相邻帧通常非常相似,只需记录变化部分即可。

  • P 帧(Predicted Frame):参考前面的 I 帧或 P 帧,只编码差异部分和运动矢量。
  • B 帧(Bidirectional Predicted Frame):同时参考前后帧,压缩率最高,但解码需要更多计算。

典型的视频帧序列如:I P B B P B B I P B B ...

帧间压缩是视频编码效率的关键。一个 I 帧可能占用数兆字节,而 P 帧和 B 帧通常只有几十到几百 KB。

9.2.3 运动估计与补偿

帧间压缩的核心技术是运动估计。编码器将画面划分为宏块(如 16×16 像素),在参考帧中搜索最佳匹配位置,记录运动矢量(位移偏移量)。解码时根据运动矢量从参考帧“搬运“像素块,再叠加残差数据还原当前帧。

运动估计的计算量巨大,是视频编码中最耗时的环节,也是硬件加速的重点。

9.3 视频编码格式

9.3.1 H.264 / AVC

H.264(又称 AVC,Advanced Video Coding)是目前最广泛使用的视频编码标准,由 ITU-T 和 ISO/IEC 联合制定。

  • 发布时间:2003 年。
  • 压缩效率:相比前代 MPEG-2,同等画质下码率降低约 50%。
  • 特点:兼容性好、专利授权成熟、硬件解码支持最广泛。
  • 应用场景:网络视频(YouTube、Bilibili)、蓝光光盘、视频监控、视频会议。
  • Profile 等级
    • Baseline:低复杂度,适合视频通话和移动设备。
    • Main:平衡画质和复杂度,用于标清广播。
    • High:最高画质,用于高清和蓝光。

9.3.2 H.265 / HEVC

H.265(又称 HEVC,High Efficiency Video Coding)是 H.264 的继任者。

  • 发布时间:2013 年。
  • 压缩效率:同等画质下码率比 H.264 降低约 50%,即 1080p 的码率可看 4K 画质。
  • 特点:支持更高分辨率(8K)、更高效的帧间预测、更大的编码单元(CTU 最大 64×64)。
  • 缺点:专利授权复杂且昂贵,导致普及速度不及预期。
  • 应用场景:4K 蓝光、Apple 设备、部分流媒体平台。

9.3.3 AV1

AV1(AOMedia Video 1)是由开放媒体联盟(AOMedia)主导开发的开源免版税视频编码格式。

  • 发布时间:2018 年。
  • 压缩效率:同等画质下码率比 H.265 低约 20%~30%,比 H.264 低约 50%。
  • 特点:完全开源免版税、由 Google、Mozilla、Netflix 等巨头推动。
  • 缺点:编码速度较慢(正在快速改善),硬件解码支持仍在普及中。
  • 应用场景:YouTube、Netflix、WebRTC、浏览器视频。
编码格式发布时间压缩效率专利授权硬件支持主要应用
H.2642003基准收费最广泛全网通用
H.2652013比 H.264 提升 50%昂贵较广泛4K 内容
AV12018比 H.265 提升 20%~30%免版税growing流媒体/Web

9.4 Rust 视频处理

Rust 在视频处理领域虽然不如 Python 或 C++ 生态成熟,但已有可用的库和绑定,特别是通过 FFI 调用 FFmpeg 这一业界标准工具。

9.4.1 FFmpeg 简介

FFmpeg 是开源多媒体处理的事实标准,支持几乎所有音视频格式的编解码、转码、流媒体处理。它由 C 语言编写,提供了命令行工具和 C 库(libavcodec、libavformat、libavutil 等)。

Rust 社区提供了 FFmpeg 的绑定库 ffmpeg-next,使得在 Rust 中调用 FFmpeg 功能成为可能。

Cargo.toml 中添加依赖:

[dependencies]
ffmpeg-next = "6.1"

9.4.2 使用 ffmpeg-next 读取视频信息

use ffmpeg_next as ffmpeg;

fn main() {
    // 初始化 FFmpeg
    ffmpeg::init().unwrap();

    // 打开视频文件
    let mut ictx = ffmpeg::format::input(&"video.mp4").unwrap();

    // 遍历所有流,找到视频流
    for (stream_index, stream) in ictx.streams().enumerate() {
        let codec = stream.codec();
        match codec.medium() {
            ffmpeg::media::Type::Video => {
                let video = codec.decoder().video().unwrap();
                println!("视频流 #{}:", stream_index);
                println!("  编码格式: {:?}", video.id());
                println!("  分辨率: {}x{}", video.width(), video.height());
                println!("  帧率: {}/{}",
                    stream.rate().0, stream.rate().1);
                println!("  总帧数: {}", stream.frames());
            }
            ffmpeg::media::Type::Audio => {
                let audio = codec.decoder().audio().unwrap();
                println!("音频流 #{}:", stream_index);
                println!("  采样率: {}", audio.rate());
                println!("  声道数: {}", audio.channels());
            }
            _ => {}
        }
    }
}

9.4.3 使用 ffmpeg-next 解码视频帧

use ffmpeg_next as ffmpeg;
use ffmpeg::software::scaling::{context::Context, flag::Flags};
use ffmpeg::util::frame::video::Video;

fn main() {
    ffmpeg::init().unwrap();

    let mut ictx = ffmpeg::format::input(&"video.mp4").unwrap();

    // 找到第一个视频流
    let input_stream = ictx.streams()
        .best(ffmpeg::media::Type::Video)
        .expect("未找到视频流");
    let stream_index = input_stream.index();

    // 创建解码器
    let context_decoder = ffmpeg::codec::context::Context::from_parameters(input_stream.parameters()).unwrap();
    let mut decoder = context_decoder.decoder().video().unwrap();

    // 创建缩放器,将帧转换为 RGB24 格式
    let mut scaler = Context::get(
        decoder.format(),
        decoder.width(),
        decoder.height(),
        ffmpeg::format::Pixel::RGB24,
        decoder.width(),
        decoder.height(),
        Flags::BILINEAR,
    ).unwrap();

    // 读取数据包并解码
    for (stream, packet) in ictx.packets() {
        if stream.index() == stream_index {
            decoder.send_packet(&packet).unwrap();

            let mut decoded = Video::empty();
            while decoder.receive_frame(&mut decoded).is_ok() {
                let mut rgb_frame = Video::empty();
                scaler.run(&decoded, &mut rgb_frame).unwrap();

                println!("解码一帧: {}x{}", rgb_frame.width(), rgb_frame.height());

                // 此处可以对 rgb_frame.data(0) 中的 RGB 像素数据进行处理
                // 例如保存为图片、进行图像识别等
            }
        }
    }

    // 刷新解码器
    decoder.send_eof().unwrap();
}

9.4.4 使用 ffmpeg-next 进行视频转码

use ffmpeg_next as ffmpeg;

fn main() {
    ffmpeg::init().unwrap();

    // 打开输入文件
    let mut ictx = ffmpeg::format::input(&"input.mp4").unwrap();

    // 创建输出上下文
    let mut octx = ffmpeg::format::output(&"output.mkv").unwrap();

    // 遍历输入流,在输出中添加对应流
    for (stream_index, istream) in ictx.streams().enumerate() {
        let codec_id = istream.codec().id();
        let mut ostream = octx.add_stream(codec_id).unwrap();
        ostream.set_parameters(istream.parameters()).unwrap();
    }

    octx.write_header().unwrap();

    // 复制数据包
    for (stream, mut packet) in ictx.packets() {
        let istream_index = stream.index();
        let ostream = octx.stream(istream_index).unwrap();

        packet.set_stream(ostream.index());
        packet.rescale_ts(stream.time_base(), ostream.time_base());
        packet.write_interleaved(&mut octx).unwrap();
    }

    octx.write_trailer().unwrap();
    println!("转码完成: input.mp4 -> output.mkv");
}

9.4.5 rav1e:Rust 原生的 AV1 编码器

rav1e 是由 Xiph.Org 基金会开发的 AV1 编码器,使用 Rust 和 Assembly 编写,是 Rust 视频编码领域的重要项目。

// rav1e 基本使用示例(API 可能随版本变化)
use rav1e::config::Config;
use rav1e::encoder::EncoderStatus;

fn main() {
    // 创建编码配置
    let mut cfg = Config::default();
    cfg.enc.width = 1920;
    cfg.enc.height = 1080;
    cfg.enc.bit_depth = 8;
    cfg.enc.time_base = rav1e::data::Rational { num: 1, den: 30 }; // 30 FPS

    // 创建编码器
    let mut ctx = cfg.new_context().unwrap();

    // 发送帧进行编码
    // ctx.send_frame(frame).unwrap();

    // 刷新编码器
    ctx.flush();

    // 接收编码后的数据包
    loop {
        match ctx.receive_packet() {
            Ok(packet) => {
                println!("收到编码包,大小: {} 字节", packet.data.len());
                // 将 packet.data 写入文件或传输
            }
            Err(EncoderStatus::LimitReached) => break,
            Err(EncoderStatus::Encoded) => continue,
            Err(e) => {
                println!("编码错误: {:?}", e);
                break;
            }
        }
    }
}

rav1e 展示了 Rust 在性能敏感型多媒体处理领域的潜力,其内存安全特性对于处理大规模视频数据尤为重要。

9.4.6 相关 Rust 库

库名说明
ffmpeg-nextFFmpeg 的 Rust 绑定,功能全面
rav1eRust 编写的 AV1 编码器
image图像处理,可用于视频帧处理
wgpu跨平台 GPU 计算,可用于视频渲染

9.5 本章小结

概念说明
帧率每秒显示帧数,电影 24 FPS,高清 60 FPS
分辨率画面像素尺寸,1080p、4K、8K 等
码率每秒数据量,决定画质和文件大小
I 帧帧内压缩,可独立解码的完整帧
P 帧前向预测帧,参考前面帧编码
B 帧双向预测帧,参考前后帧,压缩率最高
H.264最广泛使用的视频编码标准
H.265H.264 继任者,4K 时代主流
AV1开源免版税编码格式,未来趋势
FFmpeg业界标准多媒体处理工具
ffmpeg-nextFFmpeg 的 Rust 绑定库
rav1eRust 原生 AV1 编码器

练习建议

  1. 使用 ffmpeg-next 编写程序,读取一个视频文件的完整元数据(分辨率、帧率、码率、编码格式、时长等)。
  2. 使用 ffmpeg-next 提取视频中的每一帧,保存为 PNG 图片序列。
  3. 对比同一视频分别用 H.264、H.265、AV1 编码后的文件大小和画质差异。
  4. 编写程序计算视频的平均码率和峰值码率。
  5. 了解 Rust 的 vpx-encodex264 绑定,尝试进行视频编码。