目录
|
在上一篇《s5pv210中MFC的帧内存格式》中我们知道了MFC编码所需要的格式,现在我们就来看看他的编码过程。首先说一下编码环境,我用的开发板是天嵌的TQ210,运行linux系统,其他开发板差别应该不会很大。
linear mode
MFC可以接收两种帧内存格式:linear mode和tile mode,因为tile比较麻烦,我这里就用linear模式来编码。修改MFC_ENC_MAP_FOR_CUR寄存器,让MFC选择linear mode,打开文件drivers/media/video/samsung/mfc50/mfc_opr.c,在676行左右把MEM_STRUCT_TILE_ENC改成MEM_STRUCT_LINEAR。
1
|
WRITEL(MEM_STRUCT_LINEAR, MFC_ENC_MAP_FOR_CUR);
|
保存,重新编译内核。我使用的是TQ210自带的内核,他的MFC默认是使用tile模式的,如果传给他的数据是linear模式的话就会导致花屏。
SsbSipMfcApi
这是三星官方的MFC库函数,我也不清楚在哪里找到的,好像是android源码里的,在linux上也可以使用,需要的话可以在文章最后的github上下载。他里面定义了一些MFC的API,这里我说一下和编码相关的几个函数和结构体。首先是h264的编码参数结构体SSBSIP_MFC_ENC_H264_PARAM,他的定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
typedef
struct
{
/* common parameters */
SSBSIP_MFC_CODEC_TYPE codecType;
/* [IN] codec type */
int
SourceWidth;
/* [IN] width of video to be encoded */
int
SourceHeight;
/* [IN] height of video to be encoded */
int
IDRPeriod;
/* [IN] GOP number(interval of I-frame) */
int
SliceMode;
/* [IN] Multi slice mode */
int
RandomIntraMBRefresh;
/* [IN] cyclic intra refresh */
int
EnableFRMRateControl;
/* [IN] frame based rate control enable */
int
Bitrate;
/* [IN] rate control parameter(bit rate) */
int
FrameQp;
/* [IN] The quantization parameter of the frame */
int
FrameQp_P;
/* [IN] The quantization parameter of the P frame */
int
QSCodeMax;
/* [IN] Maximum Quantization value */
int
QSCodeMin;
/* [IN] Minimum Quantization value */
int
CBRPeriodRf;
/* [IN] Reaction coefficient parameter for rate control */
int
PadControlOn;
/* [IN] Enable padding control */
int
LumaPadVal;
/* [IN] Luma pel value used to fill padding area */
int
CbPadVal;
/* [IN] CB pel value used to fill padding area */
int
CrPadVal;
/* [IN] CR pel value used to fill padding area */
int
FrameMap;
/* [IN] Encoding input mode(tile mode or linear mode) */
/* H.264 specific parameters */
int
ProfileIDC;
/* [IN] profile */
int
LevelIDC;
/* [IN] level */
int
FrameQp_B;
/* [IN] The quantization parameter of the B frame */
int
FrameRate;
/* [IN] rate control parameter(frame rate) */
int
SliceArgument;
/* [IN] MB number or byte number */
int
NumberBFrames;
/* [IN] The number of consecutive B frame inserted */
int
NumberReferenceFrames;
/* [IN] The number of reference pictures used */
int
NumberRefForPframes;
/* [IN] The number of reference pictures used for encoding P pictures */
int
LoopFilterDisable;
/* [IN] disable the loop filter */
int
LoopFilterAlphaC0Offset;
/* [IN] Alpha & C0 offset for H.264 loop filter */
int
LoopFilterBetaOffset;
/* [IN] Beta offset for H.264 loop filter */
int
SymbolMode;
/* [IN] The mode of entropy coding(CABAC, CAVLC) */
int
PictureInterlace;
/* [IN] Enables the interlace mode */
int
Transform8x8Mode;
/* [IN] Allow 8x8 transform(This is allowed only for high profile) */
int
EnableMBRateControl;
/* [IN] Enable macroblock-level rate control */
int
DarkDisable;
/* [IN] Disable adaptive rate control on dark region */
int
SmoothDisable;
/* [IN] Disable adaptive rate control on smooth region */
int
StaticDisable;
/* [IN] Disable adaptive rate control on static region */
int
ActivityDisable;
/* [IN] Disable adaptive rate control on high activity region */
} SSBSIP_MFC_ENC_H264_PARAM;
|
这些参数中对码率影响比较大的是FrameQp,取值范围0~51,越小码率越大,取30就差不多了。下面说一下编码相关的函数:
1
2
3
4
5
6
7
8
9
10
11
12
|
//打开MFC
void
*SsbSipMfcEncOpen(
void
);
//初始化MFC,param就是上面介绍的配置结构体
SSBSIP_MFC_ERROR_CODE SsbSipMfcEncInit(
void
*openHandle,
void
*param);
//获取输入缓存
SSBSIP_MFC_ERROR_CODE SsbSipMfcEncGetInBuf(
void
*openHandle, SSBSIP_MFC_ENC_INPUT_INFO *input_info);
//编码
SSBSIP_MFC_ERROR_CODE SsbSipMfcEncExe(
void
*openHandle);
//获取输出缓存
SSBSIP_MFC_ERROR_CODE SsbSipMfcEncGetOutBuf(
void
*openHandle, SSBSIP_MFC_ENC_OUTPUT_INFO *output_info);
//关闭MFC
SSBSIP_MFC_ERROR_CODE SsbSipMfcEncClose(
void
*openHandle);
|
知道了这些后下面我们就可以进行h264编码了。
main()
我这里只说一下大致的过程,在最后我会把源码传到github上去。首先打开两个文件,YUV数据源和保存h264的文件。
1
2
|
fp_yuv =
fopen
(
"CITY_704x576_30_orig_01.yuv"
,
"rb"
);
fp_strm =
fopen
(
"CITY_704x576_30_orig_01.h264"
,
"wb"
);
|
定义param变量,根据视频的参数赋值。
1
2
3
4
5
6
7
|
SSBSIP_MFC_ENC_H264_PARAM *param;
param = (SSBSIP_MFC_ENC_H264_PARAM*)
malloc
(
sizeof
(SSBSIP_MFC_ENC_H264_PARAM));
memset
(param, 0 ,
sizeof
(SSBSIP_MFC_ENC_H264_PARAM));
param->SourceWidth=704;
param->SourceHeight=576;
param->FrameQp=30;
//等等。。。
|
打开MFC,并用param初始化。
1
2
|
hOpen = SsbSipMfcEncOpen();
SsbSipMfcEncInit(hOpen, param);
|
获取输入缓存地址,以及h264文件头。
1
2
3
4
5
6
7
8
|
SSBSIP_MFC_ENC_INPUT_INFO input_info;
SSBSIP_MFC_ENC_OUTPUT_INFO output_info;
//获取输入缓存地址
SsbSipMfcEncGetInBuf(hOpen, &input_info);
//获取视频文件头
SsbSipMfcEncGetOutBuf(hOpen, &output_info);
//把文件头写入h264文件
fwrite
(output_info.StrmVirAddr, 1, output_info.headerSize, fp_strm);
|
下面就是把视频一帧一帧的读取,编码,写入文件的过程了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
//读取一帧的Y分量,保存到MFC的输入缓存中。
fread
(input_info.YVirAddr, 1, 704 * 576, fp_yuv);
//UV分量,保存到临时数组中,要转换成NV12格式
fread
(CbCrBuf, 1, (704 * 576) >> 1, fp_yuv);
// convert YV12 -> NV12
p_nv12 = (
char
*)input_info.CVirAddr;
p_cb = CbCrBuf;
p_cr = CbCrBuf;
p_cr += ((704 * 576) >> 2);
for
(i = 0; i < (704 * 576) >> 2; i++){
*p_nv12 = *p_cb;
p_nv12++;
*p_nv12 = *p_cr;
p_nv12++;
p_cb++;
p_cr++;
}
//编码
SsbSipMfcEncExe(hOpen);
//获取输出缓存地址,和大小
SsbSipMfcEncGetOutBuf(hOpen, &output_info);
//写入h264文件
fwrite
(output_info.StrmVirAddr, 1, output_info.dataSize, fp_strm);
|
这样全部编码完成后,关闭MFC和文件。
1
2
3
|
SsbSipMfcEncClose(hOpen);
fclose
(fp_yuv);
fclose
(fp_strm);
|
至此,编码过程结束了。程序:https://github.com/wuyuans/TQ210/tree/master/MFC,我稍微对API进行了一下包装,写的不太好,见笑了。