从I帧到B帧,H.264编码技术为您构建画面与效果完美结合的视觉盛宴

news2024/10/6 16:31:12

H264之帧编码

H.264,也称为 MPEG-4 AVC (Advanced Video Coding),是一种高效的视频编码标准,用于压缩和存储视频。H.264 利用了预测编码和变换编码等先进的技术,其编码流程与普通视频编码类似,主要包括帧类型判定、运动估计、变换编码、熵编码等步骤。

H.264 的帧编码流程:

帧类型判断

首先,H.264 编码器需要确定当前帧属于哪种类型:I 帧、P 帧还是 B 帧。同样,I 帧是关键帧,P 帧和 B 帧是预测帧。

运动估计

对于 P 帧和 B 帧,H.264 编码器需要进行运动估计来找到当前帧和之前帧之间的运动信息。运动估计时会将待编码帧与之前一定长度的参考帧进行比较,以检测两帧之间的运动差异。

变换编码

H.264 编码器将经过运动估计的差异像素块(残差块)输入到离散余弦变换器 (DCT),将空间信息转换为频域信息。DCT 块的大小可以为 4x4、8x8 或其他尺寸,以匹配不同视频帧的压缩需求。变换编码可以有效地消除视频框架中的局部冗余。

量化

对于经过 DCT 变换之后的残差块,H.264 编码器会对其进行量化。量化通过将块的值除以一个预定义的值并四舍五入来减少数据量。不同的量化矩阵可以用于不同的视频帧,以调整视频质量和压缩率之间的平衡。

熵编码

最后,H.264 编码器将量化后的数据输出到熵编码器,以进一步减少数据量。熵编码是一种将数据序列转换为短码的技术,可以通过对于经常出现的数据序列使用较短的编码来减少数据量。H.264 使用 CABAC 或 CAVLC (Context-based Adaptive Variable Length Coding) 等熵编码方案。

I帧 P帧 B帧编码流程

I帧、P帧和B帧是视频编码中常用的三种帧类型,用于压缩和存储视频。它们的编码流程如下:

I帧编码流程

I (Intra-picture) 帧可以理解为关键帧,它是视频序列中的第一个帧或关键帧。每个 I 帧的编码是独立的,它包含了所有像素的数据,没有依赖其他的帧。因此,I 帧的压缩率不能很好地发挥。

I帧的编码流程如下:

  1. 图像分块:I 帧首先将原始图像分成若干个小块。
  2. DCT 变换:对于每个分块,将其进行离散余弦变换 (DCT),以转换为频域信号。
  3. 量化:对于每个分块,将 DCT 变换之后的频域系数进行量化。量化也是压缩图像的关键步骤之一。
  4. 熵编码:使用熵编码技术对量化后的数据进行编码,以进一步减少数据量。编码之后的结果就是 I 帧的输出。

P帧编码流程

P (Predicted picture) 帧是预测帧,它根据前一帧的参考像素进行编码。P 帧只编码与前一帧之间的像素差异,以从时间维度上减少视频数据的冗余。

P帧的编码流程如下:

  1. 运动估计:首先使用运动估计算法对前一帧和当前帧进行比较,获取两帧之间的运动信息。
  2. 帧间预测:利用得到的运动信息,在前一帧中对应的位置上获取像素值,从而对当前帧进行预测。
  3. 残差编码:将用预测帧预测出的图像和实际的图像进行比较,得到两个图像之间的差异。对这些差异进行编码并输出 P 帧。

B帧编码流程

B (Bidirectionally predictive picture) 帧是双向预测帧,该帧通过前后两个关键帧的像素值进行估计编码。B 帧同样只编码与前后帧之间的像素差异,以后缩小视频数据量。

B帧的编码流程如下:

  1. 运动估计:B 帧通常需要借助前一帧和后一帧进行运动估计。
  2. 帧间预测:参考前后两个关键帧,对当前帧进行帧间预测。
  3. 残差编码:与 P 帧相同,对用预测帧预测出的图像和实际的图像进行比较,得到两个图像之间的差异。对这些差异进行编码并输出 B 帧。

I帧 P帧 B帧编码流程代码示例分析

I帧、P帧、B帧是视频帧编码的重要概念,也是H.264视频编码的核心技术。它们在视频编码中发挥着不同的作用,对于视频质量和压缩率都有着不同的影响。下面是一个使用FFmpeg进行视频编码的I帧、P帧、B帧的编码流程代码示例分析。

#include <stdlib.h>
#include <stdio.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
​
// 帧频
#define FRAME_RATE 25
​
// 视频宽高
#define VIDEO_WIDTH 640
#define VIDEO_HEIGHT 480
#define VIDEO_PIX_FMT AV_PIX_FMT_YUV420P
​
int main(int argc, char **argv)
{
    // 初始化FFmpeg
    av_register_all();
​
    // 分配AVFormatContext和AVOutputFormat
    AVFormatContext *pFormatCtx;
    avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, "output.mp4");
​
    // 查找视频编码器(H.264)
    AVCodec *pCodec;
    pCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!pCodec)
    {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
​
    // 分配AVStream
    AVStream *pStream;
    pStream = avformat_new_stream(pFormatCtx, NULL);
    if (!pStream)
    {
        fprintf(stderr, "Could not allocate stream\n");
        exit(1);
    }
​
    // 设置编码器参数
    AVCodecContext *pCodecCtx;
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if (!pCodecCtx)
    {
        fprintf(stderr, "Could not allocate codec context\n");
        exit(1);
    }
    pCodecCtx->bit_rate = 400000;
    pCodecCtx->width = VIDEO_WIDTH;
    pCodecCtx->height = VIDEO_HEIGHT;
    pCodecCtx->time_base = (AVRational){1, FRAME_RATE};
    pCodecCtx->framerate = (AVRational){FRAME_RATE, 1};
    pCodecCtx->gop_size = 10;
    pCodecCtx->max_b_frames = 1;
    if (pFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
    {
        pCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }
​
    // 打开编码器并写入头文件
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }
    if (avcodec_parameters_from_context(pStream->codecpar, pCodecCtx) < 0)
    {
        fprintf(stderr, "Could not copy the codec parameters to the output stream\n");
        exit(1);
    }
    if (avformat_write_header(pFormatCtx, NULL) < 0)
    {
        fprintf(stderr, "Error occurred when opening output file\n");
        exit(1);
    }
​
    // 准备输入的数据
    AVFrame *src_frame;
    uint8_t *src_data[4];
    int src_linesize[4];
    int ret;
    src_frame = av_frame_alloc();
    if (!src_frame)
    {
        fprintf(stderr, "Could not allocate source video frame\n");
        exit(1);
    }
    src_frame->format = VIDEO_PIX_FMT;
    src_frame->width = VIDEO_WIDTH;
    src_frame->height = VIDEO_HEIGHT;
    ret = av_image_alloc(src_frame->data, src_frame->linesize, VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_PIX_FMT, 16);
    if (ret < 0)
    {
        fprintf(stderr, "Could not allocate source image\n");
        exit(1);
    }
    src_data[0] = src_frame->data[0];
    src_data[1] = src_frame->data[1];
    src_data[2] = src_frame->data[2];
    src_linesize[0] = src_frame->linesize[0];
    src_linesize[1] = src_frame->linesize[1];
    src_linesize[2] = src_frame->linesize[2];
​
    // 编码视频帧
    int64_t pts = 0;
    int i;
    for (i = 0; i < 100; i++)
    {
        // 准备一帧YUV420P数据
        int j, k;
        for (j = 0; j < VIDEO_HEIGHT; j++)
        {
            for (k = 0; k < VIDEO_WIDTH; k++)
            {
                src_data[0][j * src_linesize[0] + k] = (uint8_t)(j + i * 3);
            }
        }
        for (j = 0; j < VIDEO_HEIGHT / 2; j++)
        {
            for (k = 0; k < VIDEO_WIDTH / 2; k++)
            {
                src_data[1][j * src_linesize[1] + k] = (uint8_t)(j + i * 2);
                src_data[2][j * src_linesize[2] + k] = (uint8_t)(j + i * 5);
            }
        }
​
        // 编码一帧数据
        AVPacket pkt;
        av_init_packet(&pkt);
        pkt.data = NULL;
        pkt.size = 0;
        src_frame->pts = pts++;
        ret = avcodec_send_frame(pCodecCtx, src_frame);
        if (ret < 0)
        {
            fprintf(stderr, "Error sending a frame for encoding\n");
            exit(1);
        }
        while (ret >= 0)
        {
            ret = avcodec_receive_packet(pCodecCtx, &pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            {
                break;
            }
            if (ret < 0)
            {
                fprintf(stderr, "Error during encoding\n");
                exit(1);
            }
​
            // 写入帧数据
            pkt.stream_index = pStream->index;
            av_packet_rescale_ts(&pkt, pCodecCtx->time_base, pStream->time_base);
            ret = av_interleaved_write_frame(pFormatCtx, &pkt);
            if (ret < 0)
            {
                fprintf(stderr, "Error while writing video frame\n");
                exit(1);
            }
            av_packet_unref(&pkt);
        }
    }
​
    // 输入数据完成,写入尾部文件
    av_write_trailer(pFormatCtx);
​
    // 清理工作
    avcodec_close(pCodecCtx);
    avcodec_free_context(&pCodecCtx);
    av_frame_free(&src_frame);
    avio_closep(&pFormatCtx->pb);
    avformat_free_context(pFormatCtx);
    return 0;
}

该示例代码演示了如何使用FFmpeg对I帧、P帧、B帧进行编码,其中主要涉及到以下步骤:

  1. 查找编码器(H.264)
  2. 分配AVStream和AVCodecContext,并设置编码器参数
  3. 打开编码器并写入头文件
  4. 准备输入的数据
  5. 编码视频帧
  6. 写入帧数据
  7. 输入数据完成,写入尾部文件

在该示例代码中,通过avcodec_send_frame()和avcodec_receive_packet()函数完成了I帧、P帧、B帧的编码和输出。其中,通过pkt.flags判断了输出的帧类型是否为关键帧(I帧)。根据帧类型的不同,编码的方法也有所不同。I帧为关键帧,不依赖于其他帧;P帧为前向预测帧,依赖于前一关键帧或前一P帧;B帧为双向预测帧,依赖于前后的关键帧或P帧。因此,在编码视频帧时,需要对帧类型进行判断后再进行相应的编码,才能保证最终输出的视频流是高质量的。

小结

主要内容有 SystemUI车机音量控制注意事项、SystemUI车机音量控制的营销文章标题和H.264的帧编码流程。

在使用SystemUI实现车机音量控制时,需要注意权限、音量类型、音量范围、同步更新通知、提高适应性和双向同步等方面,并提供最佳用户体验。

SystemUI车机音量控制的营销文章标题可以搭配精美的图片和详细的介绍,吸引用户的眼球,让用户更愿意了解这个功能并尝试使用。

H.264的帧编码流程包括帧类型判定、运动估计、变换编码、量化和熵编码等步骤。通过I帧、P帧和B帧的编码,H.264能够准确预测和描述视频帧之间的差异,达到高效压缩和存储视频的目的。优化编码器的每一步操作,可以得到更好的视频质量和更高的压缩率。

更多有关音视频的学习资料可以参考《音视频开发从0到1精通手册》里面记录有几百个技术知识点,7个板块来帮助你快速进入音视频领域。

总结

通过 I 帧、P 帧和 B 帧的编码,H.264 能够准确预测和描述视频帧之间的差异,从而达到高效压缩和存储视频的目的。H.264 的编码器使用的技术非常复杂,其中每一步都有很多细节,可以根据实际应用场景对编码器进行调优和优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/499756.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2017年下半年软件设计师下午试题

试题四 阅读下列说明和C代码&#xff0c;回答问题1至问题 2&#xff0c;将解答写在答题纸的对应栏内。 【说明】 一个无向连通图G点上的哈密尔顿&#xff08;Hamiltion&#xff09;回路是指从图G上的某个顶点出发&#xff0c;经过图上所有其他顶点一次且仅一次&#xff0c;最后…

HNU-操作系统OS-实验Lab4

OS_Lab4_Experimental report 湖南大学信息科学与工程学院 计科 210X wolf &#xff08;学号 202108010XXX&#xff09; 实验目的 了解内核线程创建/执行的管理过程了解内核线程的切换和基本调度过程 实验内容 lab2/3完成了物理和虚拟内存管理&#xff0c;这给创建内核线程…

C++之初识STL—vector

文章目录 STL基本概念使用STL的好处容器vector1.vector容器简介2.vector对象的默认构造函数3.vector对象的带参构造函数4.vector的赋值5.vector的大小6.vector容器的访问方式7.vector的插入 STL基本概念 STL(Standard Template Library,标准模板库)STL 从广义上分为: 容器(con…

springboot+vue音乐翻唱与分享平台(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的音乐网站与分享平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;…

基于 FineReport 快速设计联动报表

一、基于 FineReport 快速设计联动报表 FineReport 一款用于报表制作&#xff0c;分析和展示的工具&#xff0c;可以轻松的构建出灵活的数据分析和报表系统。 下面基于 FineReport 自带的数据表 销量 表快速设计一页可视化联动报表&#xff0c;数据格式如下&#xff1a; 报表…

最新最全花1W买的Python+Selenium全栈Web自动化测试

前言&#xff1a;看这篇帖子&#xff0c;最好要在知道定位八大元素的基础之上才能够比较熟练的看完这篇帖子自动化测试是软件开发中非常重要的一环&#xff0c;它可以帮助开发人员提高代码质量、减少错误和缺陷&#xff0c;并节省测试时间。其中&#xff0c;Selenium是一个广泛…

01- 目标检测 (综述)

要点&#xff1a; 区分One-stage detector 和 Two-stage detector 参考链接&#xff1a;深度学习目标检测最全综述 - 爱码网 详细模型解读参考&#xff1a;目标检测简介 - 知乎 一 目标检测分类 1.1 发展历程 检测网络发布历程&#xff1a; 1.2 检测模型分类 2014年后目标…

7---整数反转

给你一个 32 位的有符号整数 x x x &#xff0c;返回将 x x x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [ − 2 31 , 2 31 − 1 ] [−2^{31}, 2^{31} − 1] [−231,231−1]&#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08…

2017年上半年软件设计师下午试题

试题四 阅读下列说明和 C 代码&#xff0c;回答问题 1 至问题 3&#xff0c;将解答写在答题纸的对应栏内。 【说明】 假币问题&#xff1a;有 n 枚硬币&#xff0c;其中有一枚是假币&#xff0c;己知假币的重量较轻。现只有一个天平&#xff0c;要求用尽量少的比较次数找出这枚…

【编程福音,25款必备AI编码工具推荐,让你事半功倍】

&#x1f680; AI破局先行者 &#x1f680; &#x1f332; AI工具、AI绘图、AI专栏 &#x1f340; &#x1f332; 如果你想学到最前沿、最火爆的技术&#xff0c;赶快加入吧✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;CSDN-Java领域优质创作者&#x1f3c6;&am…

代码随想录算法训练营第三十四天 | 加油站题、左右贪心

1005. K次取反后最大化的数组和 文档讲解&#xff1a;代码随想录 (programmercarl.com) 视频讲解&#xff1a;贪心算法&#xff0c;这不就是常识&#xff1f;还能叫贪心&#xff1f;LeetCode&#xff1a;1005.K次取反后最大化的数组和_哔哩哔哩_bilibili 状态&#xff1a;能做出…

Hadoop的安装与配置

Hadoop的安装与配置 推荐按照我的博客下载hadoop&#xff0c;spark&#xff0c;pyspark以及scala这样版本搭配更好。 如果觉得自己不会版本搭配可私聊博主。 scala的安装和配置&#xff1a;https://blog.csdn.net/weixin_41957626/article/details/130548174 spark的安装和配置…

宇树Unitree机器狗连接外网的一种方法

宇树官方提供的方式&#xff1a; ​​​​​​​ 宇树机器狗连接网络的相关说明 (yuque.com) 我这里提供一种我采用的方式&#xff0c;仅供参考&#xff1a; 需要一根手机数据线&#xff0c;能联网的手机 我这里使用的是小米手机&#xff0c;其余手机大概也能行。 如下&…

ElasticSearch学习随笔之高级检索

ElasticSearch 1、ElasticSearch学习随笔之基础介绍 2、ElasticSearch学习随笔之简单操作 3、ElasticSearch学习随笔之java api 操作 4、ElasticSearch学习随笔之SpringBoot Starter 操作 5、ElasticSearch学习随笔之嵌套操作 6、ElasticSearch学习随笔之分词算法 7、ElasticS…

强化学习_06_pytorch-TD3实践(BipedalWalkerHardcore-v3)

基于策略的离线算法TD3 1.1 简介 reference: openai-TD3 DDPG的critic会高估, 从而导致actor策略失败。TD3是增加了三个关键技巧优化DDPG。经过优化后的TD3(Twin Dalayed DDPG 双延迟深度确定性策略梯度算法)适合于具有高维连续动作空间的任务。 Tricks: Clipped Double Q-l…

github学习笔记

目录 github简介 唯一版本库是什么意思 hub的含义什么&#xff1f; github功能介绍 登录、注册 GitHub 术语解释 Git 初体验及其常用命令介绍 git中的两个分支是否有冲突是什么意思&#xff0c;这是因为什么导致的 利用 SSH 完成 Git 与 GitHub 的绑定 通过 Git 将代…

[ Azure 云计算从业者 | AZ-900 ] Chapter 06 | 认识与了解 Azure 中相关的计算服务

本章节主要内容进行讲解&#xff1a;计算服务中的虚拟机 VM、虚拟机规模集 VMSS、Azure 容器&#xff08;ACI&#xff09;、Azure Kubernetes Service (AKS) 与Azure Functions 本系列已经更新文章列表&#xff08;已更新&#xff09;&#xff1a; [ Azure 云计算从业者 | AZ…

Java 基础进阶篇(十一)—— 泛型与可变参数

文章目录 一、泛型概述二、泛型的定义2.1 泛型类2.2 泛型方法2.3 泛型接口 三、泛型深入3.1 泛型通配符3.2 泛型上下限3.3 案例&#xff1a;定义一个 “所有车量进行比赛” 的方法 四、可变参数 一、泛型概述 泛型是 JDK5 中引入的特性&#xff0c;可以在编译阶段约束操作的数…

从源码全面解析Java 线程池的来龙去脉

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;独角兽企业的Java开发工程师&#xff0c;CSDN博客专家&#xff0c;阿里云专家博主&#x1f4d5;系列专栏&#xff1a;Java设计模式、Spring源码系列、Netty源码系列、Kafka源码系列、JUC源码…

二叉树专题

⭐️前言⭐️ 本文主要总结一些常见的二叉树题目&#xff0c;希望读者能够通过这篇文章&#xff0c;来对二叉树有一个更深一步的了解。 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主将持续更新学习记录收获&…