【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码(一)

简介: 【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码

1. 引言

1.1 FFmpeg简介

FFmpeg是一个自由软件,可以运行音频和视频多种格式的录影、转换、流功能,包含了libavcodec——这是一个用于多个项目中音频和视频的解码器库,以及libavformat——一个音频/视讯封装格式的解码器库。FFmpeg在编程中被广泛应用,它的强大功能使得开发者可以更加方便地处理音视频数据。

在C++领域,我们可以通过调用FFmpeg提供的API,对音视频数据进行各种操作,如解码、编码、转码、滤镜处理等。这使得FFmpeg成为音视频处理领域的重要工具。

1.2 YUV和PCM的基础知识

1.2.1 YUV

YUV是一种颜色编码方法,常用于视频系统。在YUV中,Y代表亮度(Luminance),U和V代表色度(Chrominance)。YUV的设计考虑到了人眼对亮度信息比色度信息更敏感的特性,因此在视频压缩时,可以通过降低色度信息的精度,而保持亮度信息的精度,以实现高效的压缩。

在FFmpeg中,解码后的视频帧数据通常以YUV格式存储。每个像素的颜色信息由一个Y值和一个UV值对表示,其中Y值表示像素的亮度,UV值对表示像素的色度。

1.2.2 PCM

PCM(Pulse Code Modulation,脉冲编码调制)是一种数字表示模拟信号的方法,在音频领域被广泛使用。在PCM中,模拟信号在时间上进行等间隔采样,并将每个采样值量化为数字。PCM数据可以有不同的参数,例如采样率(每秒的样本数)、采样大小(每个样本的位数)、声道数等。

在FFmpeg中,解码后的音频数据通常被存储为PCM数据。每个音频样本的数据由一个或多个PCM值表示,每个PCM值表示在一个特定时间点的音频信号的振幅。

在接下来的章节中,我们将深入探讨YUV和PCM在FFmpeg中的应用,以及如何在C++中使用FFmpeg进行音视频数据的处理。我们将通过实例和源码分析,揭示这些技术背后的原理,并提供一些实用的编程技巧。

2. FFmpeg中的YUV和AVFrame

在FFmpeg中,解码视频后的默认格式是YUV,这个YUV格式的数据是被存储在AVFrame结构体中的。下面我们将详细介绍YUV在FFmpeg中的角色,以及AVFrame的作用和结构。

2.1 YUV在FFmpeg中的角色

YUV是一种颜色编码系统,用于视频系统如电视和计算机图形。在这种格式中,Y是亮度分量(也称为灰度),而U和V是色度分量(代表颜色信息)。在FFmpeg中,解码后的视频数据通常是以YUV格式存储的。

在FFmpeg的解码流程中,解码器将编码的数据(例如,H.264编码的视频流)解码为原始的音频/视频帧,并将这些帧存储在AVFrame结构体中。对于视频,这些帧通常是YUV格式的。

下图是FFmpeg的解码流程:

2.2 AVFrame的作用和结构

AVFrame是FFmpeg中用来存储解码后的音频/视频帧的数据结构。AVFrame结构体包含了帧的数据以及一些元数据,例如,帧的宽度和高度(对于视频)、采样率(对于音频)、时间戳(PTS和DTS)等。你可以通过AVFrame的成员变量来访问这些数据和元数据。

在C++中,AVFrame的定义如下:

typedef struct AVFrame {
    uint8_t *data[AV_NUM_DATA_POINTERS]; // 指向帧数据的指针数组
    int linesize[AV_NUM_DATA_POINTERS];  // 每行数据的大小
    ...
    int width, height;                   // 帧的宽度和高度
    int format;                          // 帧的格式(例如,YUV420P)
    ...
    int64_t pts, dts;                    // 时间戳
    ...
} AVFrame;

在这个结构体中,data数组是指向帧数据的指针,linesize数组是每行数据的大小。widthheight是帧的宽度和高度,format是帧的格式,ptsdts是时间戳。

2.3 YUV和AVFrame的关系

当你说“解码后都是AVFrame包”,实际上是指解码后的音频/视频帧被存储在AVFrame结构体中。这并不矛盾,因为AVFrame是用来存储解码后的帧,而这些帧的格式通常是YUV(对于视频)。

在FFmpeg中,解码器将编码的数据解码为原始的音频/视频帧,并将这些帧存储在AVFrame结构体中。对于视频,这些帧通常是YUV格式的。因此,你可以将YUV数据看作是存储在AVFrame中的原始视频数据。

例如,如果你想从AVFrame中获取YUV数据,你可以这样做:

AVFrame *frame = ...; // 已经解码的帧
uint8_t *y_data = frame->data[0]; // Y数据
uint8_t *u_data = frame->data[1]; // U数据
uint8_t *v_data = frame->data[2]; // V数据

在这个例子中,frame->data[0]frame->data[1]frame->data[2]分别指向Y、U和V数据。你可以直接使用这些数据,或者将它们转换为其他格式(例如,RGB)。

总的来说,YUV和AVFrame在FFmpeg中的关系是:YUV是解码后的视频数据的格式,而AVFrame是存储这些数据的结构体。

3. YUV数据的处理

在本章中,我们将深入探讨如何在FFmpeg中处理YUV数据。我们将首先讨论如何直接使用YUV数据,然后讨论如何将YUV数据转换为RGB数据,最后讨论如何优化YUV数据的处理。

3.1 YUV数据的直接使用

如果你的显示设备或渲染库可以直接处理YUV格式的数据,那么你可以直接从AVFrame中取出YUV数据进行显示,无需进行任何转换。这种情况下,你的代码可能会类似于以下的样子:

// 假设frame是一个已经解码的AVFrame
AVFrame *frame = ...;
// 获取YUV数据
uint8_t *y_data = frame->data[0];
uint8_t *u_data = frame->data[1];
uint8_t *v_data = frame->data[2];
// 使用YUV数据
display_yuv_data(y_data, u_data, v_data, frame->width, frame->height);

在这个示例中,我们首先从AVFrame中获取YUV数据,然后将这些数据传递给一个名为display_yuv_data的函数,该函数负责将YUV数据显示出来。

3.2 YUV到RGB的转换

然而,许多常见的显示设备和渲染库(例如,SDL、OpenGL)通常只能处理RGB格式的数据。在这种情况下,你需要将YUV数据转换为RGB数据才能进行显示。FFmpeg提供了一个名为swscale的库,可以用来进行这种转换。

以下是一个使用swscale库将YUV数据转换为RGB数据的示例:

// 假设frame是一个已经解码的AVFrame
AVFrame *frame = ...;
// 创建一个新的AVFrame来存储RGB数据
AVFrame *rgb_frame = av_frame_alloc();
rgb_frame->format = AV_PIX_FMT_RGB24;
rgb_frame->width = frame->width;
rgb_frame->height = frame->height;
av_frame_get_buffer(rgb_frame, 0);
// 创建swscale上下文
struct SwsContext *sws_ctx = sws_getContext(
    frame->width, frame->height, (AVPixelFormat)frame->format,
    rgb_frame->width, rgb_frame->height, (AVPixelFormat)rgb_frame->format,
    SWS_BILINEAR, NULL, NULL, NULL);
// 将YUV数据转换为RGB数据
sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, rgb_frame->data, rgb_frame->linesize);
// 使用RGB数据
display_rgb_data(rgb_frame->data[0], rgb_frame->width, rgb_frame->height);
// 释放资源
sws_freeContext(sws_ctx);
av_frame_free(&rgb_frame);

在这个示例中,我们首先创建了一个新的AVFrame来存储RGB数据,然后创建了一个swscale上下文,用于进行YUV到RGB的转换。然后,我们使用sws_scale函数将YUV数据转换为RGB数据,最后将RGB数据显示出来。

3.3 YUV数据的优化策略

YUV到RGB的转换是一个计算密集型的操作,可能会消耗一定的计算资源。因此,如果可能,最好直接使用YUV数据进行显示,以避免这种转换。如果必须进行转换,你可以考虑使用一些优化策略,例如,使用硬件加速、预先转换和缓存转换结果等,以减少转换的开销。

以下是一些可能的优化策略:

  • 硬件加速:一些硬件设备(例如,GPU)可以进行高效的YUV到RGB的转换。如果你的环境支持硬件加速,你可以考虑使用硬件加速来进行转换。
  • 预先转换:如果你知道你将需要将YUV数据转换为RGB数据,你可以在解码时就进行转换,而不是在显示时才进行转换。这样,你可以在解码和显示之间的空闲时间进行转换,从而减少显示时的延迟。
  • 缓存转换结果:如果你需要多次显示同一帧,你可以将转换结果缓存起来,而不是每次显示时都进行转换。这样,你可以避免重复的转换,从而减少计算开销。

以上就是本章的内容,我们详细讨论了如何在FFmpeg中处理YUV数据,包括如何直接使用YUV数据,如何将YUV数据转换为RGB数据,以及如何优化YUV数据的处理。在下一章中,我们将讨论如何在FFmpeg中处理PCM数据。

4. FFmpeg中的PCM和AVFrame

在音频处理中,PCM(Pulse Code Modulation,脉冲编码调制)和AVFrame是两个非常重要的概念。PCM是一种原始的、未压缩的音频数据格式,而AVFrame则是FFmpeg中用来存储解码后的音频/视频帧的数据结构。在本章节中,我们将深入探讨PCM在FFmpeg中的角色,以及如何在AVFrame中处理PCM数据。

4.1 PCM在FFmpeg中的角色

PCM是一种常用的音频数据格式,它是一种未经过压缩的原始音频数据格式。在PCM中,音频信号被分为一系列的样本,每个样本代表了在一个特定时间点的音频信号的振幅。每个样本都被量化为一个特定的数值,然后被编码为二进制数据。PCM数据可以有不同的参数,例如样本率(每秒的样本数)、样本大小(每个样本的位数)、声道数(例如,单声道、立体声)等。

在FFmpeg中,解码后的音频数据通常被存储为PCM数据。这是因为PCM数据是一种原始的、未压缩的音频数据,它可以直接被音频播放设备使用,也可以方便地进行进一步的处理和转换。

4.2 PCM数据在AVFrame中的表现

在FFmpeg中,AVFrame是用来存储解码后的音频/视频帧的数据结构。对于音频数据,AVFrame中的数据指针指向的就是PCM数据。

AVFrame结构体包含了帧的数据以及一些元数据,例如,帧的采样率(对于音频)、时间戳(PTS和DTS)等。你可以通过AVFrame的成员变量来访问这些数据和元数据。

下面是一个简单的示例,展示了如何从AVFrame中取出PCM数据:

// 假设frame是一个已经解码的音频帧
AVFrame *frame = ...;
// 数据指针指向的就是PCM数据
uint8_t *pcm_data = frame->data[0];
// 样本数可以通过nb_samples来获取
int sample_count = frame->nb_samples;
// 样本格式可以通过format来获取
enum AVSampleFormat sample_format = (enum AVSampleFormat)frame->format;
// 根据样本格式,可以知道每个样本的大小
int sample_size = av_get_bytes_per_sample(sample_format);
// 现在,你可以遍历所有的样本
for (int i = 0; i < sample_count; ++i) {
    // 根据样本大小,从pcm_data中取出一个样本
    uint8_t *sample = pcm_data + i * sample_size;
    // 现在,你可以处理这个样本了
    // ...
}

在这个示例中,我们首先从AVFrame中取出了PCM数据的指针,然后根据样本数和样本大小,遍历了所有的样本。这就是如何在AVFrame中处理PCM数据的基本方法。

4.3 PCM和AVFrame的关系

从上述内容中,我们可以看到PCM和AVFrame之间的关系:在FFmpeg中,解码后的音频帧被存储在AVFrame结构体中,而这些帧的数据就是PCM数据。因此,当我们说“解码后的音频帧是PCM数据”,实际上是指解码后的音频帧被存储在AVFrame结构体中,而这些帧的数据就是PCM数据。

这种关系可以用下面的图来表示:

在这个图中,我们可以看到PCM数据被存储在AVFrame中,而AVFrame则被用来存储解码后的音频帧。这就是PCM和AVFrame的关系。

在下一章节中,我们将探讨如何处理PCM数据,包括如何从PCM数据中提取样本,以及如何将PCM数据转换为适合音频播放设备的格式。


【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码(二)https://developer.aliyun.com/article/1467277

目录
相关文章
|
4月前
|
编解码 语音技术 内存技术
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
《FFmpeg开发实战:从零基础到短视频上线》一书中的“5.1.2 把音频流保存为PCM文件”章节介绍了将媒体文件中的音频流转换为原始PCM音频的方法。示例代码直接保存解码后的PCM数据,保留了原始音频的采样频率、声道数量和采样位数。但在实际应用中,有时需要特定规格的PCM音频。例如,某些语音识别引擎仅接受16位PCM数据,而标准MP3音频通常采用32位采样,因此需将32位MP3音频转换为16位PCM音频。
126 0
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
|
3月前
|
编解码 监控 网络协议
如何使用FFmpeg实现RTSP推送H.264和H.265(HEVC)编码视频
本文详细介绍了如何使用FFmpeg实现RTSP推送H.264和H.265(HEVC)编码视频。内容涵盖环境搭建、编码配置、服务器端与客户端实现等方面,适合视频监控系统和直播平台等应用场景。通过具体命令和示例代码,帮助读者快速上手并实现目标。
808 6
|
4月前
|
Java 数据安全/隐私保护
Java ffmpeg 实现视频加文字/图片水印功能
【10月更文挑战第22天】在 Java 中使用 FFmpeg 实现视频加文字或图片水印功能,需先安装 FFmpeg 并添加依赖(如 JavaCV)。通过构建 FFmpeg 命令行参数,使用 `drawtext` 滤镜添加文字水印,或使用 `overlay` 滤镜添加图片水印。示例代码展示了如何使用 JavaCV 实现文字水印。
311 1
|
4月前
|
计算机视觉 Python
FFMPEG学习笔记(一): 提取视频的纯音频及无声视频
本文介绍了如何使用FFmpeg工具从视频中提取纯音频和无声视频。提供了具体的命令行操作,例如使用`ffmpeg -i input.mp4 -vn -c:a libmp3lame output.mp3`来提取音频,以及`ffmpeg -i input.mp4 -c:v copy -an output.mp4`来提取无声视频。此外,还包含了一个Python脚本,用于批量处理视频文件,自动提取音频和生成无声视频。
197 1
|
4月前
FFmpeg学习笔记(二):多线程rtsp推流和ffplay拉流操作,并储存为多路avi格式的视频
这篇博客主要介绍了如何使用FFmpeg进行多线程RTSP推流和ffplay拉流操作,以及如何将视频流保存为多路AVI格式的视频文件。
528 0
|
4月前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
424 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
4月前
|
XML 开发工具 Android开发
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
ExoPlayer最初是为了解决Android早期MediaPlayer控件对网络视频兼容性差的问题而推出的。现在,Android官方已将其升级并纳入Jetpack的Media3库,使其成为音视频操作的统一引擎。新版ExoPlayer支持多种协议,解决了设备和系统碎片化问题,可在整个Android生态中一致运行。通过修改`build.gradle`文件、布局文件及Activity代码,并添加必要的权限,即可集成并使用ExoPlayer进行网络视频播放。具体步骤包括引入依赖库、配置播放界面、编写播放逻辑以及添加互联网访问权限。
280 1
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
|
4月前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
114 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
5月前
|
XML Java Android开发
FFmpeg开发笔记(五十二)移动端的国产视频播放器GSYVideoPlayer
GSYVideoPlayer是一款国产移动端视频播放器,支持弹幕、滤镜、广告等功能,采用IJKPlayer、Media3(EXOPlayer)、MediaPlayer及AliPlayer多种内核。截至2024年8月,其GitHub星标数达2万。集成时需使用新版Android Studio,并按特定步骤配置依赖与权限。提供了NormalGSYVideoPlayer、GSYADVideoPlayer及ListGSYVideoPlayer三种控件,支持HLS、RTMP等多种直播链接。
216 18
FFmpeg开发笔记(五十二)移动端的国产视频播放器GSYVideoPlayer
|
4月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
142 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库