视频编解码---x264用于编码,ffmpeg用于解码
项目要用到视频编解码,最近半个月都在搞,说实话真是走了很多弯路,浪费了很多时间。将自己的最终成果记录于此,期望会给其他人提供些许帮助。
参考教程:
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版本
欢迎批评指正!
转自:http://blog.csdn.net/scalerzhangjie/article/details/8273410
视频编解码---x264用于编码,ffmpeg用于解码的更多相关文章
- 集显也能硬件编码:Intel SDK && 各种音视频编解码学习详解
http://blog.sina.com.cn/s/blog_4155bb1d0100soq9.html INTEL MEDIA SDK是INTEL推出的基于其内建显示核心的编解码技术,我们在播放高清 ...
- 【FFMPEG】各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式
目录(?)[-] 编解码学习笔记二codec类型 编解码学习笔记三Mpeg系列Mpeg 1和Mpeg 2 编解码学习笔记四Mpeg系列Mpeg 4 编解码学习笔记五Mpeg系列AAC音频 编解码学习笔 ...
- 视频编解码的理论和实践2:Ffmpeg视频编解码
近几年,视频编解码技术在理论及应用方面都取得了重大的进展,越来越多的人想要了解编解码技术.因此,网易云信研发工程师为大家进行了归纳梳理,从理论及实践两个方面简单介绍视频编解码技术. 相关阅读推荐 &l ...
- FFmpeg音视频编解码实践总结
PS:由于目前开发RTSP服务器传输模块时用到了h264文件,所以攻了一段时间去实现h264的视频编解码,借用FFmpeg SDK实现了任意文件格式之间的转换,并实现了流媒体实时播放,目前音视频同步需 ...
- 【视频编解码·学习笔记】8. 熵编码算法:基本算法列举 & 指数哥伦布编码
一.H.264中的熵编码基本方法: 熵编码具有消除数据之间统计冗余的功能,在编码端作为最后一道工序,将语法元素写入输出码流 熵解码作为解码过程的第一步,将码流解析出语法元素供后续步骤重建图像使用 在H ...
- WebUtility(提供在处理 Web 请求时用于编码和解码 URL 的方法。)
public static string UrlEncode( string str ) UrlEncode(String) 方法可用来编码整个 URL,包括查询字符串值. 如果没有编码情况下,如空格 ...
- 【miscellaneous】各种音视频编解码学习详解
编解码学习笔记(一):基本概念 媒体业务是网络的主要业务之间.尤其移动互联网业务的兴起,在运营商和应用开发商中,媒体业务份量极重,其中媒体的编解码服务涉及需求分析.应用开发.释放license收费等等 ...
- Java版流媒体编解码和图像处理(JavaCPP+FFmpeg)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- [转帖]AVS音视频编解码技术了解
AVS高清立体视频编码器 电视技术在经历了从黑白到彩色.从模拟到数字的技术变革之后正在酝酿另一场技术革命,从单纯观看二维场景的平面电视跨越到展现三维场景的立体电视3DTV.3DTV系统的核心问题之一是 ...
随机推荐
- 缺少所需的CD/DVD驱动器设备驱动程序
公司买了个服务器想把自带系统win10 改成windows server 2008 于是,我就用我的u盘做了个系统盘(用UltraISO或者别的,网上很多在此不在赘述) 然后改了启动项,进入到系统安装 ...
- js中top、self、parent
1.在应用iframe或者frameset的时候 parent指的是父窗口.top指的是顶级的窗口.self指的是当前的窗口-window window.self 功能:是对当前窗口自身的引用.它和w ...
- Shell 命令行实现将一个站点页面全部下载到本地并替换其中链接的脚本
Shell 命令行实现将一个站点页面全部下载到本地并替换其中链接的脚本 不知道为什么,我总想用 Shell 脚本来实现把一个站点内容给下载下来.但是下载什么站点我确不知道.今天尝试了一下利用 curl ...
- 《Drools7.0.0.Final规则引擎教程》第4章 4.5RHS语法
RHS语法 使用说明 RHS是满足LHS条件之后进行后续处理部分的统称,该部分包含要执行的操作的列表信息.RHS主要用于处理结果,因此不建议在此部分再进行业务判断.如果必须要业务判断需要考虑规则设计的 ...
- Swift中的本地化实现
1. 确保localization中添加了多语言2. 添加localisable.strings文件 3.选择这个文件,勾选多语言即可4.打开localisable.strings文件,添加一些测试字 ...
- 前端构建工具-fis3使用入门
FIS3 是面向前端的工程构建工具.解决前端工程中性能优化.资源加载(异步.同步.按需.预加载.依赖管理.合并.内嵌).模块化开发.自动化工具.开发规范.代码部署等问题. 官网地址是: https:/ ...
- linux安装配置apache服务(httpd)
1. 安装 httpd. [root@linuxprobe ~]# yum -y install httpd 2. 删除默认欢迎页面 [root@linuxprobe ~]# rm -f /etc/h ...
- 会议室预定demo mrbs
关于会议室的增删改查 查: HTML: login继承django自带的admin用户认证系统 <!DOCTYPE html> <html lang="en"&g ...
- Centos kvm+ceph
Centos kvm+ceph 一. centos6.5 安装kvm 1. disable selinux 2. 确认支持intel虚拟化 3. 安装需要的包 4.设置桥接网络 5.运行kvm ins ...
- HDU3555 Bomb 数位DP第一题
The counter-terrorists found a time bomb in the dust. But this time the terrorists improve on the ti ...