转:关于视频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函数如下:
- static void
 - process_image (const char * p)
 - {
 - //fputc ('.', stdout);
 - //convert yuv422 to yuv420p
 - char *y=yuv420p;
 - char *u=&yuv420p[IMAGE_WIDTH*IMAGE_HEIGHT];
 - char *v=&yuv420p[IMAGE_WIDTH*IMAGE_HEIGHT+IMAGE_WIDTH*IMAGE_HEIGHT/4];
 - int i=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){//even line to sample U-Chriminance
 - if(l==1){//sample U-Chriminance
 - *u=p[j*IMAGE_WIDTH*2+i];
 - u++;
 - }
 - else if(l==3){//abandon V-Chroma
 - l=-1;
 - continue;
 - }
 - else{
 - *y=p[j*IMAGE_WIDTH*2+i];
 - ++y;
 - }
 - }
 - else if(j%2==1){//odd lines to sample V-Chroma
 - if(l==1){
 - continue;
 - }
 - else if(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文件。
程序如下:
- #include <stdint.h>
 - #include <x264.h>
 - #include <stdio.h>
 - #include <unistd.h>
 - #include <fcntl.h>
 - #include <stdlib.h>
 - #include <string.h>
 - #define DEBUG 0
 - #define CLEAR(x) (memset((&x),0,sizeof(x)))
 - #define IMAGE_WIDTH 320
 - #define IMAGE_HEIGHT 240
 - #define ENCODER_PRESET "veryfast"
 - #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);
 - }
 - 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_profile error!\n");
 - exit(EXIT_FAILURE);
 - }
 - #if DEBUG
 - printf("Line --------%d\n",__LINE__);
 - #endif
 - encoder->x264_encoder=x264_encoder_open(encoder->x264_parameter);
 - encoder->colorspace=ENCODER_COLORSPACE;
 - #if DEBUG
 - printf("Line --------%d\n",__LINE__);
 - #endif
 - 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;
 - #if DEBUG
 - printf("Line --------%d\n",__LINE__);
 - #endif
 - 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));
 - #if DEBUG
 - 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("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);
 - }
 - #if DEBUG
 - printf("Line --------%d\n",__LINE__);
 - #endif
 - int n_nal;
 - 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);
 - }
 - unsigned int length=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_up functions*/
 - //x264_picture_clean(encoder->yuv420p_picture);
 - //free(encoder->nal);//???? confused conflict with x264_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);
 - return 0;
 - }
 
3. 解码
利用ffmpeg进行解码
程序如下:
- #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"
 - #define IMAGE_WIDTH 320
 - #define IMAGE_HEIGHT 240
 - 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");
 - 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;
 - }
 
结果:
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版本
欢迎批评指正!
转:关于视频H264编解码的应用实现的更多相关文章
- iOS8系统H264视频硬件编解码说明
		
公司项目原因,接触了一下视频流H264的编解码知识,之前项目使用的是FFMpeg多媒体库,利用CPU做视频的编码和解码,俗称为软编软解.该方法比较通用,但是占用CPU资源,编解码效率不高.一般系统都会 ...
 - ffmpeg H264 编解码配置
		
ffmpeg H264编解码前面有文章介绍下,本文主要介绍一些参数配置. 编码: int InitEncoderCodec( int iWidth, int iHeight) { AVCodec * ...
 - H264 编解码协议
		
1.概述 H264是MPEG-4标准所定义的最新编码格式,同时也是技术含量最高.代表最新技术水平的视频编码格式之一,标准写法应该是H.264.H.264视频格式是经过有损压缩的,但是在技术上尽可能做到 ...
 - H264 编解码框架简单介绍
		
阅读完H264/AVC 编解码器的介绍,脑海中仅仅是留下下面三条: 1.H264并没有明白规定一个编解码器怎样实现,仅仅是规定了一个编码后的视频比特流的句法,和该比特流的解码方法,这个与MPEG 类似 ...
 - 让WebRTC支持H264编解码
		
近期实验了下怎样让WebRTC支持H264编码.记录下,供有须要的人參考. 说明一下,我是在 Ubuntu Server 14.04 下编译的 WebRTC ,使用 native(C++) api 开 ...
 - FFMpeg笔记(二) 使用FFmpeg对视频进行编解码的一般流程
		
1. 编码: 1.对编码资源的初始化 AVCodec* m_pVideoEncoder;// 特定编码器的参数信息 AVCodecContext* m_pVideoEncoderContext;// ...
 - 聊聊视频中的编解码器,你所不知道的h264、h265、vp8、vp9和av1编解码库
		
你知道FFmpeg吗?了解过h264/h265/vp8/vp9编解码库吗? 我们日常生活中使用最广泛的五种视频编码:H264(AVC).H265(HEVC).vp8.vp9.av1都分别是什么?由哪些 ...
 - ffmpeg编解码视频导致噪声增大的一种解决方法
		
一.前言 ffmpeg在视音频编解码领域算是一个比较成熟的解决方案了.公司的一款视频编辑软件正是基于ffmpeg做了二次封装,并在此基础上进行音视频的编解码处理.然而,在观察编码后的视频质量时,发现图 ...
 - 【并行计算与CUDA开发】英伟达硬件加速编解码
		
硬件加速 并行计算 OpenCL OpenCL API VS SDK 英伟达硬件编解码方案 基于 OpenCL 的 API 自己写一个编解码器 使用 SDK 中的编解码接口 使用编码器对于 OpenC ...
 
随机推荐
- jquery 应用小结
			
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
 - [ASP.NET]更简单的方法:FormsAuthentication登录ReturnUrl使用绝对路径
			
转自:http://www.cnblogs.com/dudu/p/formsauthentication-returnurl-absoluteuri.html [ASP.NET]更简单的方法:Form ...
 - 学习C++的一些问题总结
			
C++ 问题 (一) int main() { int i,j,m,n; i=8; j=10; m=++i+j++; //++i是先递加再使用,j++是先使用再递加,故:9+10=19 n=++i+ ...
 - 分布式服务框架 Zookeeper -- 管理分布式环境中的数据(转载)
			
本文转载自:http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/ Zookeeper 分布式服务框架是 Apache Had ...
 - OBD Experts OBD II Software OBD II Protocol Stack
			
http://www.obdexperts.co.uk/stack.html OBD II Software OBD Experts can provide you with ready to use ...
 - C++ Primer 学习笔记_46_STL实践与分析(20)--容器特有的算法
			
STL实践与分析 --容器特有的算法 与其它顺序容器所支持的操作相比,标准库为list容器定义了更精细的操作集合,使它不必仅仅依赖于泛型操作.当中非常大的一个原因就是list容器不是依照内存中的顺序进 ...
 - iframe式ajax调用示例
			
1.新建 a.html <!doctype html> <html> <head> <meta charset='utf-8'> <title&g ...
 - OS_TASK.C
			
/*************************************************************************************************** ...
 - 识货的拿走:Android游戏框架解读之总体结构
			
Android游戏开发的框架图无偿奉上.
 - 一天掌握Android JNI本地编程 快速入门
			
一.JNI(Java Native Interface) 1.什么是JNI: JNI(Java Native Interface):java本地开发接口 ...