从I帧到B帧,H.264编码技术为您构建画面与效果完美结合的视觉
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帧的编码流程如下:
- 图像分块:I 帧首先将原始图像分成若干个小块。
- DCT 变换:对于每个分块,将其进行离散余弦变换 (DCT),以转换为频域信号。
- 量化:对于每个分块,将 DCT 变换之后的频域系数进行量化。量化也是压缩图像的关键步骤之一。
- 熵编码:使用熵编码技术对量化后的数据进行编码,以进一步减少数据量。编码之后的结果就是 I 帧的输出。
P帧编码流程
P (Predicted picture) 帧是预测帧,它根据前一帧的参考像素进行编码。P 帧只编码与前一帧之间的像素差异,以从时间维度上减少视频数据的冗余。
P帧的编码流程如下:
- 运动估计:首先使用运动估计算法对前一帧和当前帧进行比较,获取两帧之间的运动信息。
- 帧间预测:利用得到的运动信息,在前一帧中对应的位置上获取像素值,从而对当前帧进行预测。
- 残差编码:将用预测帧预测出的图像和实际的图像进行比较,得到两个图像之间的差异。对这些差异进行编码并输出 P 帧。
B帧编码流程
B (Bidirectionally predictive picture) 帧是双向预测帧,该帧通过前后两个关键帧的像素值进行估计编码。B 帧同样只编码与前后帧之间的像素差异,以后缩小视频数据量。
B帧的编码流程如下:
- 运动估计:B 帧通常需要借助前一帧和后一帧进行运动估计。
- 帧间预测:参考前后两个关键帧,对当前帧进行帧间预测。
- 残差编码:与 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帧进行编码,其中主要涉及到以下步骤:
- 查找编码器(H.264)
- 分配AVStream和AVCodecContext,并设置编码器参数
- 打开编码器并写入头文件
- 准备输入的数据
- 编码视频帧
- 写入帧数据
- 输入数据完成,写入尾部文件
在该示例代码中,通过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 的编码器使用的技术非常复杂,其中每一步都有很多细节,可以根据实际应用场景对编码器进行调优和优化。