实际上只是一个头文件h264_util.h。h264_util.h封装了x264库。我做的工作就是使用模板封装了一下,保证使用任何mp4 container库都可以方便地输出mp4文件。使用模板的目的是为了避免任何回调的开销。
h264_util.h依赖于x264库。
main.cpp依赖于h264_util.h和libmp42(可选)和libmp4av(可选),后两者都是mpeg4ip的一部分。使用的mpeg4ip是为了生成Quicktime7可播放的mp4文件(相信我,这点很难的,我试了很多库了。原因不是这些库不好,而是QT7太糟糕)。
附带说明一下,所用到的库和我写的代码都是可以同时在window下和linux编译运行的(我已经都试过了)。要在windows下编译x264很简单的,参考说明文档就可以。麻烦的是mpeg4ip,其文档声称不再会对windows下的兼容性负责了。要在windows下编译并使用mpeg4ip,有两个办法,或者自己提供gettimeofday的定义,或者使用其libmissing(和libmissing60是一回事,60表示vs6下编译通过,实际上我用vc7也编译运行过了)。
如果同时使用x264和mpeg4ip,又会发现一些兼容性问题。问题的关键在于windows操作下默认没有提供int8_t之类的类型定义,于是mpeg4ip会自己提供一个mpeg4ip_version.h,补上这些定义。所有的跨平台的开源项目似乎都会自说自话地为windows平台(linux下有标准头文件)提供这些定义,如果我同时使用两个这样的开源项目,就可能会产生类型重复定义的编译错误。x264是个特例,它的libx264根本就没有提供这些定义,所以我在h264_util.h补上了这些定义。又由于这些类型定义可能和mpeg4ip_version.h中的定义冲突,所以我在h264_util.h中预先加入了判断避免冲突的宏。要这些宏起作用,mpeg4ip的头文件必须先被#include于我的h264_util.h之前。
要编译x264,必须安装nasm(一种汇编语言编译器),mpeg4ip如果是只编译上文中的两个库的话,不需要,否则也需要nasm。
下面就是源代码,包括两个源代码文件,h264_util.h和main.cpp和一个makefile(仅用于linux),GNUmakefile。
h264_util.h封装了x264,main.cpp提供了两个测试例子,一个仅使用h264_util.h演示了如何进行视频编码,另一个测试例子说明了如何生成mp4。test_h264_utilities是在内存中快速生成rgb888的raw image的工具,仅仅是为了我测试方便用,你完全可以不使用它。
使用方法很简单(仅限于于linux,windows下懒得写了),安装libx264,libmp4ip,libmp4v2(./configure;make;make install总会吧?mpeg4ip安装完整的软件包也许会有问题,但是安装库是没问题的(先在源代码包的根目录下./configure,然后还是在对应的库代码的目录里make;make install),把下述三个文件拷贝到同一个目录里,然后make clean;make;./test_main,就会生成一个叫test.mp4的文件。
//h264_util.h
#ifndef __H264_UTIL_H__
#define __H264_UTIL_H__
#include <cstdio>
#include <cassert>
#include <climits>
#include <cstdlib>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if (defined HAVE_INTTYPES_H)
#include <inttypes.h>
#elif (defined HAVE_STDINT_H)
#include <stdint.h>
#else
#ifndef __MPEG4IP_WIN32_H__
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
#if (defined WIN32)||(defined _WIN32)
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else //other OS
typedef signed long long int64_t;
typedef unsigned long long uint64_t;
#endif
#endif
#endif
#include "x264.h"
#ifdef __cplusplus
}
#endif
struct ContainerTraits{
//insert+=return of write_nalu ; x264_nal_encode( _bit_buffer+inserter ......);
static int write_nalu(unsigned char *nalu, int size){ return 0;}
//encoded bit buffer, contains raw h.264 data
static void set_eop(unsigned char* bit_buffer, int size){ }
};
template <typename CTraits=ContainerTraits>
class h264_utilities
{
public:
h264_utilities(int width, int height):_h(0),_bit_buffer(0),_bit_buffer_capacity(1024*256)
{
assert(width%16==0);
assert(height%16==0);
x264_param_default(&_param);
_param.i_width = width;
_param.i_height= height;
_h=x264_encoder_open(&_param);
assert(_h);
//http://www.via.ecp.fr/via/ml/vlc-devel/2005-02/msg00371.html or google "x264_encoder_encode"
//if we do not allocate the new buffer ,
//the code "RGB_TO_I420( rgb_to_i420, 0, 1, 2, 3 );" will raise segment fault
x264_picture_alloc(&_pic,X264_CSP_RGB,_param.i_width ,_param.i_height);
// Do not force any parameters
_pic.i_type = X264_TYPE_AUTO;
_pic.i_qpplus1 = 0;
_bit_buffer_capacity=(_bit_buffer_capacity>_param.i_width*_param.i_height*4? _bit_buffer_capacity:_param.i_width*_param.i_height*4);
if (!_bit_buffer)
_bit_buffer = (unsigned char*)av_malloc(_bit_buffer_capacity);
}
~h264_utilities()
{
//assert(0);
if(_bit_buffer)
{
av_free(_bit_buffer);
_bit_buffer=0;
}
x264_encoder_close(_h);
}
//only accept row images (rgb888)
bool compress(unsigned char* frame)
{
memcpy(reinterpret_cast<void*>(_pic.img.plane[0]), reinterpret_cast<void*