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

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

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


7.2 YUV和RGB在视频数据处理中的应用

YUV和RGB是两种不同的颜色空间。RGB是基于颜色光的三原色(红、绿、蓝)来描述颜色的,每个像素的颜色由这三种颜色的强度组合而成。而YUV则是将颜色信息分为亮度信息(Y)和色度信息(UV),这种方式更接近人眼对颜色的感知方式,因此在视频编码和传输中更常用。

在处理视频数据时,可能需要根据具体的需求和环境,选择合适的颜色模型。例如,如果你的显示设备或者渲染库可以直接处理YUV格式的数据,那么你可以直接从AVFrame中取出YUV数据进行显示,无需进行任何转换。然而,许多常见的显示设备和渲染库(例如,SDL、OpenGL)通常只能处理RGB格式的数据。在这种情况下,你需要将YUV数据转换为RGB数据才能进行显示。

在C++中,我们可以使用FFmpeg提供的swscale库来进行这种转换。以下是一个简单的示例:

// 创建一个swsContext,用于YUV到RGB的转换
struct SwsContext* sws_ctx = sws_getContext(width, height, AV_PIX_FMT_YUV420P,
                                            width, height, AV_PIX_FMT_RGB24,
                                            SWS_BILINEAR, NULL, NULL, NULL);
if (!sws_ctx) {
    // 错误处理
}
// 创建一个AVFrame,用于存储RGB数据
AVFrame* rgb_frame = av_frame_alloc();
if (!rgb_frame) {
    // 错误处理
}
rgb_frame->format = AV_PIX_FMT_RGB24;
rgb_frame->width = width;
rgb_frame->height = height;
// 分配RGB数据的内存
int ret = av_frame_get_buffer(rgb_frame, 0);
if (ret < 0) {
    // 错误处理
}
// 将YUV数据转换为RGB数据
sws_scale(sws_ctx, yuv_frame->data, yuv_frame->linesize, 0, height,
          rgb_frame->data, rgb_frame->linesize);
// 此时,rgb_frame中存储的就是RGB数据,可以直接用于显示

在这个示例中,我们首先创建了一个swsContext,用于YUV到RGB的转换。然后,我们创建了一个AVFrame,用于存储RGB数据,并分配了相应的内存。最后,我们使用sws_scale函数将YUV数据转换为RGB数据。此时,rgb_frame中存储的就是RGB数据,可以直接用于显示。

7.3 视频数据的最小单位:帧

在视频中,帧(Frame)是最小的独立单位,每一帧都代表了一个独立的图像。在处理视频数据时,我们通常需要处理一系列的视频帧,每一帧都是一个独立的YUV或RGB图像。

在FFmpeg中,每一帧的数据被存储在一个AVFrame结构体中。AVFrame结构体包含了帧的数据以及一些元数据,例如,帧的宽度和高度、时间戳(PTS和DTS)等。你可以通过AVFrame的成员变量来访问这些数据和元数据。

在处理视频帧时,我们通常需要遍历每一帧,对每一帧进行相应的处理。以下是一个简单的示例:

// 创建一个AVFrame,用于存储解码后的帧
AVFrame* frame = av_frame_alloc();
if (!frame) {
    // 错误处理
}
// 循环读取和解码帧
while (1) {
    // 读取一帧
    int ret = av_read_frame(format_ctx, &pkt);
    if (ret < 0) {
        // 错误处理
    }
    // 判断是否是我们需要的视频流
    if (pkt.stream_index == video_stream_index) {
        // 解码帧
       
 ret = avcodec_send_packet(codec_ctx, &pkt);
        if (ret < 0) {
            // 错误处理
        }
        while (ret >= 0) {
            ret = avcodec_receive_frame(codec_ctx, frame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                break;
            } else if (ret < 0) {
                // 错误处理
            }
            // 此时,frame中存储的就是解码后的帧,可以进行相应的处理
            // 例如,你可以将帧的数据转换为RGB格式,然后显示出来
        }
    }
    // 释放pkt
    av_packet_unref(&pkt);
}
// 释放frame
av_frame_free(&frame);

在这个示例中,我们首先创建了一个AVFrame,用于存储解码后的帧。然后,我们进入一个循环,不断地读取和解码帧。对于每一帧,我们首先判断它是否是我们需要的视频流,如果是,我们就进行解码,并对解码后的帧进行相应的处理。最后,我们释放了pkt和frame。

这就是视频数据的采集和处理的基本过程。在下一章中,我们将讨论音频数据的采集和处理。

8. 音频数据的采集和处理

在本章中,我们将深入探讨音频数据的采集和处理过程。我们将从音频数据的采集开始,然后讨论PCM(Pulse Code Modulation,脉冲编码调制)在音频数据处理中的应用,最后我们将探讨音频数据的最小单位:样本。

8.1 音频数据的采集过程

音频数据的采集过程可以分为以下几个步骤:

  1. 音频采集设备(如麦克风)首先将声音(这是一种模拟信号)转换为电信号。
  2. 通过模数转换器(ADC,Analog-to-Digital Converter)将电信号转换为数字信号。这个数字信号就是 PCM 数据。

下图展示了音频采集的过程:

在这个过程中,声音信号会被样本化(sampling),量化(quantization),并编码(encoding):

  • 样本化是指在一定的时间间隔内测量信号的振幅。样本化的频率(即每秒的样本数)通常被称为采样率(sample rate)。常见的采样率包括 44.1 kHz(CD 质量)和 48 kHz(DVD 质量)。
  • 量化是指将每个样本的振幅值映射到一个有限的集合。量化的精度(即振幅值的可能数)通常被称为位深(bit depth)。常见的位深包括 16 位(CD 质量)和 24 位(高分辨率音频)。
  • 编码是指将量化后的样本值转换为二进制数据。

这个过程会生成 PCM 数据,这是一种原始的、未压缩的音频数据格式。

8.2 PCM在音频数据处理中的应用

在音频数据处理中,PCM(Pulse Code Modulation,脉冲编码调制)是一种常用的音频数据格式。PCM 是一种未经过压缩的原始音频数据格式。在 PCM 中,音频信号被分为一系列的样本,每个样本代表了在一个特定时间点的音频信号的振幅。

在 FFmpeg 中,解码后的音频数据通常被存储为 PCM 数据。AVFrame 中的数据指针指向的就是这些 PCM 数据。你可以直接从 AVFrame 中取出这些数据,并将其转换为适合你的音频播放设备的格式(例如,float 数组)。

在处理 PCM 数据时,你需要知道以下的参数:

  • 样本大小:这决定了每个样本的位数。例如,如果样本大小为 16 位,那么你需要每 16 位取出一个样本。
  • 样本格式:这决定了样本的表示方式。例如,样本可以是有符号的整数、无符号的整数、浮点数等。
  • 声道布局:这决定了多声道音频中各个声道的顺序。例如,对于立体声音频,声道布局可以是左声道在前,右声道在后,也可以是右声道在前,左声道在后。
  • 声道数:这决定了每个立体声样本中包含的声道数。例如,对于立体声音频,声道数为 2。

你需要根据这些参数,正确地从 AVFrame 的数据指针中取出样本,并将样本转换为适合你的音频播放设备的格式。例如,如果你的音频播放设备需要 float 格式的数据,你可能需要将样本从原始的 PCM 格式转换为 float 格式。

8.3 音频数据的最小单位:样本

在音频数据中,样本(Sample)是最小的独立单位。在 PCM 中,音频信号被分为一系列的样本,每个样本代表了在一个特定时间点的音频信号的振幅。样本的数量、大小和格式都会影响音频的质量和特性。

样本的数量,也就是采样率,决定了音频的频率响应。根据奈奎斯特定理,采样率必须至少是音频信号最高频率的两倍,才能无失真地重建音频信号。因此,对于人耳能听到的最高频率(大约 20 kHz),CD 的采样率为 44.1 kHz,这是足够的。

样本的大小,也就是位深,决定了音频的动态范围。位深越大,动态范围越大,音频信号的细节就越丰富。例如,16 位的位深可以提供大约 96 dB 的动态范围,而 24 位的位深可以提供大约 144 dB 的动态范围。

样本的格式,例如有符号的整数、无符号的整数、浮点数等,决定了样本的表示方式。不同的样本格式可能需要不同的处理方式。

在处理音频数据时,理解样本的概念是非常重要的。只有正确地处理样本,才能正确地处理音频数据。

结语

FFmpeg在音视频处理中的重要性

FFmpeg是一个强大的音视频处理库,它提供了一套完整的工具和API,可以用来进行音视频的编解码、转码、流化等操作。在C/C++和嵌入式领域,FFmpeg是音视频处理的首选工具。

FFmpeg的强大之处在于它的灵活性和功能性。它支持大量的音视频编解码器,可以处理几乎所有的音视频格式。它的API设计得非常灵活,可以满足各种复杂的音视频处理需求。

在实际应用中,我们可以使用FFmpeg进行各种音视频处理任务,例如:

解码视频文件,获取原始的YUV数据

将YUV数据转换为RGB数据,以便在计算机屏幕上显示

解码音频文件,获取原始的PCM数据

将PCM数据转换为其他格式,以便在音频设备上播放

编码YUV或PCM数据,生成视频或音频文件

将一个音视频格式转换为另一个格式(转码)

将音视频数据流化,以便进行网络传输

在使用FFmpeg时,我们需要了解一些关键的概念和技术,例如YUV、PCM、I帧、P帧和B帧等。这些概念和技术是音视频处理的基础,理解它们对于有效地使用FFmpeg非常重要。

在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。

这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。

我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。

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