s5pv210中MFC的编码过程

来源:互联网 发布:php从入门到放弃 编辑:程序博客网 时间:2024/06/10 14:09


在上一篇《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。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. typedef struct {
  2. /* common parameters */
  3. SSBSIP_MFC_CODEC_TYPE codecType; /* [IN] codec type */
  4. int SourceWidth; /* [IN] width of video to be encoded */
  5. int SourceHeight; /* [IN] height of video to be encoded */
  6. int IDRPeriod; /* [IN] GOP number(interval of I-frame) */
  7. int SliceMode; /* [IN] Multi slice mode */
  8. int RandomIntraMBRefresh; /* [IN] cyclic intra refresh */
  9. int EnableFRMRateControl; /* [IN] frame based rate control enable */
  10. int Bitrate; /* [IN] rate control parameter(bit rate) */
  11. int FrameQp; /* [IN] The quantization parameter of the frame */
  12. int FrameQp_P; /* [IN] The quantization parameter of the P frame */
  13. int QSCodeMax; /* [IN] Maximum Quantization value */
  14. int QSCodeMin; /* [IN] Minimum Quantization value */
  15. int CBRPeriodRf; /* [IN] Reaction coefficient parameter for rate control */
  16. int PadControlOn; /* [IN] Enable padding control */
  17. int LumaPadVal; /* [IN] Luma pel value used to fill padding area */
  18. int CbPadVal; /* [IN] CB pel value used to fill padding area */
  19. int CrPadVal; /* [IN] CR pel value used to fill padding area */
  20. int FrameMap; /* [IN] Encoding input mode(0=linear, 1=tiled) */
  21. /* H.264 specific parameters */
  22. int ProfileIDC; /* [IN] profile */
  23. int LevelIDC; /* [IN] level */
  24. int FrameQp_B; /* [IN] The quantization parameter of the B frame */
  25. int FrameRate; /* [IN] rate control parameter(frame rate) */
  26. int SliceArgument; /* [IN] MB number or byte number */
  27. int NumberBFrames; /* [IN] The number of consecutive B frame inserted */
  28. int NumberReferenceFrames; /* [IN] The number of reference pictures used */
  29. int NumberRefForPframes; /* [IN] The number of reference pictures used for encoding P pictures */
  30. int LoopFilterDisable; /* [IN] disable the loop filter */
  31. int LoopFilterAlphaC0Offset; /* [IN] Alpha & C0 offset for H.264 loop filter */
  32. int LoopFilterBetaOffset; /* [IN] Beta offset for H.264 loop filter */
  33. int SymbolMode; /* [IN] The mode of entropy coding(CABAC, CAVLC) */
  34. int PictureInterlace; /* [IN] Enables the interlace mode */
  35. int Transform8x8Mode; /* [IN] Allow 8x8 transform(This is allowed only for high profile) */
  36. int EnableMBRateControl; /* [IN] Enable macroblock-level rate control */
  37. int DarkDisable; /* [IN] Disable adaptive rate control on dark region */
  38. int SmoothDisable; /* [IN] Disable adaptive rate control on smooth region */
  39. int StaticDisable; /* [IN] Disable adaptive rate control on static region */
  40. int ActivityDisable; /* [IN] Disable adaptive rate control on high activity region */
  41. } SSBSIP_MFC_ENC_H264_PARAM;

这些参数中对码率影响比较大的是FrameQp,取值范围0~51,越小码率越大,取30就差不多了。下面说一下编码相关的函数:

  1. //打开MFC
  2. void *SsbSipMfcEncOpen(void);
  3. //初始化MFC,param就是上面介绍的配置结构体
  4. SSBSIP_MFC_ERROR_CODE SsbSipMfcEncInit(void *openHandle, void *param);
  5. //获取输入缓存
  6. SSBSIP_MFC_ERROR_CODE SsbSipMfcEncGetInBuf(void *openHandle, SSBSIP_MFC_ENC_INPUT_INFO *input_info);
  7. //编码
  8. SSBSIP_MFC_ERROR_CODE SsbSipMfcEncExe(void *openHandle);
  9. //获取输出缓存
  10. SSBSIP_MFC_ERROR_CODE SsbSipMfcEncGetOutBuf(void *openHandle, SSBSIP_MFC_ENC_OUTPUT_INFO *output_info);
  11. //关闭MFC
  12. SSBSIP_MFC_ERROR_CODE SsbSipMfcEncClose(void *openHandle);

知道了这些后下面我们就可以进行h264编码了。

main()

我这里只说一下大致的过程,在最后我会把源码传到github上去。首先打开两个文件,YUV数据源和保存h264的文件。

  1. fp_yuv = fopen("CITY_704x576_30_orig_01.yuv", "rb");
  2. fp_strm = fopen("CITY_704x576_30_orig_01.h264", "wb");

定义param变量,根据视频的参数赋值。

  1. SSBSIP_MFC_ENC_H264_PARAM *param;
  2. param = (SSBSIP_MFC_ENC_H264_PARAM*)malloc(sizeof(SSBSIP_MFC_ENC_H264_PARAM));
  3. memset(param, 0 , sizeof(SSBSIP_MFC_ENC_H264_PARAM));
  4. param->SourceWidth=704;
  5. param->SourceHeight=576;
  6. param->FrameQp=30;
  7. //等等。。。

打开MFC,并用param初始化。

  1. hOpen = SsbSipMfcEncOpen();
  2. SsbSipMfcEncInit(hOpen, param);

获取输入缓存地址,以及h264文件头。

  1. SSBSIP_MFC_ENC_INPUT_INFO input_info;
  2. SSBSIP_MFC_ENC_OUTPUT_INFO output_info;
  3. //获取输入缓存地址
  4. SsbSipMfcEncGetInBuf(hOpen, &input_info);
  5. //获取视频文件头
  6. SsbSipMfcEncGetOutBuf(hOpen, &output_info);
  7. //把文件头写入h264文件
  8. fwrite(output_info.StrmVirAddr, 1, output_info.headerSize, fp_strm);

下面就是把视频一帧一帧的读取,编码,写入文件的过程了。

  1. //读取一帧的Y分量,保存到MFC的输入缓存中。
  2. fread(input_info.YVirAddr, 1, 704 * 576, fp_yuv);
  3. //UV分量,保存到临时数组中,要转换成NV12格式
  4. fread(CbCrBuf, 1, (704 * 576) >> 1, fp_yuv);
  5. // convert YV12 -> NV12
  6. p_nv12 = (char *)input_info.CVirAddr;
  7. p_cb = CbCrBuf;
  8. p_cr = CbCrBuf;
  9. p_cr += ((704 * 576) >> 2);
  10. for(i = 0; i < (704 * 576) >> 2; i++){
  11. *p_nv12 = *p_cb;
  12. p_nv12++;
  13. *p_nv12 = *p_cr;
  14. p_nv12++;
  15. p_cb++;
  16. p_cr++;
  17. }
  18. //编码
  19. SsbSipMfcEncExe(hOpen);
  20. //获取输出缓存地址,和大小
  21. SsbSipMfcEncGetOutBuf(hOpen, &output_info);
  22. //写入h264文件
  23. fwrite(output_info.StrmVirAddr, 1, output_info.dataSize, fp_strm);

这样全部编码完成后,关闭MFC和文件。

  1. SsbSipMfcEncClose(hOpen);
  2. fclose(fp_yuv);
  3. fclose(fp_strm);

至此,编码过程结束了。程序:https://github.com/wuyuans/TQ210/tree/master/MFC,我稍微对API进行了一下包装,写的不太好,见笑了。

0 0
原创粉丝点击