02-编码-YUV转H264

整体方案:

采集端:摄像头采集(YUV)->编码(YUV转H264)->RTMP推流

客户端:RTMP拉流->解码(H264转YUV)->YUV显示(SDL2)

YUV转H264是一个视频编码的过程,具体的代码实现过程如下:

cpp 复制代码
#include <math.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <libavutil/frame.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#ifdef __cplusplus
}
#endif

int framenum = 0;

int main(int argc, char **argv)
{
    AVFrame *frame;
    AVCodec *codec = NULL;
    AVPacket packet;
    AVCodecContext *codecContext;
    int readSize=0;
    int ret=0,getPacket;
    FILE * fileIn,*fileOut;
    int frameCount=0;
    /* register all the codecs */
    av_register_all();

    if(argc!=2){
        fprintf(stdout,"usage:./test input.yuv\n");
        return -1;
    }

    //1.我们需要读一帧一帧的数据,所以需要AVFrame结构
    //读出的一帧数据保存在AVFrame中。
    frame  = av_frame_alloc();
    frame->width = 640;
    frame->height = 360;
    //fprintf(stdout,"transf para width=%d,height=%d\n",frame->width,frame->height);
    frame->format = AV_PIX_FMT_YUV420P;
	//根据指定的宽,高,和像素格式申请图像内存,最后一个参数表示内存对齐的值,32位对齐4字节,导致352*288时候绿屏,尝试修改成16位对齐
	//修改成16位对齐之后
    av_image_alloc(frame->data,frame->linesize,frame->width,frame->height,frame->format,16);
    fileIn =fopen(argv[1],"r+");

    //2.读出来的数据保存在AVPacket中,因此,我们还需要AVPacket结构体
    //初始化packet
    av_init_packet(&packet);

    //3.读出来的数据,我们需要编码,因此需要编码器
    //下面的函数找到h.264类型的编码器
    /* find the mpeg1 video encoder */
	fprintf(stdout, "find encoder AV_CODEC_ID_H264\n");
    codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    //有了编码器,我们还需要编码器的上下文环境,用来控制编码的过程
    codecContext = avcodec_alloc_context3(codec);//分配AVCodecContext实例
    if (!codecContext)
    {
        fprintf(stderr, "Could not allocate video codec context\n");
        return -1;
    }
	//设置编码器参数控制编码
	/* put sample parameters */
    codecContext->bit_rate = 400000;		//参数为400000的时候,编码出来的busH264文件为254KB
    /* resolution must be a multiple of two */
    codecContext->width = 640;	
    codecContext->height = 360;
    /* frames per second */
    codecContext->time_base = (AVRational){1,25};
    /* emit one intra frame every ten frames
     * check frame pict_type before passing frame
     * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
     * then gop_size is ignored and the output of encoder
     * will always be I frame irrespective to gop_size
     */
    codecContext->gop_size = 10;			//每10帧发送一帧I帧
    /*
	* vcodec_encode_video2函数输出的延时仅仅跟max_b_frames的设置有关,  
	* 想进行实时编码,将max_b_frames设置为0便没有编码延时了
	*/
    codecContext->max_b_frames = 0;			//每两个非B帧之间最多一个B帧
    codecContext->pix_fmt = AV_PIX_FMT_YUV420P;  
	/** 
 	* ultrafast,superfast, veryfast, faster, fast, medium 
	* slow, slower, veryslow, placebo. 这是x264编码速度的选项 
	*/  
    //av_opt_set(codecContext->priv_data, "preset", "slow", 0);
	av_opt_set(codecContext->priv_data, "preset", "ultrafast", 0);
    //准备好了编码器和编码器上下文环境,现在可以打开编码器了
    fprintf(stdout, "open encoder AV_CODEC_ID_H264\n");
    if (avcodec_open2(codecContext, codec, NULL) < 0)      //根据编码器上下文打开编码器
    {
        fprintf(stderr, "Could not open codec\n");
        return -1;
    }

    //4.准备输出文件
    fileOut= fopen("out.h264","w+");
    //下面开始编码
    while(1){
        //读一帧数据出来
        readSize = fread(frame->data[0],1,frame->linesize[0]*frame->height,fileIn);
		fprintf(stdout,"fread data[0] frame->linesize[0] %d,frame->height %d,readSize %d\n",frame->linesize[0],frame->height,readSize);
        if(readSize == 0){
            fprintf(stdout,"end of file\n");
            frameCount++;
            break;
        }
        readSize = fread(frame->data[1],1,frame->linesize[1]*frame->height/2,fileIn);
		fprintf(stdout,"fread data[1] frame->linesize[1] %d,readSize %d \n",frame->linesize[1],readSize);
        readSize = fread(frame->data[2],1,frame->linesize[2]*frame->height/2,fileIn);
		fprintf(stdout,"fread data[2] frame->linesize[2] %d readSize %d \n",frame->linesize[2],readSize);
        //初始化packet
        av_init_packet(&packet);
        /* encode the image */
        frame->pts = frameCount;
		//将AVFrame中的像素信息编码为AVPacket中的码流,成功编码一个packet,将getPacket置位1
        ret = avcodec_encode_video2(codecContext, &packet, frame, &getPacket); 
        if (ret < 0) 
        {
            fprintf(stderr, "Error encoding frame\n");
            return -1;
        }
		fprintf(stdout,"had encode %d frame,and getPacket is %d\n",framenum,getPacket);	//read并encode了22帧,才开始获得一个完整编码帧
        if (getPacket) 
        {
            frameCount++;
            //获得一个完整的编码帧
            printf("Write frame %3d (size=%5d)\n", frameCount, packet.size);
            fwrite(packet.data, 1,packet.size, fileOut);
            av_packet_unref(&packet);
        }
    }
    /* flush buffer */
    for ( getPacket= 1; getPacket; frameCount++) 
    {
		fprintf(stdout,"get flush buffer getPacket %d,frameCount %d\n",getPacket,frameCount);
		fflush(stdout);
        frame->pts = frameCount;
        ret = avcodec_encode_video2(codecContext, &packet, NULL, &getPacket);
        if (ret < 0)
        {
            fprintf(stderr, "Error encoding frame\n");
            return -1;
        }

        if (getPacket) 
        {
            printf("Write frame %3d (size=%5d)\n", frameCount, packet.size);	//
            fwrite(packet.data, 1, packet.size, fileOut);
            av_packet_unref(&packet);
        }
    } 
    fclose(fileIn);
    fclose(fileOut);
    av_frame_free(&frame);
    avcodec_close(codecContext);
    av_free(codecContext);

    return 0;
}

整个工程目录:https://download.csdn.net/download/sishen4199/88522517?spm=1001.2014.3001.5503

YUV视频查看工具:https://download.csdn.net/download/sishen4199/88522520?spm=1001.2014.3001.5503

H264码流查看工具:https://download.csdn.net/download/sishen4199/88522519?spm=1001.2014.3001.5503

相关推荐
微帧Visionular1 天前
AI视频单帧清晰,播放时却闪烁,原因是什么?
人工智能·深度学习·音视频
辰辰观影1 天前
Windows网盘视频播放器推荐:无广告4K HDR怎么选
音视频
2601_966827451 天前
电视端云盘在线播放怎么选?支持4K HDR的应用推荐
音视频
开开心心就好1 天前
音频降噪剪辑工具,音量裁剪一键处理
运维·服务器·前端·spring·ocr·音视频·1024程序员节
AI视频工具指南1 天前
AI视频的声音和画面怎么配合?先分清谁在传递信息
人工智能·aigc·音视频
辰辰观影1 天前
无广告播放器推荐来啦
音视频
TrueDei1 天前
GStreamer 使用 valve 分别控制音视频流的暂停与恢复
音视频
一条破秋裤1 天前
基于 Hi3516CV610 的立定跳远视频测量系统调研与实施方案
arm开发·音视频
2601_966827451 天前
视频里的特效字幕怎么翻译?外挂字幕与AI字幕一键处理教程
音视频
八月的雨季 最後的冰吻1 天前
音视频面试--延迟优化
面试·职场和发展·音视频