04 ffmpeg摄像头数据H264编码


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);

完整工程:
http://download.csdn.net/detail/jklinux/9845982

参考:
http://blog.csdn.net/leixiaohua1020

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页