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

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

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


5. PCM数据的处理

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

5.1 PCM数据的直接使用

在FFmpeg中,你可以直接从AVFrame中取出PCM数据,并将其转换为适合你的音频播放设备的格式。例如,如果你的音频播放设备需要float格式的数据,你可能需要将样本从原始的PCM格式转换为float格式。

下面是一个简单的示例,展示了如何从AVFrame中取出PCM数据,并将其转换为float数组:

// 假设frame是一个已经解码的AVFrame
AVFrame *frame = ...;
// 获取样本大小(以字节为单位)
int sample_size = av_get_bytes_per_sample(frame->sample_format);
// 获取声道数
int channels = frame->channels;
// 创建一个float数组来存储转换后的数据
float *data = new float[frame->nb_samples * channels];
// 遍历每个样本
for (int i = 0; i < frame->nb_samples; i++) {
    // 遍历每个声道
    for (int ch = 0; ch < channels; ch++) {
        // 获取样本数据
        uint8_t *sample_data = frame->data[ch] + sample_size * i;
        // 根据样本大小和样本格式,将样本数据转换为float
        float sample_value;
        if (sample_size == 1) {
            // 8位样本
            sample_value = *sample_data / 255.0f;
        } else if (sample_size == 2) {
            // 16位样本
            sample_value = *((int16_t *)sample_data) / 32767.0f;
        } else {
            // 其他情况
            sample_value = 0.0f;
        }
        // 存储转换后的样本值
        data[i * channels + ch] = sample_value;
    }
}
// 现在,data数组中存储的就是转换后的音频数据,可以传输给音频播放设备

这个示例中,我们首先获取了样本大小和声道数,然后创建了一个float数组来存储转换后的数据。然后,我们遍历了每个样本和每个声道,将样本数据从原始的PCM格式转换为float格式,并存储在float数组中。最后,我们可以将这个float数组传输给音频播放设备。

5.2 PCM数据的转换

在处理PCM数据时,你需要根据PCM的参数(例如,样本大小、样本格式、声道布局、声道数)来解释数据,并将样本转换为适合你的音频播放设备的格式。这个过程可能涉及到一些复杂的计算和转换,但是FFmpeg提供了一些工具和函数来帮助你进行这些操作。

例如,FFmpeg提供了一个名为swresample的库,可以用来进行音频重采样和格式转换。你可以使用这个库来将PCM数据转换为其他格式,例如,将16位整数PCM转换为float PCM,或者将立体声PCM转换为单声道PCM。

下面是一个简单的示例,展示了如何使用swresample库来进行PCM数据的转换:

// 假设frame是一个已经解码的AVFrame
AVFrame *frame = ...;
// 创建一个重采样上下文
SwrContext *swr_ctx = swr_alloc_set_opts(NULL,
                                         AV_CH_LAYOUT_MONO,   // 输出声道布局:单声道
                                         AV_SAMPLE_FMT_FLT,   // 输出样本格式:float
                                         44100,               // 输出样本率:44100Hz
                                         frame->channel_layout, // 输入声道布局
                                         frame->format,         // 输入样本格式
                                         frame->sample_rate,    // 输入样本率
                                         0, NULL);
if (!swr_ctx) {
    // 错误处理
}
// 初始化重采样上下文
if (swr_init(swr_ctx) < 0) {
    // 错误处理
}
// 创建一个新的AVFrame来存储转换后的数据
AVFrame *out_frame = av_frame_alloc();
out_frame->channel_layout = AV_CH_LAYOUT_MONO;
out_frame->format = AV_SAMPLE_FMT_FLT;
out_frame->sample_rate = 44100;
out_frame->nb_samples = frame->nb_samples;
 // 假设输出样本数和输入样本数相同
// 分配数据缓冲区
av_frame_get_buffer(out_frame, 0);
// 进行重采样
if (swr_convert(swr_ctx, out_frame->data, out_frame->nb_samples, (const uint8_t **)frame->data, frame->nb_samples) < 0) {
    // 错误处理
}
// 现在,out_frame中存储的就是转换后的音频数据,可以传输给音频播放设备
// 释放资源
swr_free(&swr_ctx);
av_frame_free(&out_frame);

这个示例中,我们首先创建了一个重采样上下文,并设置了输入和输出的参数。然后,我们创建了一个新的AVFrame来存储转换后的数据,并分配了数据缓冲区。然后,我们调用swr_convert函数进行重采样。最后,我们释放了重采样上下文和AVFrame。

5.3 PCM数据处理流程图

下面的流程图展示了PCM数据的处理流程:

在这个流程中,我们首先从AVFrame中取出PCM数据,然后根据PCM参数(样本大小、样本格式、声道布局、声道数)解释数据,然后将样本转换为适合音频播放设备的格式,最后将数据传输给音频播放设备。

5.4 PCM数据处理方法对比

下面的表格总结了处理PCM数据的一些常用方法的对比:

方法 优点 缺点
直接使用PCM数据 简单,不需要额外的转换 可能需要手动处理样本大小和样本格式的转换
使用swresample库进行转换 可以处理复杂的转换,例如声道布局的转换和样本格式的转换 需要创建和管理重采样上下文,可能需要处理错误

在选择处理PCM数据的方法时,你需要根据你的需求和音频播放设备的能力来选择最适合的方法。

6. 视频编码中的I帧、P帧和B帧

在本章中,我们将深入探讨视频编码中的I帧、P帧和B帧。这些帧类型是视频编码中的基础概念,理解它们对于理解视频编码和解码的过程至关重要。

6.1 I帧、P帧和B帧的定义和特性

在视频编码中,我们通常会遇到I帧(Intra-coded picture)、P帧(Predicted picture)和B帧(Bidirectionally predicted picture)这三种类型的帧。它们的定义和特性如下:

  • I帧:也被称为关键帧,是自我完整的帧,不依赖于任何其他帧进行解码。I帧通常包含了图像的全部信息,因此在视频中的数据量最大。
  • P帧:依赖于前面的I帧或P帧进行解码,它只存储与参考帧的差异信息。P帧的数据量通常小于I帧,因为它只包含差异信息。
  • B帧:依赖于前面和后面的I帧或P帧进行解码,它只存储与前后参考帧的差异信息。B帧的数据量通常是最小的,因为它只包含差异信息。

下表总结了这三种帧类型的主要特性:

帧类型 完整性 依赖性 数据量
I帧 完整的 无依赖 最大
P帧 差异的 有依赖 中等
B帧 差异的 有依赖 最小

6.2 I帧、P帧和B帧在编码和解码中的作用

在视频编码中,I帧、P帧和B帧的主要作用是通过存储差异信息来减少数据量,从而实现视频的压缩。

例如,假设我们有一个视频序列,其中包含10帧图像。如果我们将每一帧都作为I帧进行编码,那么每一帧都需要存储完整的图像信息,这将产生大量的数据。然而,如果我们将第一帧作为I帧,然后将后续的帧作为P帧或B帧,那么后续的帧只需要存储与前一帧的差异信息,这将大大减少数据量。

在解码时,解码器会首先解码I帧,然后根据P帧和B帧的差异信息来恢复出原始的图像序列。

6.3 I帧、P帧和B帧与YUV、PCM的关系

I帧、P帧和B帧是描述帧之间的依赖关系和压缩方式的概念,而YUV和PCM则是描述帧的颜色和音频信息的方式。

在视频编码中,每一帧的图像信息通常以YUV格式存储,而音频信息则以PCM格式存储。然后,这些帧会根据它们之间的依赖关系和差异信息,被编码为I帧、P帧或B帧。

在解码后,无论原来是I帧、P帧还是B帧,都已经被转换成了完整的图像数据,可以用YUV或RGB等颜色空间来表示。这些解码后的帧都是可以独立显示的,不再依赖于其他帧。

下面是一个简单的示例,展示了如何使用FFmpeg库来解码视频帧,并获取YUV数据:

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
int main() {
    AVFormatContext *pFormatCtx = avformat_alloc_context();
    // 打开视频文件
    if(avformat_open_input(&pFormatCtx, "input.mp4", NULL, NULL)!=0)
        return -1;
    // 查找视频流
    if(avformat_find_stream_info(pFormatCtx, NULL)<0)
        return -1;
    // 找到视频流的编码器
    AVCodecContext *pCodecCtx = pFormatCtx->streams[0]->codec;
    AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec==NULL)
        return -1;
    // 打开编码器
    if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
        return -1;
    // 分配视频帧
    AVFrame *pFrame = av_frame_alloc();
    // 读取视频帧
    AVPacket packet;
    while(av_read_frame(pFormatCtx, &packet)>=0) {
        if(packet.stream_index == 0) {
            // 解码视频帧
            int got_picture;
            avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet);
            if(got_picture) {
                // 此时,pFrame中的数据就是YUV数据
                // 可以对这些数据进行进一步的处理
            }
        }
        av_packet_unref(&packet);
    }
    // 释放资源
    av_frame_free(&pFrame);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
    return 0;
}

在这个示例中,我们首先打开了一个视频文件,然后找到了视频流的编码器。然后,我们读取了视频帧,并使用编码器将它们解码为YUV数据。这些YUV数据可以用于进一步的处理,例如转换为RGB数据,或者进行图像分析等。

希望这个章节能帮助你更深入地理解视频编码中的I帧、P帧和B帧,以及它们与YUV和PCM的关系。在下一章中,我们将探讨视频数据的采集和处理。

7. 视频数据的采集和处理

在本章中,我们将深入探讨视频数据的采集和处理过程,包括视频数据的采集方式,YUV和RGB在视频数据处理中的应用,以及视频数据的最小单位——帧。

7.1 视频数据的采集过程

视频数据的采集通常由摄像头完成。摄像头捕获的原始视频数据通常是以YUV格式存储的。YUV是一种颜色编码系统,用于视频系统如电视和计算机图形。在这种格式中,Y是亮度分量(也称为灰度),而U和V是色度分量(代表颜色信息)。

在摄像头捕获图像时,图像的每个像素首先被转换为RGB格式,然后通常被转换为YUV格式。这是因为YUV格式有一些优点,例如它可以更有效地进行视频压缩,因为人眼对亮度信息(Y分量)比色度信息(U和V分量)更敏感,所以可以在保持视觉质量的同时减少色度信息的精度。

然而,尽管YUV是一种常见的视频数据格式,但并不是所有的摄像头都直接输出YUV数据。有些摄像头可能会输出其他格式的数据,例如RGB或者直接输出压缩后的数据(例如JPEG或H.264)。具体的输出格式取决于摄像头的硬件和驱动程序。


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

目录
相关文章
|
3月前
|
编解码 语音技术 内存技术
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
《FFmpeg开发实战:从零基础到短视频上线》一书中的“5.1.2 把音频流保存为PCM文件”章节介绍了将媒体文件中的音频流转换为原始PCM音频的方法。示例代码直接保存解码后的PCM数据,保留了原始音频的采样频率、声道数量和采样位数。但在实际应用中,有时需要特定规格的PCM音频。例如,某些语音识别引擎仅接受16位PCM数据,而标准MP3音频通常采用32位采样,因此需将32位MP3音频转换为16位PCM音频。
109 0
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
|
2月前
|
编解码 监控 网络协议
如何使用FFmpeg实现RTSP推送H.264和H.265(HEVC)编码视频
本文详细介绍了如何使用FFmpeg实现RTSP推送H.264和H.265(HEVC)编码视频。内容涵盖环境搭建、编码配置、服务器端与客户端实现等方面,适合视频监控系统和直播平台等应用场景。通过具体命令和示例代码,帮助读者快速上手并实现目标。
512 6
|
3月前
FFmpeg学习笔记(二):多线程rtsp推流和ffplay拉流操作,并储存为多路avi格式的视频
这篇博客主要介绍了如何使用FFmpeg进行多线程RTSP推流和ffplay拉流操作,以及如何将视频流保存为多路AVI格式的视频文件。
427 0
|
6月前
|
数据采集 大数据 Python
FFmpeg 在爬虫中的应用案例:流数据解码详解
在大数据背景下,网络爬虫与FFmpeg结合,高效采集小红书短视频。需准备FFmpeg、Python及库如Requests和BeautifulSoup。通过设置User-Agent、Cookie及代理IP增强隐蔽性,解析HTML提取视频链接,利用FFmpeg下载并解码视频流。示例代码展示完整流程,强调代理IP对避免封禁的关键作用,助你掌握视频数据采集技巧。
FFmpeg 在爬虫中的应用案例:流数据解码详解
|
6月前
|
语音技术 C语言 Windows
语音识别------ffmpeg的使用01,ffmpeg的安装,会做PPT很好,ffmpeg不具备直接使用,只可以操作解码数据,ffmpeg用C语言写的,得学C语言,ffmpeg的安装
语音识别------ffmpeg的使用01,ffmpeg的安装,会做PPT很好,ffmpeg不具备直接使用,只可以操作解码数据,ffmpeg用C语言写的,得学C语言,ffmpeg的安装
|
7月前
|
Linux 编解码 Python
FFmpeg开发笔记(二十四)Linux环境给FFmpeg集成AV1的编解码器
AV1是一种高效免费的视频编码标准,由AOM联盟制定,相比H.265压缩率提升约27%。各大流媒体平台倾向使用AV1。本文介绍了如何在Linux环境下为FFmpeg集成AV1编解码库libaom、libdav1d和libsvtav1。涉及下载源码、配置、编译和安装步骤,包括设置环境变量以启用这三个库。
329 3
FFmpeg开发笔记(二十四)Linux环境给FFmpeg集成AV1的编解码器
|
7月前
|
编解码 Linux 计算机视觉
python 调用ffmpeg使用usb摄像头录制视频,输出h264格式,自动获取摄像头的最佳帧率和最大画面尺寸
使用 Python 调用 FFmpeg 进行 USB 摄像头视频录制,需先确保安装 FFmpeg 和 Python 的 `subprocess` 模块。代码示例展示了如何自动获取摄像头的最佳帧率和最大分辨率,然后录制视频。首先通过 FFmpeg 列出摄像头格式获取信息,解析出帧率和分辨率,选择最优值。之后调用 FFmpeg 命令录制视频,设置帧率、分辨率等参数。注意 `/dev/video0` 是 Linux 的摄像头设备路径,Windows 系统需相应调整。代码中未直接实现自动获取最佳参数,通常需要借助其他库如 OpenCV。
|
8月前
|
编解码 5G Linux
FFmpeg开发笔记(二十一)Windows环境给FFmpeg集成AVS3解码器
AVS3是中国首个8K及5G视频编码标准,相比AVS2和HEVC性能提升约30%。解码器libuavs3d支持8K/60P视频实时解码,兼容多种平台。《FFmpeg开发实战》书中介绍了在Windows环境下如何集成libuavs3d到FFmpeg。集成步骤包括下载源码、使用Visual Studio 2022编译、调整配置、安装库文件和头文件,以及重新配置和编译FFmpeg以启用libuavs3d。
129 0
FFmpeg开发笔记(二十一)Windows环境给FFmpeg集成AVS3解码器
|
3月前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
298 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
3月前
|
XML 开发工具 Android开发
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
ExoPlayer最初是为了解决Android早期MediaPlayer控件对网络视频兼容性差的问题而推出的。现在,Android官方已将其升级并纳入Jetpack的Media3库,使其成为音视频操作的统一引擎。新版ExoPlayer支持多种协议,解决了设备和系统碎片化问题,可在整个Android生态中一致运行。通过修改`build.gradle`文件、布局文件及Activity代码,并添加必要的权限,即可集成并使用ExoPlayer进行网络视频播放。具体步骤包括引入依赖库、配置播放界面、编写播放逻辑以及添加互联网访问权限。
225 1
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频