h264编码
http://blog.csdn.net/scalerzhangjie/article/details/8273410
项目要用到视频编解码,最近半个月都在搞,说实话真是走了很多弯路,浪费了很多时间。将自己的最终成果记录于此,期望会给其他人提供些许帮助。
参考教程:
http://ffmpeg.org/trac/ffmpeg/wiki/ubuntuCompilationGuide安装ffmpeg和x264,官方权威教程(注意不要用命令行安装,会少很多库的。编译安装最保险)
http://blog.csdn.net/zgyulongfei/article/details/7526249采集与编码的教程
http://www.cnblogs.com/fojian/archive/2012/09/01/2666627.html编码的好文章
http://my.oschina.net/u/555701/blog/56616?p=2#comments-解码的好文章
整体过程流程如下:
显而易见,整个过程分为三个部分:采集、编码、解码。
1.采集视频
我是利用USB摄像头采集视频的,我的摄像头只支持YUV422格式的图像采集,因为x264编码库只能编码YUV420P(planar)格式,因此在采集到yuv422格式的图像数据后要变换成yuv420p格式。
采集视频使用官方的那个采集程序,稍加修改即可,具体点说就是修改
static void process_image (const char * p) ;函数
参数p指向一帧采集图像的yuv数据。
关于YUV格式和RGB格式,网上有很多教程。
在这儿,我讲一下自己的理解。
假设有一幅4*4分辨率的图片,如下:
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
每个像素是由YUV数据构成,假设如下:
Y1 |
U1 |
V1 |
Y2 |
U2 |
V2 |
Y3 |
U3 |
V3 |
Y4 |
U4 |
V4 |
Y5 |
U5 |
V5 |
Y6 |
U6 |
V6 |
Y7 |
U7 |
V7 |
Y8 |
U8 |
V8 |
Y9 |
U9 |
V9 |
Y10 |
U10 |
V10 |
Y11 |
U11 |
V11 |
Y12 |
U12 |
V12 |
Y13 |
U13 |
V13 |
Y14 |
U14 |
V14 |
Y15 |
U15 |
V15 |
Y16 |
U16 |
V16 |
YUV422图像是这样的,每个像素采集Y,UV每隔两个像素采集一次:
Packed格式的YUV420是这样的,每个像素采集Y,UV隔行采集,每行是每两个像素采集一次:
以上几种格式存储就是按照从左到右,从上到下顺序存储的。
我想要得到是planar格式的YUV420,即在一段连续的内存中,先存储所有的Y,接着是所有的U,最后是所有的V。
修改后的process_image函数如下:
[cpp]view plaincopy
- staticvoid
- process_image(constchar*p)
- {
- //fputc('.',stdout);
- //convertyuv422toyuv420p
- char*y=yuv420p;
- char*u=&yuv420p[IMAGE_WIDTH*IMAGE_HEIGHT];
- char*v=&yuv420p[IMAGE_WIDTH*IMAGE_HEIGHT+IMAGE_WIDTH*IMAGE_HEIGHT/4];
- inti=0,j=0,l=0;
- for(j=0;j<IMAGE_HEIGHT;j++)
- for(i=0;i<IMAGE_WIDTH*2;i++,l++){
- if(j%2==0){//evenlinetosampleU-Chriminance
- if(l==1){//sampleU-Chriminance
- *u=p[j*IMAGE_WIDTH*2+i];
- u++;
- }
- elseif(l==3){//abandonV-Chroma
- l=-1;
- continue;
- }
- else{
- *y=p[j*IMAGE_WIDTH*2+i];
- ++y;
- }
- }
- elseif(j%2==1){//oddlinestosampleV-Chroma
- if(l==1){
- continue;
- }
- elseif(l==3){
- l=-1;
- *v=p[j*IMAGE_WIDTH*2+i];
- ++v;
- }
- else{
- *y=p[j*IMAGE_WIDTH*2+i];
- ++y;
- }
- }
- }
- fwrite(yuv420p,IMAGE_WIDTH*IMAGE_HEIGHT*3>>1,1,fp_yuv420p);
- fflush(stdout);
- }
2.编码
采用x264编码库编码yuv420p文件。
程序如下:
[cpp]view plaincopy
- #include<stdint.h>
- #include<x264.h>
- #include<stdio.h>
- #include<unistd.h>
- #include<fcntl.h>
- #include<stdlib.h>
- #include<string.h>
- #definedEBUG0
- #defineCLEAR(x)(memset((&x),0,sizeof(x)))
- #defineIMAGE_WIDTH320
- #defineIMAGE_HEIGHT240
- #defineENCODER_PRESET"veryfast"
- #defineENCODER_TUNE"zerolatency"
- #defineENCODER_profile"baseline"
- #defineENCODER_colorSPACEX264_CSP_I420
- typedefstructmy_x264_encoder{
- x264_param_t*x264_parameter;
- charparameter_preset[20];
- charparameter_tune[20];
- charparameter_profile[20];
- x264_t*x264_encoder;
- x264_picture_t*yuv420p_picture;
- longcolorspace;
- unsignedchar*yuv;
- x264_nal_t*nal;
- }my_x264_encoder;
- char*read_filename="yuv420p.yuv";
- char*write_filename="encode.h264";
- int
- main(intargc,char**argv){
- intret;
- intfd_read,fd_write;
- my_x264_encoder*encoder=(my_x264_encoder*)malloc(sizeof(my_x264_encoder));
- if(!encoder){
- printf("cannotmallocmy_x264_encoder!\n");
- exit(EXIT_FAILURE);
- }
- CLEAR(*encoder);
- /****************************************************************************
- *Advancedparameterhandlingfunctions
- ****************************************************************************/
- /*Thesefunctionsexposethefullpowerofx264'spreset-tune-profilesystemfor
- *easyadjustmentoflargenumbers//free(encoder->yuv420p_picture);ofinternalparameters.
- *
- *Inordertoreplicatex264CLI'soptionhandling,thesefunctionsMUSTbecalled
- *inthefollowingorder:
- *1)x264_param_default_preset
- *2)Customuseroptions(viaparam_parseordirectlyassignedvariables)
- *3)x264_param_APPly_fastfirstpass
- *4)x264_param_apply_profile
- *
- *Additionally,x264CLIdoesnotapplystep3ifthepresetchosenis"placebo"
- *or--slow-firstpassisset.*/
- 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("mallocx264_parameterERROR!\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_preseterror!\n");
- exit(EXIT_FAILURE);
- }
- encoder->x264_parameter->i_fps_den=1;
- encoder->x264_parameter->i_fps_num=25;
- encoder->x264_parameter->i_width=IMAGE_WIDTH;
- encoder->x264_parameter->i_height=IMAGE_HEIGHT;
- encoder->x264_parameter->i_threads=1;
- encoder->x264_parameter->i_keyint_max=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_profileerror!\n");
- exit(EXIT_FAILURE);
- }
- #ifDEBUG
- printf("Line--------%d\n",__LINE__);
- #endif
- encoder->x264_encoder=x264_encoder_open(encoder->x264_parameter);
- encoder->colorspace=ENCODER_COLORSPACE;
- #ifDEBUG
- printf("Line--------%d\n",__LINE__);
- #endif
- encoder->yuv420p_picture=(x264_picture_t*)malloc(sizeof(x264_picture_t));
- if(!encoder->yuv420p_picture){
- printf("mallocencoder->yuv420p_pictureerror!\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_allocerror!\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;
- #ifDEBUG
- printf("Line--------%d\n",__LINE__);
- #endif
- encoder->yuv=(uint8_t*)malloc(IMAGE_WIDTH*IMAGE_HEIGHT*3/2);
- if(!encoder->yuv){
- printf("mallocyuverror!\n");
- exit(EXIT_FAILURE);
- }
- CLEAR(*(encoder->yuv));
- #ifDEBUG
- printf("Line--------%d\n",__LINE__);
- #endif
- 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("cannotopeninputfile!\n");
- exit(EXIT_FAILURE);
- }
- if((fd_write=open(write_filename,O_WRONLY|O_append|O_CREAT,0777))<0){
- printf("cannotopenoutputfile!\n");
- exit(EXIT_FAILURE);
- }
- #ifDEBUG
- printf("Line--------%d\n",__LINE__);
- #endif
- intn_nal;
- x264_picture_tpic_out;
- x264_nal_t*my_nal;
- encoder->nal=(x264_nal_t*)malloc(sizeof(x264_nal_t));
- if(!encoder->nal){
- printf("mallocx264_nal_terror!\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_encodeerror!\n");
- exit(EXIT_FAILURE);
- }
- unsignedintlength=0;
- for(my_nal=encoder->nal;my_nal<encoder->nal+n_nal;++my_nal){
- write(fd_write,my_nal->p_payload,my_nal->i_payload);
- length+=my_nal->i_payload;
- }
- printf("length=%d\n",length);
- }
- /*clean_upfunctions*/
- //x264_picture_clean(encoder->yuv420p_picture);
- //free(encoder->nal);//????confusedconflictwithx264_encoder_close(encoder->x264_encoder);
- 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);
- return0;
- }
3.解码
利用ffmpeg进行解码
程序如下:
[cpp]view plaincopy
- #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>
- #defineDECODED_OUTPUT_FORMATAV_PIX_FMT_YUV420P
- #defineINPUT_FILE_NAME"encode.h264"
- #defineOUTPUT_FILE_NAME"decode.yuv"
- #defineIMAGE_WIDTH320
- #defineIMAGE_HEIGHT240
- void
- error_handle(constchar*errorInfo){
- printf("%serror!\n",errorInfo);
- exit(EXIT_FAILURE);
- }
- int
- main(intargc,char**argv){
- intwrite_fd,ret,videoStream;
- AVFormatcontext*formatContext=NULL;
- AVCodec*codec;
- AVCodecContext*codecContext;
- AVFrame*decodedFrame;
- AVPacketpacket;
- uint8_t*decodedBuffer;
- unsignedintdecodedBufferSize;
- intfinishedFrame;
- 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_inputerror");
- ret=avformat_find_stream_info(formatContext,NULL);
- if(ret<0)
- error_handle("av_find_stream_info");
- videoStream=0;
- codecContext=formatContext->streams[videoStream]->codec;
- codec=avcodec_find_decoder(AV_CODEC_ID_H264);
- if(codec==NULL)
- error_handle("avcodec_find_decodererror!\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("mallocdecodedBuffererror!");
- 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_video2error!");
- 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("writeyuvstreamerror!");
- }
- 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("writeyuvstreamerror!");
- }
- av_free_packet(&packet);
- }
- avformat_close_input(&formatContext);
- free(decodedBuffer);
- av_free(decodedFrame);
- avcodec_close(codecContext);
- return0;
- }
结果:
1.利用USB摄像头采集的YUV420P,大小11.0MB,可以用pyuv播放器正常播放。
2.编码后的文件encode.h264,大小262.4kb,可用vlc播放器正常播放。
3.解码后的文件decode.yuv,大小11.0MB,可以用pyuv播放器正常播放。
相关文件在我的资源里,里面包含:
1.采集、编码、解码程序、对应的可执行程序和makefile文件;
2.Pyuv播放器(用于XP)
3. 实验文件-yuv420p.yuv 、encode.h264、 decode.yuv
4. 相关参考文档pdf版本
欢迎批评指正!
相关阅读
【人人早报】568期:多亏李世石的不懈努力,AlphaGo 的命
导读香港科技大学计算机科学硕士,中国科学技术大学科技与战略风云学会研究员,新浪围棋 6D。本文首发于“观察者”,原标题为“ 陈经:
python的方法重载有些特殊,在python中,如下的代码并不能实现方法重载: def Aa(): pass def Aa(a): print(1) def
我最近发现一个问题,在浏览我经常浏览的一些网站时,浏览器总是提示http 404错误,无法找到文件。起初,以为是该网站在进行日常维护,打开
What is CRA? 什么是CRA? 众所周知,sensor的效能与sensor
sprintf( s, " threads=%d", p->i_threads );最大值128 X264_THREAD_MAX。如果没有设置threads参数,x264根据当前CPU核数以及slice