关于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左右,感觉!)

Hardware Video Encoding on iPhone — RTSP Server example On iOS, the only way to use hardware acceleration when encoding video is to use AVAssetWriter, and that means writing the compressed video to file. If you want to stream that video over the network, for example, it needs to be read back out of the file. I’ve written an example application that demonstrates how to do this, as part of an RTSP server that streams H264 video from the iPhone or iPad camera to remote clients. The end-to-end latency, measured using a low-latency DirectShow client, is under a second. Latency with VLC and QuickTime playback is a few seconds, since these clients buffer somewhat more data at the client side. The whole example app is available in source form here under an attribution license. It’s a very basic app, but is fully functional. Build and run the app on an iPhone or iPad, then use Quicktime Player or VLC to play back the URL that is displayed in the app. Details, Details When the compressed video data is written to a MOV or MP4 file, it is written to an mdat atom and indexed in the moov atom. However, the moov atom is not written out until the file is closed, and without that index, the data in mdat is not easily accessible. There are no boundary markers or sub-atoms, just raw elementary stream. Moreover, the data in the mdat cannot be extracted or used without the data from the moov atom (specifically the lengthSize and SPS and PPS param sets). My example code takes the following approach to this problem: Only video is written using the AVAssetWriter instance, or it would be impossible to distinguish video from audio in the mdat atom. Initially, I create two AVAssetWriter instances. The first frame is written to both, and then one instance is closed. Once the moov atom has been written to that file, I parse the file and assume that the parameters apply to both instances, since the initial conditions were the same. Once I have the parameters, I use a dispatch_source object to trigger reads from the file whenever new data is written. The body of the mdat chunk consists of H264 NALUs, each preceded by a length field. Although the length of the mdat chunk is not known, we can safely assume that it will continue to the end of the file (until we finish the output file and the moov is added). For RTP delivery of the data, we group the NALUs into frames by parsing the NALU headers. Since there are no AUDs marking the frame boundaries, this requires looking at several different elements of the NALU header. Timestamps arrive with the uncompressed frames from the camera and are stored in a FIFO. These timestamps are applied to the compressed frames in the same order. Fortunately, the AVAssetWriter live encoder does not require re-ordering of frames. When the file gets too large, a new instance of AVAssetWriter is used, so that the old temporary file can be deleted. Transition code must then wait for the old instance to be closed so that the remaining NALUs can be read from the mdat atom without reading past the end of that atom into the subsequent metadata. Finally, the new file is opened and timestamps are adjusted. The resulting compressed output is seamless. A little experimentation suggests that we are able to read compressed frames from file about 500ms or so after they are captured, and these frames then arrive around 200ms after that at the client app. Rotation For modern graphics hardware, it is very straightforward to rotate an image when displaying it, and this is the method used by AVFoundation to handle rotation of the camera. The buffers are captured, encoded and written to file in landscape orientation. If the device is rotated to portrait mode, a transform matrix is written out to the file to indicate that the video should be rotated for playback. At the same time, the preview layer is also rotated to match the device orientation. This is efficient and works in most cases. However, there isn’t a way to pass this transform matrix to an RTP client, so the view on a remote player will not match the preview on the device if it is rotated away from the base camera orientation. The solution is to rotate the pixel buffers after receiving them from the capture output and before delivering them to the encoder. There is a cost to this processing, and this example code does not include this extra step.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值