项目要用到视频编解码,最近半个月都在搞,说实话真是走了很多弯路,浪费了很多时间。将自己的最终成果记录于此,期望会给其他人提供些许帮助。
参考教程:
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)
-
{
-
-
-
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){
-
if(l==1){
-
*u=p[j*IMAGE_WIDTH*2+i];
-
u++;
-
}
-
else if(l==3){
-
l=-1;
-
continue;
-
-
}
-
else{
-
*y=p[j*IMAGE_WIDTH*2+i];
-
++y;
-
}
-
}
-
-
else if(j%2==1){
-
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);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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);
-
}
-
-
-
-
-
-
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