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