The project uses video codec, which has been around for the last half a month. To be honest, it has taken a lot of detours and wasted a lot of time. Record your final results here, hoping to help others.
Reference Tutorial:
Http://ffmpeg.org/trac/ffmpeg/wiki/ubuntucompilationguideinstall the ffmpegand x264 official tutorials (be careful not to use command line installation, there will be a lot less libraries. Installation and compilation are the safest)
Http://blog.csdn.net/zgyulongfei/article/details/7526249
Http://www.cnblogs.com/fojian/archive/2012/09/01/2666627.html#good article
Http://my.oschina.net/u/555701/blog/56616? P = 2 # comments-a good decoded article
The overall process is as follows:
The entire process is divided into three parts: Collection, encoding, and decoding.
1. Video collection
I use a USB camera to collect videos. My camera only supports image acquisition in yuv422 format, because the x264 encoding library can only encode the yuv420p (planar) format, therefore, the image data in yuv422 format must be converted to yuv420p format.
You can use the official video collection program to modify the video.
Static void process_image (const char * P); Function
The parameter P points to the YUV data of a captured image.
There are many tutorials on YUV and RGB formats.
Here, I will explain my understanding.
Suppose there is a 4*4 resolution image, as shown below:
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Each pixel is composed of YUV data, assuming the following:
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 |
2010.cn |
V16 |
The yuv422 image is like this. Each pixel is Y and UV is collected every two pixels:
In packed format, yuv420 is like this. Each pixel is collected by Y and UV, and each row is collected by every two pixels:
The above formats are stored from left to right, from top to bottom.
I want to get the yuv420 in planar format, that is, in a continuous memory, store all y, then all U, and finally all v.
The modified process_image function is as follows:
static voidprocess_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-Chromal=-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.
Encoding
Use the x264 encoding library to encode the yuv420p file.
The procedure is as follows:
#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_I420typedef 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";intmain(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 DEBUGprintf("Line --------%d\n",__LINE__);#endifencoder->x264_encoder=x264_encoder_open(encoder->x264_parameter);encoder->colorspace=ENCODER_COLORSPACE;#if DEBUGprintf("Line --------%d\n",__LINE__);#endifencoder->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 DEBUGprintf("Line --------%d\n",__LINE__);#endifencoder->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 DEBUGprintf("Line --------%d\n",__LINE__);#endifencoder->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 DEBUGprintf("Line --------%d\n",__LINE__);#endifint 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. Decoding
Use FFMPEG for decoding
The procedure is as follows:
#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 240voiderror_handle(const char *errorInfo ){printf("%s error!\n",errorInfo);exit(EXIT_FAILURE);}intmain(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;}
Result:
1. yuv420p, 11.0 MB in size collected by the USB camera, can be played normally using the pyuv player.
2. The encoded file encode. h264 is 262.4kb in size and can be played normally on the VLC player.
3. The decoded file Decode. YUV is 11.0 MB in size and can be played normally using the pyuv player.
Related Files are included in my resources:
1. Collection, encoding, decoding, corresponding executable programs, and makefile files;
2. pyuv player (for XP)
3. Experiment file-yuv420p. YUV, encode. h264, decode. YUV
4. PDF
Thank you for your criticism!