ref :https://blog.csdn.net/wytzsjzly/article/details/82500277

 

前言

H264视频编码技术诞生于2003年,至今已有十余载,技术相当成熟,它的优势在于有高的视频的压缩率,利用帧间和帧内预测(Estimation)、变换(Transform)和反变换、量化(Quantization)和反量化、环路滤波(Loop Filter)、熵编码(Entropy Coding)等视频编码技术,可以实现高质量、低码率的视频流编码。H.264提供了网络抽象层NALU(Network Abstraction Layer)概念对编码出来的视频码流进一步格式封装,使得H.264的文件能容易地在不同网络上传输,以达到低带宽占用、低播放延时的目的。相信在未来几年H.264仍是视频编码的主流技术,尽管在2013年提出了H.265新一代视频编码技术,但是H265的压缩率仅仅提高40%,复杂程度却提升%50以上,这对硬件性能提出新的要求。本文讲述如何在海思HI35xx平台上实现H.264解码。

背景知识

先来弄清楚视频格式和视频编码的相互关系,视频格式可以理解为一个容器,它将编码器生成的多媒体内容(视频,音频,字幕,章节信息等)混合封装在一起的标准,这样就能很好处理视频、音频、字幕的播放同步问题,常见的视频格式有mp4、avi、flv、rmvb、mkv等等。视频编码属于后处理技术,它其实是非必须的,但是从摄像机采集出来的数据十分庞大,不适合网络传输和存储,故需要对视频进行编码压缩,以达到低码率、高成像质量的目的,常见的视频编码技术有h.263,h.264,mpeg-4。接下来我们了解一下海思HIMPP平台解码的概念,HIMPP平台音视频编解码架构遵循下图所示的数据处理流程。H264解码实例走的是HARD DISK->VDEC->VPSS->VO->显示器的流程,这个流程一定要熟悉牢记,代码实现都是围绕这条主线来编写的。

H264解码实例

实例源码很简单,先来了解一下实现H.264解码实例的几个函数,以达到了解实例源码大概构造组成,然后再对每个函数进行具体分析。

 
  1. /*

  2. **函数描述:linux标准信号捕捉函数

  3. **函数功用:接收Ctrl+C信号,用来退出程序,并销毁HIMPP调用

  4. */

  5. HI_VOID SAMPLE_VDEC_HandleSig(HI_S32 signo)

  6. {

  7. ......

  8. }

  9. /*

  10. **函数描述:用于音视频文件读写推流

  11. **函数功用:用fread等文件操作函数读取音视频文件,并解析后推送HIMPP进行解码

  12. */

  13. int SAMPLE_COMM_VDEC_H264_SendStream( VdecThreadParam *pArgs)

  14. {

  15. ......

  16. }

  17. }

  18. /*

  19. **函数描述:HIMPP系统初始化

  20. **函数功用:配置HIMPP系统的各项参数以满足对目标进行编解码

  21. */

  22. HI_S32 HI_S32 SAMPLE_VDEC_VdhH264(char *filename)

  23. {

  24. ......

  25. }

  26. /*

  27. **主函数

  28. */

  29. int main(int argc, char *argv[])

  30. {

  31. ......

  32. }

下面重点讲解SAMPLE_VDEC_VdhH264(char*filename)这个函数,它是H264解码样例的重点函数。HIMPP系统的API函数是海思提供的SDK开发包,调用它相关的接口,在编译时必须将其提供的相应库文件进行包含编译。下面结合样例程序讲述如何使用HIMPP提供的API实现自己的业务逻辑。H.264视频解码实例走的是HARDDISK->VDEC->VPSS->VO->显示器的处理流程,这个过程可以细分为八大步骤,这八大步骤在其他类型的音视频编解码样例也类似,可以说这八大步骤是使用海思HIMPP API的灵魂。下面简单介绍这个八大步骤的内容:

Step1:初始化HIMPP SYS和通用VB缓冲,包括设置缓冲区的大小,缓冲区块的数目。需要注意的是,在设置通用VB参数之前,必须确保HIMPP系统已经退出,否则设置失败。

Step2:设置通用缓冲区的公共缓冲池属性。

Step3:配置解码器,包括指定解码类型,这里是H.264解码样例,当然选PT_H264啦,然后指定视频大小、解码优先级等等。然后创建解码通道,并是能加收解码流。

Step4:配置VPSS参数,VPSS是对VDEC解码后的流进行处理,如裁剪、降噪等,MPEG解码实例从简单应用出发,仅仅按默认的方式配置VPSS。

Step5:配置VO参数,这一步也很关键,因为它指定了画面输出,包括常见的HDMI和VGA,主要是配置输出显示,图层属性设置、输出位置等信息。

Step6:绑定VDEC与VPSS,实现H264解码流程。

Step7:绑定VPSS与VO,实现H264解码流程。

Step8:推送视频流数据,这一步需要文件读写配合使用,对于H264一般做法是先从NALU层中找到视频I帧,然后将I帧推流至VDEC,紧接下来就按帧推送就好了,该注意的是H264解码必须先推送I帧,否则会视频会花屏。

 
  1. /*

  2. **函数功能:HIMPP系统初始化及配置

  3. **HARDDISK->VDEC->VPSS->VO->显示器的处理流程

  4. */

  5. HARDDISK->VDEC->VPSS->VO->显示器的处理流程

  6. HI_S32 SAMPLE_VDEC_VdhH264(char *filename)

  7. {

  8. VB_CONF_S stVbConf, stModVbConf;

  9. HI_S32 i, s32Ret = HI_SUCCESS;

  10. VdecThreadParam pstVdecSend;

  11. SIZE_S stSize;

  12. VO_PUB_ATTR_S stVoPubAttr;

  13. VO_VIDEO_LAYER_ATTR_S stVoLayerAttr;

  14. stSize.u32Width = HD_WIDTH;

  15. stSize.u32Height = HD_HEIGHT;

  16. /************************************************

  17. step1: HIMPP系统初始化以及通用视频缓冲池配置

  18. *************************************************/

  19. MPP_SYS_CONF_S stSysConf = {0};

  20. memset(&stVbConf,0,sizeof(stVbConf));

  21. stSize.u32Width = HD_WIDTH; //指定宽度

  22. stSize.u32Height = HD_HEIGHT; //指定高度

  23. stVbConf.u32MaxPoolCnt = 1; //指定最大缓冲池数量,我们只创建一路H264解码

  24. stVbConf.astCommPool[0].u32BlkSize = (stSize.u32Width * stSize.u32Height* 4) >> 1; //指定缓冲池大小,经验值一般为分辨率的1.5倍左右,这里取2倍

  25. stVbConf.astCommPool[0].u32BlkCnt = 3;

  26. memset(stVbConf.astCommPool[0].acMmzName,0,sizeof(stVbConf.astCommPool[0].acMmzName));

  27. HI_MPI_SYS_Exit(); //设置前先去初始换HIMPP调用

  28. for(i=0;i<22;i++)

  29. {

  30. HI_MPI_VB_ExitModCommPool(i);

  31. }

  32. for(i=0; i<256; i++)

  33. {

  34. HI_MPI_VB_DestroyPool(i);

  35. }

  36. HI_MPI_VB_Exit();

  37. s32Ret = HI_MPI_VB_SetConf(&stVbConf);//配置缓冲池

  38. if (HI_SUCCESS != s32Ret)

  39. {

  40. hidebug("HI_MPI_VB_SetConf failed!\n");

  41. return HI_FAILURE;

  42. }

  43. s32Ret = HI_MPI_VB_Init();//缓冲池初始化

  44. if (HI_SUCCESS != s32Ret)

  45. {

  46. hidebug("HI_MPI_VB_Init failed!\n");

  47. return HI_FAILURE;

  48. }

  49. stSysConf.u32AlignWidth = 16;

  50. /*set config of mpp system*/

  51. s32Ret = HI_MPI_SYS_SetConf(&stSysConf);//HIMPP配置

  52. if (HI_SUCCESS != s32Ret)

  53. {

  54. hidebug("HI_MPI_SYS_SetConf failed!\n");

  55. return HI_FAILURE;

  56. }

  57. s32Ret = HI_MPI_SYS_Init(); //HIMPP系统初始化

  58. if (HI_SUCCESS != s32Ret)

  59. {

  60. hidebug("HI_MPI_SYS_Init failed!\n");

  61. return HI_FAILURE;

  62. }

  63. /************************************************

  64. step2: 系统缓冲池模块初始化配置

  65. *************************************************/

  66. memset(&stModVbConf, 0, sizeof(VB_CONF_S));

  67. stModVbConf.u32MaxPoolCnt = 2;

  68. stModVbConf.astCommPool[0].u32BlkSize = (stSize.u32Width * stSize.u32Height* 4) >> 1;;

  69. stModVbConf.astCommPool[0].u32BlkCnt = 5;

  70. stModVbConf.astCommPool[1].u32BlkSize = (stSize.u32Width * stSize.u32Height* 4) >> 1;;

  71. stModVbConf.astCommPool[1].u32BlkCnt = 5;

  72. HI_MPI_VB_ExitModCommPool(VB_UID_VDEC);

  73. HI_MPI_VB_SetModPoolConf(VB_UID_VDEC, &stModVbConf);

  74. HI_MPI_VB_InitModCommPool(VB_UID_VDEC);

  75. /************************************************

  76. step3: 解码器配置及初始化

  77. *************************************************/

  78. VDEC_CHN_ATTR_S stVdecChnAttr;

  79. stVdecChnAttr.enType = PT_H264; //创建H264类型的解码器

  80. stVdecChnAttr.u32BufSize = 3 * stSize.u32Width * stSize.u32Height;//指定解码缓冲区大小

  81. stVdecChnAttr.u32Priority = 5; //设置解码优先级

  82. stVdecChnAttr.u32PicWidth = stSize.u32Width; //解码宽高

  83. stVdecChnAttr.u32PicHeight =stSize.u32Height;

  84. stVdecChnAttr.stVdecVideoAttr.enMode = VIDEO_MODE_FRAME; //帧式解码模式

  85. stVdecChnAttr.stVdecVideoAttr.u32RefFrameNum = 2;

  86. stVdecChnAttr.stVdecVideoAttr.bTemporalMvpEnable = 0;

  87. HI_MPI_VDEC_SetChnVBCnt(0, 10);

  88. HI_MPI_VDEC_CreateChn(0, &stVdecChnAttr);

  89. HI_MPI_VDEC_StartRecvStream(0);

  90. /************************************************

  91. step4: VPSS配置及初始化

  92. *************************************************/

  93. VPSS_GRP_PARAM_S stVpssParam = {0};

  94. VPSS_CHN_ATTR_S stChnAttr = {0};

  95. VPSS_GRP_ATTR_S stVpssGrpAttr;

  96. stVpssGrpAttr.enDieMode = VPSS_DIE_MODE_NODIE;

  97. stVpssGrpAttr.bIeEn = HI_FALSE;

  98. stVpssGrpAttr.bDciEn = HI_TRUE;

  99. stVpssGrpAttr.bNrEn = HI_FALSE;

  100. stVpssGrpAttr.bHistEn = HI_FALSE;

  101. stVpssGrpAttr.bEsEn = HI_FALSE;

  102. stVpssGrpAttr.enPixFmt = PIXEL_FORMAT_YUV_SEMIPLANAR_420;//解码像素格式YUV420

  103. stVpssGrpAttr.u32MaxW = stSize.u32Width;

  104. stVpssGrpAttr.u32MaxH = stSize.u32Height;

  105. /*** create vpss group ***/

  106. s32Ret = HI_MPI_VPSS_CreateGrp(0, &stVpssGrpAttr);//创建VPSS Group,在HI3536平台一个Group有4个VPSS Channel

  107. if (s32Ret != HI_SUCCESS)

  108. {

  109. hidebug("HI_MPI_VPSS_CreateGrp failed!\n");

  110. return HI_FAILURE;

  111. }

  112. /*** set vpss param ***/

  113. s32Ret = HI_MPI_VPSS_GetGrpParam(0, &stVpssParam);//设置VPSS Group属性

  114. if(s32Ret != HI_SUCCESS)

  115. {

  116. hidebug("HI_MPI_VPSS_GetGrpParam failed!\n");

  117. return HI_FAILURE;

  118. }

  119. stVpssParam.u32IeStrength = 0;

  120. s32Ret = HI_MPI_VPSS_SetGrpParam(0, &stVpssParam);

  121. if(s32Ret != HI_SUCCESS)

  122. {

  123. hidebug("HI_MPI_VPSS_GetGrpParam failed!\n");

  124. return HI_FAILURE;

  125. }

  126. /*** enable vpss chn, with frame ***/

  127. /* Set Vpss Chn attr */

  128. stChnAttr.bSpEn = HI_FALSE;

  129. stChnAttr.bUVInvert = HI_FALSE;

  130. stChnAttr.bBorderEn = HI_TRUE;

  131. stChnAttr.stBorder.u32Color = 0xffffff;//背景色为黑色

  132. stChnAttr.stBorder.u32LeftWidth = 2;

  133. stChnAttr.stBorder.u32RightWidth = 2;

  134. stChnAttr.stBorder.u32TopWidth = 2;

  135. stChnAttr.stBorder.u32BottomWidth = 2;

  136. s32Ret = HI_MPI_VPSS_SetChnAttr(0, 0, &stChnAttr);

  137. if(s32Ret != HI_SUCCESS)

  138. {

  139. hidebug("HI_MPI_VPSS_SetChnAttr failed!\n");

  140. return HI_FAILURE;

  141. }

  142. s32Ret = HI_MPI_VPSS_EnableChn(0, 0);//由于只有一路H264解码,故只用Group0 及 Channel 0

  143. if(s32Ret != HI_SUCCESS)

  144. {

  145. hidebug("HI_MPI_VPSS_EnableChn failed!\n");

  146. return HI_FAILURE;

  147. }

  148. /*** start vpss group ***/

  149. s32Ret = HI_MPI_VPSS_StartGrp(0);

  150. if(s32Ret != HI_SUCCESS)

  151. {

  152. hidebug("HI_MPI_VPSS_StartGrp failed!\n");

  153. return HI_FAILURE;

  154. }

  155. /************************************************

  156. step5: 配置VO及初始化VO

  157. *************************************************/

  158. VO_CHN_ATTR_S stChnAttr1;

  159. stVoPubAttr.enIntfSync = VO_OUTPUT_1080P60;//VO输出模式为1080p 60帧,普通显示器的输出

  160. stVoPubAttr.enIntfType = VO_INTF_VGA | VO_INTF_HDMI;//启用VGA及HDMI输出

  161. s32Ret = HI_MPI_VO_SetPubAttr(0, &stVoPubAttr);

  162. if(s32Ret != HI_SUCCESS)

  163. {

  164. hidebug("HI_MPI_VO_SetPubAttr failed!\n");

  165. return HI_FAILURE;

  166. }

  167. s32Ret = HI_MPI_VO_Enable(0);

  168. if (s32Ret != HI_SUCCESS)

  169. {

  170. hidebug("HI_MPI_VO_Enable failed!\n");

  171. return HI_FAILURE;

  172. }

  173. //设置VO Layer显示配置,如显示位置,大小,像素类型

  174. stVoLayerAttr.u32DispFrmRt = 60;

  175. stVoLayerAttr.stDispRect.u32Width = 1920;

  176. stVoLayerAttr.stDispRect.u32Height = 1080;

  177. stVoLayerAttr.stImageSize.u32Width = stVoLayerAttr.stDispRect.u32Width;

  178. stVoLayerAttr.stImageSize.u32Height = stVoLayerAttr.stDispRect.u32Height;

  179. stVoLayerAttr.bClusterMode = HI_FALSE;

  180. stVoLayerAttr.bDoubleFrame = HI_FALSE;

  181. stVoLayerAttr.enPixFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420;

  182. s32Ret = HI_MPI_VO_SetVideoLayerAttr(0, &stVoLayerAttr);

  183. if(s32Ret != HI_SUCCESS)

  184. {

  185. hidebug("HI_MPI_VO_SetVideoLayerAttr failed!\n");

  186. return HI_FAILURE;

  187. }

  188. s32Ret = HI_MPI_VO_EnableVideoLayer(0);

  189. if (s32Ret != HI_SUCCESS)

  190. {

  191. hidebug("HI_MPI_VO_EnableVideoLayer failed!\n");

  192. return HI_FAILURE;

  193. }

  194. /*

  195. if (HI_SUCCESS != SAMPLE_COMM_VO_HdmiStart(stVoPubAttr.enIntfSync))

  196. {

  197. hidebug("Start SAMPLE_COMM_VO_HdmiStart failed!\n");

  198. }

  199. */

  200. //设置VO Channel显示位置,大小

  201. stChnAttr1.stRect.s32X = 0;

  202. stChnAttr1.stRect.s32Y = 0;

  203. stChnAttr1.stRect.u32Width = 1920;

  204. stChnAttr1.stRect.u32Height = 1080;

  205. stChnAttr1.u32Priority = 0;

  206. stChnAttr1.bDeflicker = HI_FALSE;

  207. s32Ret = HI_MPI_VO_SetChnAttr(0, 0, &stChnAttr1);

  208. if (s32Ret != HI_SUCCESS)

  209. {

  210. hidebug("failed with %#x!\n", s32Ret);

  211. }

  212. s32Ret = HI_MPI_VO_EnableChn(0,0);

  213. if (s32Ret != HI_SUCCESS)

  214. {

  215. hidebug("failed with %#x!\n", s32Ret);

  216. }

  217. /************************************************

  218. step6: 解码器绑定VPSS

  219. *************************************************/

  220. MPP_CHN_S stSrcChn;

  221. MPP_CHN_S stDestChn;

  222. stSrcChn.enModId = HI_ID_VDEC;

  223. stSrcChn.s32DevId = 0;

  224. stSrcChn.s32ChnId = 0;

  225. stDestChn.enModId = HI_ID_VPSS;

  226. stDestChn.s32DevId = 0;

  227. stDestChn.s32ChnId = 0;

  228. s32Ret = HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);

  229. if(s32Ret != HI_SUCCESS)

  230. {

  231. hidebug("HI_MPI_SYS_Bind failed!\n");

  232. return HI_FAILURE;

  233. }

  234. /************************************************

  235. step7: VPSS绑定VO

  236. *************************************************/

  237. stSrcChn.enModId = HI_ID_VPSS;

  238. stSrcChn.s32DevId = 0;

  239. stSrcChn.s32ChnId = 0;

  240. stDestChn.enModId = HI_ID_VOU;

  241. stDestChn.s32DevId = 0;

  242. stDestChn.s32ChnId = 0;

  243. s32Ret = HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);

  244. if(s32Ret != HI_SUCCESS)

  245. {

  246. hidebug("HI_MPI_SYS_Bind failed!\n");

  247. return HI_FAILURE;

  248. }

  249. /************************************************

  250. step8: 发送解码流,推送H264流至VDEC

  251. *************************************************/

  252. sprintf(pstVdecSend.cFileName,filename);

  253. pstVdecSend.s32MilliSec = 0;

  254. pstVdecSend.s32ChnId = 0;

  255. pstVdecSend.s32IntervalTime = 1;

  256. pstVdecSend.u64PtsInit = 0;

  257. pstVdecSend.u64PtsIncrease = 0;

  258. pstVdecSend.eCtrlSinal = VDEC_CTRL_START;

  259. pstVdecSend.bLoopSend = HI_TRUE;

  260. pstVdecSend.bManuSend = HI_FALSE;

  261. pstVdecSend.enType = PT_H264;

  262. pstVdecSend.s32MinBufSize = (stVdecChnAttr.u32PicWidth *stVdecChnAttr.u32PicHeight * 3)>>1;

  263. pstVdecSend.s32StreamMode = VIDEO_MODE_FRAME;

  264. SAMPLE_COMM_VDEC_H264_SendStream(&pstVdecSend);

  265. return s32Ret;

  266. }

  267. int main(int argc, char *argv[])

  268. {

  269. if(argc != 2)

  270. {

  271. printf("Usage: h264 filename\n");

  272. exit(0);

  273. }

  274. signal(SIGINT, SAMPLE_VDEC_HandleSig);

  275. signal(SIGTERM, SAMPLE_VDEC_HandleSig);

  276. SAMPLE_VDEC_VdhH264(argv[1]);

  277. return 0;

  278. }

H264的NALU

这里讲一下H264编码的NALU基础知识,NAL全称Network Abstract Layer, 即网络抽象层。如何从NAL里面找到我们需要的帧呢,原来实际的H264数据帧中,往往在帧前面带有00 00 00 01 或 00 00 01分隔符,来标识一帧的起始位置。以00 00 00 01分割之后的下一个字节就是NALU类型,将其转为二进制数据后,解读顺序为从左往右算,第一位为禁止位,值为1表示语法出错;第2~3位为参考级别;第4~8为是NAL单元类型。常见的NALU的类型有序列参数集SPS,它的NAL单元类型值为0x67,即0000 0001 0110 0111;图像参数集PPS,它的NAL单元类型值为0x68,即0000 00010110 1000;IDR图像中的片(I帧) ,它的NAL单元类型值为0x65,即0000 0001 0110 0101。所以判断是否为I帧的算法为: (NALU类型 & 0001  1111)= 5,即NALU类型 & 1F= 5,比如0x65 & 1F = 5。结合H264推流的代码看一下,这段代码的通过读取H.264文件到一个缓冲区,然后对缓冲区进行数据处理,判断I帧的起始位置。

 
  1. //解析H264文件的I帧信息

  2. else if ( (pstVdecThreadParam->s32StreamMode==VIDEO_MODE_FRAME) && (pstVdecThreadParam->enType == PT_H264) )

  3. {

  4. bFindStart = HI_FALSE;

  5. bFindEnd = HI_FALSE;

  6. fseek(fpStrm, s32UsedBytes, SEEK_SET);

  7. s32ReadLen = fread(pu8Buf, 1, pstVdecThreadParam->s32MinBufSize, fpStrm);

  8. if (s32ReadLen == 0)

  9. {

  10. if (pstVdecThreadParam->bLoopSend)

  11. {

  12. s32UsedBytes = 0;

  13. fseek(fpStrm, 0, SEEK_SET);

  14. s32ReadLen = fread(pu8Buf, 1, pstVdecThreadParam->s32MinBufSize, fpStrm);

  15. }

  16. else

  17. {

  18. break;

  19. }

  20. }

  21. for (i=0; i<s32ReadLen-8; i++)

  22. {

  23. int tmp = pu8Buf[i+3] & 0x1F; //判断I帧

  24. if ( pu8Buf[i] == 0 && pu8Buf[i+1] == 0 && pu8Buf[i+2] == 1 &&

  25. (

  26. ((tmp == 5 || tmp == 1) && ((pu8Buf[i+4]&0x80) == 0x80)) ||

  27. (tmp == 20 && (pu8Buf[i+7]&0x80) == 0x80)

  28. )

  29. )

  30. {

  31. bFindStart = HI_TRUE;

  32. i += 8;

  33. break;

  34. }

  35. }

  36. for (; i<s32ReadLen-8; i++)

  37. {

  38. int tmp = pu8Buf[i+3] & 0x1F;//判断I帧

  39. if ( pu8Buf[i ] == 0 && pu8Buf[i+1] == 0 && pu8Buf[i+2] == 1 &&

  40. (

  41. tmp == 15 || tmp == 7 || tmp == 8 || tmp == 6 ||

  42. ((tmp == 5 || tmp == 1) && ((pu8Buf[i+4]&0x80) == 0x80)) ||

  43. (tmp == 20 && (pu8Buf[i+7]&0x80) == 0x80)

  44. )

  45. )

  46. {

  47. bFindEnd = HI_TRUE;

  48. break;

  49. }

  50. }

  51. if(i > 0) s32ReadLen = i;

  52. if (bFindStart == HI_FALSE)

  53. {

  54. printf("SAMPLE_TEST: chn %d can not find start code!s32ReadLen %d, s32UsedBytes %d. \n",

  55. pstVdecThreadParam->s32ChnId, s32ReadLen, s32UsedBytes);

  56. }

  57. else if (bFindEnd == HI_FALSE)

  58. {

  59. s32ReadLen = i+8;

  60. }

  61. }

总结

H.264解码实例参考了海思提供的样例及库,程序源码及相关库文件请点击这里,修改不同的编译链工具,即可在不同HI35XX系列平台运行,整个H.264解码实例提供了最简单的解码实现方式,当然还可以实现快进播放、暂停播放等常用的视频播放控制逻辑,这需要读者进一步摸索。

海思HI35xx平台软件开发快速入门之H264解码实例学习的更多相关文章

  1. FFmpeg开发笔记(十):ffmpeg在ubuntu上的交叉编译移植到海思HI35xx平台

    FFmpeg和SDL开发专栏(点击传送门) 上一篇:<FFmpeg开发笔记(九):ffmpeg解码rtsp流并使用SDL同步播放>下一篇:敬请期待   前言   将ffmpeg移植到海思H ...

  2. 海思hi35xx 开发学习(3):视频输入

    视频输入(VI)模块实现的功能:通过 MIPI Rx(含 MIPI 接口.LVDS 接口和 HISPI 接口),SLVS-EC,BT.1120,BT.656,BT.601,DC 等接口接收视频数据.V ...

  3. HealthKit开发快速入门教程之HealthKit开发概述简介

    HealthKit开发快速入门教程之HealthKit开发概述简介 2014年6月2日召开的年度开发者大会上,苹果发布了一款新的移动应用平台,可以收集和分析用户的健康数据.该移动应用平台被命名为“He ...

  4. Transform组件C#游戏开发快速入门

    Transform组件C#游戏开发快速入门大学霸 组件(Component)可以看作是一类属性的总称.而属性是指游戏对象上一切可设置.调节的选项,如图2-8所示.本文选自C#游戏开发快速入门大学霸   ...

  5. HealthKit开发快速入门教程之HealthKit数据的操作

    HealthKit开发快速入门教程之HealthKit数据的操作 数据的表示 在HealthKit中,数据是最核心的元素.通过分析数据,人们可以看到相关的健康信息.例如,通过统计步数数据,人们可以知道 ...

  6. HealthKit开发快速入门教程之HealthKit框架体系创建健康AppID

    HealthKit开发快速入门教程之HealthKit框架体系创建健康AppID HealthKit开发准备工作 在开发一款HealthKit应用程序时,首先需要讲解HealthKit中有哪些类,在i ...

  7. Apple Watch开发快速入门教程

     Apple Watch开发快速入门教程  试读下载地址:http://pan.baidu.com/s/1eQ8JdR0 介绍:苹果为Watch提供全新的开发框架WatchKit.本教程是国内第一本A ...

  8. 游戏控制杆OUYA游戏开发快速入门教程

    游戏控制杆OUYA游戏开发快速入门教程 1.2.2  游戏控制杆 游戏控制杆各个角度的视图,如图1-4所示,它的硬件规格是本文选自OUYA游戏开发快速入门教程大学霸: 图1-4  游戏控制杆各个角度的 ...

  9. SpringBoot开发快速入门

    SpringBoot开发快速入门 目录 一.Spring Boot 入门 1.Spring Boot 简介 2.微服务 3.环境准备 1.maven设置: 2.IDEA设置 4.Spring Boot ...

随机推荐

  1. Java IO系统--RandomAccessFile

    RandomAccessFile 实现了DataOutput接口和DataInput接口.父类是Object,不继承任何的InputStream和OutStram. public class Rand ...

  2. WIN7在计算机管理中修改用户名之后 文件夹名字不变怎么办?

      WIN7在计算机管理中修改用户名之后 文件夹名字不变?   可以新建一个管理员账户,起名字,然后删除旧的账户.保留原用户文件夹中的内容,以备不时之需.不要在控制面板的用户账户中修改用户名,修改之后 ...

  3. openstack kvm cannot set up guest memory 'pc.ram': Cannot allocate memory

    Kvm:启动报错:error: internal error: process exited while connecting to monitor: 2018-11-12T01:47:14.9933 ...

  4. Apollo简介及项目集成

    1. 产生背景 随着程序功能的日益复杂,程序的配置日益增多:各种功能的开关.参数的配置.服务器的地址…… 对程序配置的期望值也越来越高:配置修改后实时生效,灰度发布,分环境.分集群管理配置,完善的权限 ...

  5. naxsi-waf-with-ui :dockerfile

    scollazo/naxsi-waf-with-ui Dockerfile - Docker Hubhttps://hub.docker.com/r/scollazo/naxsi-waf-with-u ...

  6. springMVC Controller 参数映射

    springMVC 对参数为null或参数不为null的处理 - 小浩子的博客 - CSDN博客https://blog.csdn.net/change_on/article/details/7664 ...

  7. JEECG Hibernate 自动更新 持久化

    Hibernate不调用update却自动更新 - 七郎 - 博客园http://www.cnblogs.com/yangy608/p/4073941.html hibernate自动更新持久化类的问 ...

  8. linux内核在挂载ramdisk的过程中报错"RAMDISK: incomplete write (10739 != 32768)"如何处理?

    1. 原因 ramdisk大小不够 2. 解决方法 在启动变量bootargs中添加参数"ramdisk_size=10000000"即可

  9. iptables nat表配置

  10. python接入微博第三方API之1环境准备

    环境准备: 1.注册微博账号 2.注册应用