yuyv(yuv422): 一行里的两个像素结合, 一个像素有(y, u), 另一个像素有(y, v). 还原rgb时两像素的数据需结合来使用.
yuv420: 每两行的上下两个像素结合, 一行的两个像素只存(y, u, y), 下一行的两个像素只存(y, v, y).
/////////////////////////////////////
yuv444:
y00u00v00 y01u01v01 y02u02v02 y03u03v03 y04u04v04 y05u05v05 y06u06v06 y07u07v07...
y56u56v56 y57u57v57 y58u58v58 y59u59v59 y60u60v60 y61u61v61 y62u62v62 y63u63v63
yuv444->yuv420:
y00u00 y01 y02u02 y03 | y04u04 y05 y06u06 y07
y56v56 y57 y58v58 y59 | y60v60 y61 y62v62 y63
yuv420->yuv444:
y00u00v56 y01u00v56 y02u02v58 y03u02v58 ....
y56u00v56 y57u00v56 y58u02v58 y59u02v58 ...
像素数通常分成:打包(packed)格式和平面(planar)格式
packed: 就是一个像素的YUV数据存放完后接着存放另一个像素的YUV数据
上面的yuv数据就是打包的.
planar: 就是每个像素的yuv数据分成三个地方存放, y专存一个地方, u.., v...
// 三个地方可以是三个不同的数组, 也可以是同一数组里的不同的位置
"yuv420p" 后面的p表示planar
////////////////////////////////////////////
yuv422 --> yuv420p
yuv422:
y00u00 y01v01 y02u02 y03v03 y04u04 y05v05 y06u06 y07v07 ...
y56u56 y57v57 y58u58 y59v59 y60u60 y61v61 y62u62 y63v63 ...
yuv420:
y00u00 y01 y02u02 y03 | y04u04 y05 y06u06 y07
y56v57 y57 y58v59 y59 | y60v61 y61 y62v63 y63
yuv420p:
y00 y01 y02 y03 y04 y05 y06 y07 ... y56 y57 y58 y59 y60 y61 y62 y63
u00 u02 u04 u06 ...
v57 v59 v61 v63 ...
////////////////////////////////
转换函数:
int yuv422_2_yuv420p(unsigned char *yuv422, unsigned char *y, unsigned char *u, unsigned char *v, int w, int h)
{
int i, j;
unsigned char *yuv = yuv422;
for (i = 0; i < h; i += 2)
{
for (j = 0; j < w; j += 2) // 第一行: y u y
{
*y++ = yuv[0]; // y0
*u++ = yuv[1]; // u0
*y++ = yuv[2]; // y1
//yuv[3]; // v1
yuv += 4;
}
for (j = 0; j < w; j += 2) // 第二行: y v y
{
*y++ = yuv[0]; //y0
// yuv[1] ; // u0
*y++ = yuv[2]; // y1
*v++ = yuv[3]; // v1
yuv += 4;
}
}
return (w*h*3)>>1; //yuv420里每个像素占用1.5字节
}
//////////////yuv420p数据编码过程///////
ffmpeg编码库只支持yuv420p的数据编码成视频格式
#define __STDC_CONSTANT_MACROS
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
};
//初始化编解码,文件格式等库
1. av_register_all();
2. //找到想用的编码器
AVCodec *pCodec = avcodec_find_encoder(AV_CODEC_ID_H264); //AV_CODEC_ID_H265
3. //分配一个描述编码器的配置信息对象
AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec);
//初始化编码的配置
pCodecCtx->bit_rate = 400000; //400K比特传输率/秒
pCodecCtx->width = 图像的宽度;
pCodecCtx->height = 图像的高度;
pCodecCtx->time_base.num=1;
pCodecCtx->time_base.den=25; // 1秒钟25帧图像
pCodecCtx->gop_size = 10; //多少帧图像里有一张关键帧
pCodecCtx->max_b_frames = 1;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; //数据的格式
4. //根据指定的参数,打开编码器
avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
5. //准备一帧要编码前的图像数据的空间
AVFrame *pFrame = av_frame_alloc();
pFrame->format = pCodecCtx->pix_fmt;
pFrame->width = pCodecCtx->width;
pFrame->height = pCodecCtx->height;
//再准备好存放yuv420p的数据缓冲区
av_image_alloc(pFrame->data, pFrame->linesize, pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, 16);
注意,yuv420p是平面 pFrame->data[0]装y数据 , pFrame->data[1]装u数据, pFrame->data[2]装v数据
6. //编码:
AVPacket pkt ; //用于存放编码后的数据
for (;;)
{
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
//把yuv420p的数据放入pFrame->data[]数组里
pFrame->pts = i; //记录当前是第几帧的数据
ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_output); //编码
if (got_output) //一帧图像已编码完成, 需要输出保存
{
pkt.data里存放已编码好的数据, pkt.size记录编码的数据大小
pkt.flags == AV_PKT_FLAG_KEY //用于判断当前编码出来的是否为关键帧
av_packet_unref(&pkt);
}
}
7. //编码退出前需把编码器里还未完成一帧图像的数据进行处理
for (got_output = 1; got_output; i++) {
ret = avcodec_encode_video2(pCodecCtx, &pkt, NULL, &got_output);
if (got_output) {
pkt.data里存放已编码好的数据, pkt.size记录编码的数据大小
pkt.flags == AV_PKT_FLAG_KEY //用于判断当前编码出来的是否为关键帧
av_packet_unref(&pkt);
}
}
8.// 退出编码的处理
avcodec_close(pCodecCtx);
av_free(pCodecCtx);
av_freep(&pFrame->data[0]);
av_frame_free(&pFrame);
04 ffmpeg摄像头数据H264编码
最新推荐文章于 2024-08-28 16:56:59 发布
本文详细介绍了YUV格式的各种类型,如YUV422、YUV420及YUV444,并提供了从YUV422到YUV420P的转换函数示例。此外还介绍了如何利用FFmpeg库将YUV420P格式的图像数据编码为视频。
摘要由CSDN通过智能技术生成