zoukankan      html  css  js  c++  java
  • 使用X264编码yuv格式的视频帧使用ffmpeg解码h264视频帧

      前面一篇博客介绍在centos上搭建点击打开链接ffmpeg及x264开发环境。以下就来问个样例:

      1、利用x264库将YUV格式视频文件编码为h264格式视频文件

      2、利用ffmpeh库将h264格式的视频文件解码为yuv格式视频文件

      解码和编码前后对文件大小进行比較,如图:

       

     当中yuv420p.yuv为原始文件,大小77M

            encode.h264为H264编码后的视频文件,大小1.4M

           decode.yuv为ffmpeg解码后的视频文件,大小77M。

    从文件的大小非常明显能够看出h264压缩率。在Windows平台分辨播放了三个文件,画面看不出差别。

    以下是代码"

    /*File : yuvTO264.c
     *Auth : sjin
     *Date : 20141115
     *Mail : 413977243@qq.com
     */
    
    /*利用x264库将YUV文件编码为h264文件
     *
     */
    
    #include <stdint.h>
    #include <x264.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define CLEAR(x) (memset((&x),0,sizeof(x)))
    #define IMAGE_WIDTH   176
    #define IMAGE_HEIGHT  144
    #define ENCODER_PRESET "veryfast"
    
    /*配置參数
     * 使用默认參数,在这里使用了zerolatency的选项,使用这个选项之后,就不会有
     * delayed_frames,假设你使用不是这个的话,还须要在编码完毕之后得到缓存的
     * 编码帧
     */
    #define ENCODER_TUNE   "zerolatency"
    #define ENCODER_PROFILE  "baseline"
    #define ENCODER_COLORSPACE X264_CSP_I420
    
    typedef struct my_x264_encoder{
            x264_param_t  * x264_parameter;
            char parameter_preset[20];
            char parameter_tune[20];
            char parameter_profile[20];
            x264_t  * x264_encoder;
            x264_picture_t * yuv420p_picture;
            long colorspace;
            unsigned char *yuv;
            x264_nal_t * nal;
    } my_x264_encoder;
    
    char *read_filename="yuv420p.yuv";
    char *write_filename="encode.h264";
    
    int main(int argc ,char **argv)
    {
            int ret;
            int fd_read,fd_write;
            my_x264_encoder * encoder = (my_x264_encoder *)malloc(sizeof(my_x264_encoder));
            if(!encoder){
                    printf("cannot malloc my_x264_encoder !
    ");
                    exit(EXIT_FAILURE);
            }
            CLEAR(*encoder);
    
    
            /****************************************************************************
             * Advanced parameter handling functions
             ****************************************************************************/
    
            /* These functions expose the full power of x264's preset-tune-profile system for
             * easy adjustment of large numbers //free(encoder->yuv420p_picture);of internal parameters.
             *
             * In order to replicate x264CLI's option handling, these functions MUST be called
             * in the following order:
             * 1) x264_param_default_preset
             * 2) Custom user options (via param_parse or directly assigned variables)
             * 3) x264_param_apply_fastfirstpass
             * 4) x264_param_apply_profile
             *
             * Additionally, x264CLI does not apply step 3 if the preset chosen is "placebo"
             * or --slow-firstpass is set. */
            strcpy(encoder->parameter_preset,ENCODER_PRESET);
            strcpy(encoder->parameter_tune,ENCODER_TUNE);
    
            encoder->x264_parameter = (x264_param_t *)malloc(sizeof(x264_param_t));
            if(!encoder->x264_parameter){
                    printf("malloc x264_parameter error!
    ");
                    exit(EXIT_FAILURE);
            }
    
        /*初始化编码器*/
            CLEAR(*(encoder->x264_parameter));
            x264_param_default(encoder->x264_parameter);
    
            if((ret = x264_param_default_preset(encoder->x264_parameter,encoder->parameter_preset,encoder->parameter_tune))<0){
                    printf("x264_param_default_preset error!
    ");
                    exit(EXIT_FAILURE);
            }
    
        /*cpuFlags 去空缓冲区继续使用不死锁保证*/
            encoder->x264_parameter->i_threads               =X264_SYNC_LOOKAHEAD_AUTO;
        /*视频选项*/
            encoder->x264_parameter->i_width                 =IMAGE_WIDTH;//要编码的图像的宽度
            encoder->x264_parameter->i_height                =IMAGE_HEIGHT;//要编码的图像的高度
            encoder->x264_parameter->i_frame_total   =0;//要编码的总帧数,不知道用0
            encoder->x264_parameter->i_keyint_max    =25;
        /*流參数*/
            encoder->x264_parameter->i_bframe        =5;
            encoder->x264_parameter->b_open_gop      =0;
            encoder->x264_parameter->i_bframe_pyramid=0;
            encoder->x264_parameter->i_bframe_adaptive=X264_B_ADAPT_TRELLIS;
    
        /*log參数,不须要打印编码信息时直接凝视掉*/
            encoder->x264_parameter->i_log_level    =X264_LOG_DEBUG;
    
            encoder->x264_parameter->i_fps_den               =1;//码率分母
            encoder->x264_parameter->i_fps_num               =25;//码率分子
            encoder->x264_parameter->b_intra_refresh =1;
            encoder->x264_parameter->b_annexb                =1;
    
            strcpy(encoder->parameter_profile,ENCODER_PROFILE);
            if((ret=x264_param_apply_profile(encoder->x264_parameter,encoder->parameter_profile))<0){
                    printf("x264_param_apply_profile error!
    ");
                    exit(EXIT_FAILURE);
            }
        /*打开编码器*/
            encoder->x264_encoder = x264_encoder_open(encoder->x264_parameter);
            encoder->colorspace = ENCODER_COLORSPACE;
    
        /*初始化pic*/
            encoder->yuv420p_picture = (x264_picture_t *)malloc(sizeof(x264_picture_t ));
            if(!encoder->yuv420p_picture){
                    printf("malloc encoder->yuv420p_picture error!
    ");
                    exit(EXIT_FAILURE);
            }
            if((ret = x264_picture_alloc(encoder->yuv420p_picture,encoder->colorspace,IMAGE_WIDTH,IMAGE_HEIGHT))<0){
                    printf("ret=%d
    ",ret);
                    printf("x264_picture_alloc error!
    ");
                    exit(EXIT_FAILURE);
            }
    
            encoder->yuv420p_picture->img.i_csp = encoder->colorspace;
            encoder->yuv420p_picture->img.i_plane = 3;
            encoder->yuv420p_picture->i_type = X264_TYPE_AUTO;
    
        /*申请YUV buffer*/
            encoder->yuv = (uint8_t *)malloc(IMAGE_WIDTH*IMAGE_HEIGHT*3/2);
            if(!encoder->yuv){
                    printf("malloc yuv error!
    ");
                    exit(EXIT_FAILURE);
            }
            CLEAR(*(encoder->yuv));
            encoder->yuv420p_picture->img.plane[0] = encoder->yuv;
            encoder->yuv420p_picture->img.plane[1] = encoder->yuv+IMAGE_WIDTH*IMAGE_HEIGHT;
            encoder->yuv420p_picture->img.plane[2] = encoder->yuv+IMAGE_WIDTH*IMAGE_HEIGHT+IMAGE_WIDTH*IMAGE_HEIGHT/4;
    
            if((fd_read = open(read_filename,O_RDONLY))<0){
                    printf("cannot open input file!
    ");
                    exit(EXIT_FAILURE);
            }
    
            if((fd_write = open(write_filename,O_WRONLY | O_APPEND | O_CREAT,0777))<0){
                    printf("cannot open output file!
    ");
                    exit(EXIT_FAILURE);
            }
    
            int n_nal = 0;
            x264_picture_t pic_out;
            x264_nal_t *my_nal;
            encoder->nal = (x264_nal_t *)malloc(sizeof(x264_nal_t ));
            if(!encoder->nal){
                    printf("malloc x264_nal_t error!
    ");
                    exit(EXIT_FAILURE);
            }
            CLEAR(*(encoder->nal));
    
        /*编码*/
            while(read(fd_read,encoder->yuv,IMAGE_WIDTH*IMAGE_HEIGHT*3/2)>0){
                    encoder->yuv420p_picture->i_pts++;
                    if((ret = x264_encoder_encode(encoder->x264_encoder,&encoder->nal,&n_nal,encoder->yuv420p_picture,&pic_out))<0){
                            printf("x264_encoder_encode error!
    ");
                            exit(EXIT_FAILURE);
                    }
    
                    for(my_nal = encoder->nal; my_nal<encoder->nal+n_nal; ++my_nal){
                            write(fd_write,my_nal->p_payload,my_nal->i_payload);
                    }
            }
    
            free(encoder->yuv);
            free(encoder->yuv420p_picture);
            free(encoder->x264_parameter);
            x264_encoder_close(encoder->x264_encoder);
            free(encoder);
            close(fd_read);
            close(fd_write);
    
            return 0;
    }
    

       
    /*File : decode_h264.c
     *Auth : sjin
     *Date : 20141115
     *Mail : 413977243@qq.com
     */
    
    /*将h264解码为yuv文件*/
    
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavutil/mathematics.h>
    
    #define DECODED_OUTPUT_FORMAT  AV_PIX_FMT_YUV420P
    #define INPUT_FILE_NAME "encode.h264"
    #define OUTPUT_FILE_NAME "decode.yuv"
    /*h264文件的宽度和高度,必须和实际的宽度和高度一致
     *否则将出错
     * */
    #define IMAGE_WIDTH  176
    #define IMAGE_HEIGHT 144
    
    void error_handle(const char *errorInfo )
    {
    	printf("%s error!
    ",errorInfo);
    	exit(EXIT_FAILURE);
    }
    
    
    int main(int argc,char ** argv)
    {
    	int  write_fd,ret,videoStream;
    	AVFormatContext * formatContext=NULL;
    	AVCodec * codec;
    	AVCodecContext * codecContext;
    	AVFrame * decodedFrame;
    	AVPacket packet;
    	uint8_t *decodedBuffer;
    	unsigned int decodedBufferSize;
    	int finishedFrame;
    
        //初始化环境
    	av_register_all();
    
    	write_fd = open(OUTPUT_FILE_NAME,O_RDWR | O_CREAT,0666);
    	if(write_fd<0){
    		perror("open");
    		exit(1);
    	}
    
    	ret = avformat_open_input(&formatContext, INPUT_FILE_NAME, NULL,NULL);
    	if(ret<0)
    		error_handle("avformat_open_input error");
    
    	ret = avformat_find_stream_info(formatContext,NULL);
    	if(ret<0)
    		error_handle("av_find_stream_info");
    
        //打印输入文件的具体信息
        av_dump_format(formatContext,0,INPUT_FILE_NAME,0);
    
    
    	videoStream = 0;
    	codecContext = formatContext->streams[videoStream]->codec;
    
    	codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    	if(codec == NULL)
    		error_handle("avcodec_find_decoder error!
    ");
    
    	ret = avcodec_open2(codecContext,codec,NULL);
    	if(ret<0)
    		error_handle("avcodec_open2");
    
        //分配保存视频帧的空间
    	decodedFrame = avcodec_alloc_frame();
    	if(!decodedFrame)
    		error_handle("avcodec_alloc_frame!");
    
        //分配解码后视频帧的空间
    	decodedBufferSize = avpicture_get_size(DECODED_OUTPUT_FORMAT,IMAGE_WIDTH,IMAGE_HEIGHT);
    	decodedBuffer = (uint8_t *)malloc(decodedBufferSize);
    	if(!decodedBuffer)
    		error_handle("malloc decodedBuffer error!");
    
    	av_init_packet(&packet);
    	while(av_read_frame(formatContext,&packet)>=0){
    			ret = avcodec_decode_video2(codecContext,decodedFrame,&finishedFrame,&packet);
    			if(ret<0)
    				error_handle("avcodec_decode_video2 error!");
    			if(finishedFrame){
    				avpicture_layout((AVPicture*)decodedFrame,DECODED_OUTPUT_FORMAT,IMAGE_WIDTH,IMAGE_HEIGHT,decodedBuffer,decodedBufferSize);
    				ret = write(write_fd,decodedBuffer,decodedBufferSize);
    				if(ret<0)
    					error_handle("write yuv stream error!");
    			}
    
    		av_free_packet(&packet);
    	}
    
        /*防止视频解码完毕后丢帧的情况*/
    	while(1){
    		packet.data = NULL;
    		packet.size = 0;
    		ret = avcodec_decode_video2(codecContext,decodedFrame,&finishedFrame,&packet);
    		if(ret<=0 && (finishedFrame<=0))
    			break;
    		if(finishedFrame){
    			avpicture_layout((AVPicture*)decodedFrame,DECODED_OUTPUT_FORMAT,IMAGE_WIDTH,IMAGE_HEIGHT,decodedBuffer,decodedBufferSize);
    			ret = write(write_fd,decodedBuffer,decodedBufferSize);
    			if(ret<0)
    				error_handle("write yuv stream error!");
    		}
    
    		av_free_packet(&packet);
    	}
    
    
    	avformat_close_input(&formatContext);
    	free(decodedBuffer);
    	av_free(decodedFrame);
    	avcodec_close(codecContext);
    
    	return 0;
    }
    
    Makefile:

    # use pkg-config for getting CFLAGS and LDLIBS
    FFMPEG_LIBS=    libavdevice                        
                    libavformat                        
                    libavfilter                        
                    libavcodec                         
                    libswresample                      
                    libswscale                         
                    libavutil                          
    
    CFLAGS += -Wall -O2 -g
    CFLAGS := $(shell pkg-config --cflags $(FFMPEG_LIBS)) $(CFLAGS)
    LDLIBS := $(shell pkg-config --libs $(FFMPEG_LIBS)) $(LDLIBS)
    
    EXAMPLES= decode_h264 yuvTO264
                    						
    OBJS=$(addsuffix .o,$(EXAMPLES))
    
    # the following examples make explicit use of the math library
    LDLIBS += -lx264 -m32 -pthread -lm -ldl
    
    .phony:all clean
    
    all: $(OBJS) $(EXAMPLES)
    
    clean:
    	rm $(EXAMPLES) $(OBJS)
    

    參考资料:
    參考资料
    1、http://blog.csdn.net/liushu1231/article/details/9203239
    2、http://www.cnblogs.com/fojian/archive/2012/09/01/2666627.html
    3、http://stackoverflow.com/questions/2940671/how-does-one-encode-a-series-of-images-into-h264-using-the-x264-c-api
    4、http://blog.yikuyiku.com/?p=3486

        

    live555搭建的rtspserver发送当前屏幕(x264)


  • 相关阅读:
    JVM 垃圾回收器工作原理及使用实例介绍(转载自IBM),直接复制粘贴,需要原文戳链接
    装tomcat和nginx心得
    jms的俩种模式
    裸奔Spring(1)
    一个最小mybatis
    SpringBoot和数据库连接
    SpringBoot的基础Pom
    SpringBoot读取配置文件
    埃拉托斯特尼素数筛法
    hdu 1175 连连看
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/4557007.html
Copyright © 2011-2022 走看看