关于IMX6Dl 芯片使用硬编解码的问题记录
NXP出了IMX6系列芯片(现在应该是9系列最新了),性能相当于我们嵌入式行业,已经非常优秀(可以做很多事情)
同时,IMX6DL IMX6Q 自带vpu硬编解码,用来处理下视频也是非常不错的,对于我们来说的确非常合适
众所周知,mxc_vpu_test.out 是官方提供的测试程序,用于测试各种功能,源码也提供,
但是系统里面源码是跟其他测试程序整合在一起的,往往我们想要在此基础上改改弄弄,然后重新编译是件困难的事情(大神除外)
当然,也可以从网上下载 mxc_vpu_test.out 的源码,貌似也有(csdn就有),但可能跟你板子的库文件版本不一致,会导致各种问题
总之这是一个绕不过去的坑(编译过不算,正常运行编解码才算)
以上问题,困扰很久,后来版本搞对了,才算顺利过去。 ---- 记一下,如果用测试程序 编译没有问题,但测试解码时候,出现段错误,往往是你所用h文件跟板子so库用的h文件版本不一样,也就是某些结构体size不一样。
另外一个问题:
假设在同一块板子上,先采集USB摄像头的数据,然后vpu编码产生h264流,然后通过“管道”发送给解码线程,解码线程收到h264流,然后调用vpu的解码,然后再显示。当然这个步骤是异步的,两个线程(进程),中间涉及到数据交互的。
源代码是参考 mxc_vpu_test.out 的源码
ps: 说到这里,可能有些人会说,既然是一块板子,那么直接显示USB摄像头上的数据,不是更好的?嗯,我举的例子,的确这样做更好,但不要忘记现实的项目可能是这样的需求:在本终端上显示的画面是其他终端传过来的h264数据,而本终端需要将自己的视频流传输给对方(就好比视频对讲),这样就需要一台板子上同时编解码(当然此时用的通道肯定是两个)
貌似应该没有问题,也说得通,只要编解码本身没有问题,那么问题就没有。但现实往往是骨感的,的确是碰到问题
一开始怀疑管道写错了,但管道的确没有问题
通过步骤printf 的方式,发现卡在:
也就是说 开始编码的时候,就不再返回。
分析了dec.c 的源码:
dec这边最后一步是出现这里:
dec_fill_bsbuffer 实际上是去读取管道数据(由于是阻塞式管道,不阻塞不行,不阻塞的话,这里返回是0,那么dec这边会认为“没有数据” 自然退出)。此时卡住了。
原因:
解码这边,在读取管道数据之前,先调用 vpu_DecStartOneFrame ,这个函数会占据vpu资源,
导致解码那边调用 vpu_EncStartOneFrame 函数卡死(因为获取不到vpu资源,从而也没有往下走)
这样就形成了“死锁”,编码这边等待vpu资源释放(才能有数据往管道里放),解码这边等待管道里有数据(才能完成解码,从而才能释放vpu资源)
也许,把 vpu_DecStartOneFrame 放在 dec_fill_bsbuffer 之后,是否可行?也不行,只是概率问题。
从现象来看,我们也知道同一块板子如果同时 vpu_DecStartOneFrame vpu_EncStartOneFrame是不行的
当然唯一的办法就是错时。
我的办法也很简单,利用一个缓冲,错时,如果缓冲里没有数据,解码这边就不调用 vpu_DecStartOneFrame,线程等待就好了。
当然这个缓冲也有点小复杂,不是简简单单加个锁就行了,还是要考虑到各种情况的。
下面亮出我的代码:
1 static char *m_pPipeBuf = NULL; 2 static int m_nNowPos = 0; 3 static int m_nGetPos = 0; 4 static pthread_mutex_t m_lock; 5 6 //建立缓存区 7 void Safe_InitBuff() 8 { 9 if(m_pPipeBuf != NULL) 10 Safe_UnInitBuff(); 11 12 pthread_mutex_init(&m_lock,NULL); 13 m_pPipeBuf = (char*)malloc(PIPE_LENTH + 1024); 14 m_nNowPos = 0; 15 m_nGetPos = 0; 16 } 17 18 void Safe_UnInitBuff() 19 { 20 if(m_pPipeBuf != NULL) 21 { 22 free(m_pPipeBuf); 23 m_pPipeBuf = NULL; 24 25 pthread_mutex_destroy(&m_lock); 26 m_nNowPos = 0; 27 m_nGetPos = 0; 28 } 29 } 30 31 //剩余可以存放 多少空间 32 static int GetLastTemp() 33 { 34 int nLast = 0; 35 36 if(m_nNowPos >= m_nGetPos) 37 { 38 nLast = PIPE_LENTH - (m_nNowPos - m_nGetPos); 39 } 40 else 41 { 42 43 } 44 return nLast; 45 } 46 47 static int AddToPipeBuf(void *pBuf,int nLen) 48 { 49 int nRes = -1; 50 if(m_pPipeBuf != NULL) 51 { 52 int nPos = m_nNowPos % PIPE_LENTH; 53 if ((nPos + nLen) > PIPE_LENTH) 54 { 55 int nCpLen = (PIPE_LENTH - nPos); 56 memcpy(&m_pPipeBuf[nPos],pBuf,nCpLen); 57 58 nPos = 0; 59 memcpy(&m_pPipeBuf[0],&pBuf[nCpLen],nLen - nCpLen); 60 } 61 else 62 { 63 memcpy(&m_pPipeBuf[nPos],pBuf,nLen); 64 } 65 66 m_nNowPos = m_nNowPos + nLen; 67 nRes = nLen; 68 } 69 70 return nRes; 71 } 72 73 static int GetSavedLen() 74 { 75 int nLen = 0; 76 if (m_nNowPos == m_nGetPos) 77 { 78 nLen = 0; 79 } 80 else if(m_nNowPos > m_nGetPos) 81 { 82 nLen = m_nNowPos - m_nGetPos; 83 } 84 else 85 { 86 //异常,不存在 87 } 88 89 return nLen; 90 } 91 92 static int GetFromPipeBuf(void *pBuf,int nLen) 93 { 94 int nPos = (m_nGetPos % PIPE_LENTH); 95 int nRes = -1; 96 97 if (nLen > 0) 98 { 99 if ((nPos + nLen) > PIPE_LENTH) 100 { 101 int nCpLen = PIPE_LENTH - nPos; 102 //先拷贝末尾 103 memcpy(pBuf,&m_pPipeBuf[nPos],nCpLen); 104 nPos = 0; 105 memcpy(&pBuf[nCpLen], &m_pPipeBuf[0], nLen - nCpLen); 106 } 107 else 108 { 109 memcpy(pBuf,&m_pPipeBuf[nPos],nLen); 110 } 111 112 nRes = nLen; 113 m_nGetPos = m_nGetPos + nLen; 114 } 115 116 return nRes; 117 } 118 119 //添加数据(如果数据已满,等待) 120 int Safe_AddBuf(char *pBuf,int nLen) 121 { 122 int nRes = -1; 123 if(((nLen + 4) > PIPE_LENTH) || (m_pPipeBuf == NULL)) 124 { 125 return nRes; 126 } 127 128 int bIsSaved = 0; 129 while (bIsSaved == 0) 130 { 131 pthread_mutex_lock(&m_lock); 132 int nLast = GetLastTemp(); 133 134 if(nLast >= (nLen + 4)) 135 { 136 char strNUM[5]; 137 memcpy(strNUM, &nLen, 4); 138 AddToPipeBuf(strNUM, 4); 139 140 nRes = AddToPipeBuf(pBuf, nLen); 141 bIsSaved = 1; 142 } 143 144 pthread_mutex_unlock(&m_lock); 145 146 if(bIsSaved == 0) 147 { 148 //< delay 10ms .continue waited 149 printf("Safe_SaveBuf: delay 10ms .continue waited\n"); 150 usleep(10000); 151 } 152 } 153 154 return nRes; 155 } 156 157 //提取数据(如果没有数据,等待) 158 int Safe_GetBuf(char *pBuf,int nLen) 159 { 160 static int lastReadAll = 1; ///< 取完 161 static int nLastRead = 0; ///< 还有多少个没读完 162 163 int nRes = -1; 164 if((nLen <= 0) || (m_pPipeBuf == NULL)) 165 { 166 return 0; 167 } 168 169 int bIsGet = 0; 170 while (bIsGet == 0) 171 { 172 pthread_mutex_lock(&m_lock); 173 int nSaved = GetSavedLen(); 174 int nframe_length = 0; 175 176 if(nSaved > 0) 177 { 178 if(lastReadAll == 1) 179 { 180 if(nSaved > 4) 181 { 182 GetFromPipeBuf(&nframe_length,4); 183 184 if(nframe_length > nLen) 185 { 186 lastReadAll = 0; 187 188 nLastRead = nframe_length - nLen; 189 nframe_length = nLen; 190 //外部存储 不够(先不管,除非长度写错了) 191 } 192 else 193 { 194 //取走这帧 195 lastReadAll = 1; 196 nLastRead = 0; 197 } 198 } 199 else 200 { 201 //错误--- 202 } 203 } 204 else 205 { 206 //继续- 207 if(nLen >= nLastRead) 208 { 209 //这次空间够了 210 if(nSaved >= nLastRead) 211 { 212 //把上次未取走的 全部取走 213 nframe_length = nLastRead; 214 nLastRead = 0; 215 lastReadAll = 1; 216 } 217 else 218 { 219 nframe_length = nSaved; 220 nLastRead = nLastRead - nSaved; 221 lastReadAll = 0; 222 } 223 } 224 else 225 { 226 //空间不够 nLastRead 227 if(nSaved >= nLen) 228 { 229 nframe_length = nLen; 230 nLastRead = nLastRead - nLen; 231 lastReadAll = 0; 232 } 233 else 234 { 235 nframe_length = nSaved; 236 nLastRead = nLastRead - nSaved; 237 lastReadAll = 0; 238 } 239 } 240 } 241 242 if(nframe_length > 0) 243 { 244 nRes = GetFromPipeBuf(pBuf, nframe_length); 245 bIsGet = 1; 246 } 247 } 248 249 pthread_mutex_unlock(&m_lock); 250 251 if(bIsGet == 0) 252 { 253 //< delay 100ms .continue waited 254 printf("Safe_GetBuf: delay 10ms .continue waited\n"); 255 usleep(10000); 256 } 257 } 258 return nRes; 259 } 260 261 int Safe_IsHaveData() 262 { 263 int nSaved = 0; 264 pthread_mutex_lock(&m_lock); 265 nSaved = GetSavedLen(); 266 pthread_mutex_unlock(&m_lock); 267 return nSaved; 268 }
题外话,问题也解决了,效率非常高,cpu仅占3%~5%左右,性能上也很强劲,这款cpu不错
很流畅,延时很少(100ms~200ms左右,感觉!)