利用X264编码yuv格式视频帧,利用ffmpeg解码h264视频帧

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

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

  二、利用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 !\n");
                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!\n");
                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!\n");
                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!\n");
                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!\n");
                exit(EXIT_FAILURE);
        }
        if((ret = x264_picture_alloc(encoder->yuv420p_picture,encoder->colorspace,IMAGE_WIDTH,IMAGE_HEIGHT))<0){
                printf("ret=%d\n",ret);
                printf("x264_picture_alloc error!\n");
                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!\n");
                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!\n");
                exit(EXIT_FAILURE);
        }

        if((fd_write = open(write_filename,O_WRONLY | O_APPEND | O_CREAT,0777))<0){
                printf("cannot open output file!\n");
                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!\n");
                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!\n");
                        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!\n",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!\n");

	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)

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

    

live555搭建的rtsp服务器发送当前屏幕(x264)