【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

目录
相关文章
|
1月前
|
算法 数据处理 开发者
FFmpeg库的使用与深度解析:解码音频流流程
FFmpeg库的使用与深度解析:解码音频流流程
36 0
|
1月前
|
存储 编解码 数据处理
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码(三)
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码
35 0
|
3月前
|
Linux 编译器 数据安全/隐私保护
Windows10 使用MSYS2和VS2019编译FFmpeg源代码-测试通过
FFmpeg作为一个流媒体的整体解决方案,在很多项目中都使用了它,如果我们也需要使用FFmpeg进行开发,很多时候我们需要将源码编译成动态库或者静态库,然后将库放入到我们的项目中,这样我们就能在我们的项目中使用FFmpeg提供的接口进行开发。关于FFmpeg的介绍这里就不过多说明。
76 0
|
7月前
|
C++ Windows
FFmpeg入门及编译 3
FFmpeg入门及编译
54 0
|
7月前
|
编解码 API 开发工具
FFmpeg入门及编译 1
FFmpeg入门及编译
97 0
|
24天前
|
开发工具
使用FFmpeg4.3.1的SDK官方开发包编译ffmpeg.c(二)
使用FFmpeg4.3.1的SDK官方开发包编译ffmpeg.c(二)
12 0
|
7月前
|
API C语言 C++
FFmpeg入门及编译 2
FFmpeg入门及编译
80 0
|
3月前
|
编解码 Ubuntu C++
WebAssembly01--web 编译FFmpeg(WebAssembly版)库
WebAssembly01--web 编译FFmpeg(WebAssembly版)库
21 0
|
3月前
|
存储 Ubuntu 开发工具
ffmpeg笔记(二)windows下和ubuntu-16.04下ffmpeg编译
ffmpeg笔记(二)windows下和ubuntu-16.04下ffmpeg编译
|
4月前
ffmpeg的下载、编译与安装
ffmpeg的下载、编译与安装
104 0