值得注意的是,无论编码还是解码,处理的数据都是以一帧帧的形式操作的,所以第4步是一个不断循环的过程,直到所有数据处理完成。另外,虽然编解码器以设备文件的形式使用,但是它不能使用标准的文件读写操作,查看编解码的设备驱动可以发现,其文件读写函数是空的,这一点三星公司的开发文档并没有说明。
3 H.264硬件编解码实现
FFmpeg的H.264硬件编解码[5]实现就是自定义一个视频编解码器,加入到FFmpeg库中。这个视频编解码器使用S3C6410处理视频硬件编解码功能来实现H.264的视频编码和解码过程,这样使用FFmpeg库的多媒体程序可以用访问FFmpeg其他编解码器一样的方法使用这个自定义的编解码器。添加自定义编解码器的关键是根据FFmpeg中对编解码的描述定义编解码器,并实现定义中的相关函数。
在libavcodec/avcodec.h中的AVCodec结构体是定义FFmpeg编解码器的关键结构体,包括编解码器的名字、类型(声音/视频)、编解码器的识别号(CodecID)、支持格式和一些用于初始化、编码、解码和关闭的函数指针。
typedef struct AVCodec {
const char *name;
enum CodecType type;
enum CodecID id;
int priv_data_size;
int (*init)(AVCodecContext *);
int (*encode)(AVCodecContext *,uint8_t *buf,int buf_size,void *data);
int (*close)(AVCodecContext *);
int (*decode)(AVCodecContext *,void *outdata,int *outdata_size,
uint8_t *buf,int buf_size);
int capabilities;
struct AVCodec *next;
void (*flush)(AVCodecContext *);
const AVRational *supported_framerates;
const enum PixelFormat *pix_fmts;
} AVCodec;
H.264硬件编解码器定义如下:
AVCodec s3cx264_encoder = {
.name="s3cx264",
.type=AVMEDIA_TYPE_VIDEO,
.id=CODEC_ID_H264,
.init=X264_init,
.encode=X264_frame,
.decode=X264_decode,
.close=X264_close,
…
};
解码器的名字为s3cx264,类型为视频。CodecID为H264,表示这个解码器用于H.264视频编解码。初始化、编码、解码和关闭函数指针分别指向X264_init、X264_frame、X264_decodec和X264_close函数。
添加s3cx264编解码器到编解器链中,关键是通过修改libavcodec/allcodecs.c文件实现,修改如下:
REGISTER_ENCDEC (ASV1,asv1);
REGISTER_ENCDEC (S3CX264,s3cx264);
//添加s3cx264编解码器
REGISTER_ENCDEC (ASV2,asv2);
这样,在程序运行时调用av_register_all(void)函数后,就可以把自定义的编解码器s3cx264添加到FFmpeg存放在内存中的解编码器链中。值得提出的是,对同一个视频格式FFmpeg有多个编解码器与之相对应。如H.264格式的视频,FFmpeg本身就带有对应的软解码器,现在添加了硬解码器,为了避免不确定是哪一个解码器在执行,可以把自定义的硬件编解码器在注册时放在注册过程的最前面,这样编解码器在添加到解编器链中时就会放在靠前的位置,查找时就可以优于软件解码器找到硬解码器。
把硬件编解码器s3cx264注册到编解码器链后,还要完成X264_init、X264_frame、X264_decodec和X264_close函数,编解码器才能正常工作。以下结合前面对S3C6410视频编解码过程的分析,以编码为例详细阐述实现过程。
定义X264Context结构体,保存设备文件描述符、编码参数和输入/输出地址等信息,用于FFmpeg模块间数据的传递:
typedef struct X264Context {
int dev_fd;
uint8_t *addr;
s3c_mfc_enc_init_arg_t enc_init;
s3c_mfc_enc_exe_arg_t enc_exe;
s3c_mfc_get_buf_addr_arg_t get_buf_addr;
uint8_t *in_buf,*out_buf;
AVFrame out_pic;
} X264Context;
X264_init实现的是编码器初始化过程, 用于编码器设备文件的打开、内存空间的映射、编码参数设置和获取编解码数据输入/输出地址。
static av_cold int X264_init(AVCodecContext *avctx){
X264Context *x4 = avctx>priv_data;
//打开编码器设备文件
x4>dev_fd = open(MFC_DEV_NAME,O_RDWR|O_NDELAY);
//内存空间映射
x4>addr = (uint8_t *) mmap(0,BUF_SIZE,PROT_READ |PROT_WRITE,MAP_SHARED,x4>dev_fd,0);
//编码参数设置
ioctl(x4>dev_fd,S3C_MFC_IOCTL_MFC_H264_ENC_INIT,&x4>enc_init);
//获取输入/输出地址
x4>get_buf_addr.in_usr_data = (int)x4>addr;
ioctl(x4>dev_fd,S3C_MFC_IOCTL_MFC_GET_YUV_BUF_ADDR,&x4>get_buf_addr);
x4>in_buf = (uint8_t *)x4>get_buf_addr.out_buf_addr;
x4>get_buf_addr.in_usr_data = (int)x4>addr;
ioctl(x4>dev_fd,S3C_MFC_IOCTL_MFC_GET_LINE_BUF_ADDR,&x4>get_buf_addr);
x4>out_buf = (uint8_t *)x4>get_buf_addr.out_buf_addr;
return 0;
}
ioctl的参数为S3C_MFC_IOCTL_MFC_H264_ENC_INIT,表示使用H.264编码。