#codec #av #encoding

pfv-rs

用于操作PFV(一种类似于MPEG的最小视频编解码器)的库

11个版本

0.2.2 2023年6月4日
0.2.1 2023年6月4日
0.1.7 2023年6月1日
0.1.6 2023年5月31日

#195 in 视频

每月40次下载
pfv-cli中使用

MIT许可

105KB
2K SLoC

快速视频

作为漂亮的视频的后续版本,设计为最小的视频编解码器

目标是提高

  • 质量
  • API设计
  • 编解码器结构
  • (希望)性能

当前编解码器版本为2.1.0

为什么?

PFV的目标是成为视频领域的QOA(即,一个极简编解码器,旨在优先考虑简单性和解码速度,而不仅仅是压缩比,并且完全免费使用,适用于游戏等离线应用程序的回放)。

性能的主要目标是Theora。虽然这个Rust库未能击败Theora的解码性能,但我已经编写了一个配套的纯C库,它确实击败了Theora的解码性能(特别是在扩展到使用系统中的所有可用核心时,因为它是一个极端的并行算法):libpfvdec

未来探索的目标将是通过计算着色器进行GPU解码 - 因为每个宏块已经可以完全并行处理,因此简单地将DCT系数上传到GPU并在GPU上完全进行解码应该很简单,直接解码到游戏就绪的纹理中。

用法

编码视频

创建pfv_rs::enc::Encoder,输入帧,然后写入结果

use pfv_rs::enc::Encoder;

let out_video = File::create("my_video.pfv").unwrap();
let mut enc = Encoder::new(out_video, width, height, framerate, quality, num_threads).unwrap();

// feed in frames as VideoFrames (1 keyframe every 15 frames)
for (idx, frame) in &my_frames.iter().enumerate() {
  if idx % 15 == 0 {
    enc.encode_iframe(frame).unwrap();
  } else {
    enc.encode_pframe(frame).unwrap();
  }
}

// finish PFV stream (will also be automatically called if encoder is dropped)
enc.finish().unwrap();

解码视频

创建pfv_rs::dec::Decoder,并在每一帧上调用advance_delta,传递自上一帧以来的经过时间,并使用闭包处理帧

use pgv_rs::dec::Decoder;

let mut dec = Decoder::new(my_file, num_threads).unwrap();

while dec.advance_delta(delta_time, &mut |frame| {
    // do something with returned &VideoFrame
}).unwrap() {}

或者,您也可以调用advance_frame来直接跳到下一帧,而不传递delta参数。签名相同。

这两个函数还会在文件中还有更多数据要读取时返回Ok(true),或者在解码器达到文件末尾时返回Ok(false)。

算法概述

视频帧编码就视频编解码器而言相当标准。帧被分割成16x16宏块,这些宏块进一步分为8x8子块。每个子块都通过DCT变换和量化来减少存储所需的位数。系数进一步使用熵编码进行压缩。

PFV还采用4:2:0色度子采样 - 因此U和V色度平面在每个轴上都是Y平面的尺寸的一半。

存在三种帧:丢弃帧、I-帧和P-帧。

  • 丢弃帧不进行编码,只是被视为自上一帧以来没有变化。
  • I-帧只编码完整帧。
  • P-帧将帧编码为相对于上一帧的差分。每个宏块都有一个像素偏移,用于从上一帧复制,宏块还可以编码每像素的差分(量化到0..255的范围)。

音频

自编解码器版本2.0.0起,已从规范中删除音频。

您可以使用任何音频流格式与PFV视频流一起使用,无论它是嵌入到某种容器格式中还是直接与视频文件一起发送。对于轻量级的CPU要求,QOA是音频轨道的一个不错的选择。

依赖关系

~230–530KB