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. Oracle导出/导入数据库的三种模式

    导出 模式一:全量导出(慎用) exp 用户名/密码@数据库实例 owner=用户名 file=文件存储路径 log=日志存储路径 full=y 栗子:exp Mark/123456@151.2.*. ...

  2. node依赖包格式区别

    UMD:UMD 版本可以通过 <script> 标签直接用在浏览器中.jsDelivr CDN 的 https://cdn.jsdelivr.net/npm/vue 默认文件就是运行时 + ...

  3. Eclipse 搭建Struts2

    Eclipse版本 Mars Release (4.5.0) Struts版本 struts-2.5.20 下载地址:https://struts.apache.org/download.cgi#st ...

  4. python 处理geoJson to shp 互转

  5. Comparison and difference for Default geodatabase,Current workspace,Scratch workspace,Home Folder

    Comparison and difference for Default geodatabase,Current workspace,Scratch workspace,Home Folder 商务 ...

  6. Ubuntu 1604 安装配置 kafka,并配置开机自启(systemctl)

    安装 kafka 需要先安装 jdk.一.下载官网:http://kafka.apache.org/downloads.html 二.安装 安装参考:https://segmentfault.com/ ...

  7. YII2 输出 执行的 SQL 语句,直接用程序输出

    $query = User::find() ->,,,]) ->select(['username']) // 输出SQL语句 $commandQuery = clone $query; ...

  8. leetcode 139. Word Break 、140. Word Break II

    139. Word Break 字符串能否通过划分成词典中的一个或多个单词. 使用动态规划,dp[i]表示当前以第i个位置(在字符串中实际上是i-1)结尾的字符串能否划分成词典中的单词. j表示的是以 ...

  9. idea 报错javax/xml/bind/DatatypeConverter

    idea 报错javax/xml/bind/DatatypeConverter   java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeCon ...

  10. ISO/IEC 9899:2011 条款6.8.2——标签语句

    6.8.2 复合语句 语法 1.compound-statement: {    block-item-listopt    } block-item-list: block-item block-i ...