大疆视频回传流媒体开发
最近,去了广东省电力设计院做实习生。外聘的那个项目经理让我一个实习生做流媒体这一块。。我也是醉。没办法,硬着头皮上呗。还好找到了一个开源的流媒体解决方案。改改他们的代码。就能用了。但是大疆回传的视频流还是很坑爹的。
首先。大疆的SDK使用要用USB连着他那个手柄。我了个大艹。连着这个手柄我就不能逐步调试了啊。简直设计脑残。嗯。吐槽完了。
1.现在大疆SDK经过更新。这个回传的videoBuffer的开头就有数据。数据不会再分隔在两个videoBuffer中了。
2.数据没有 0x00 0x00 0x00 0x01 0x09的开头了。一上来就是数据。
3.数据后边有大量冗余。全是0x00。只用读出他给的大小就好。就是那个Size参数
4.读出无冗余的数据后,把他官网说的I帧(raw文件夹下的),拼在这些数据前面,就完成了
5.之后你拿到这个数据,该推送到RTP就推送,该解码就解码。
6.不得不说大疆还是很坑爹的。你更新了数据格式也不说。又不能逐步调试。
嗯。时间过去了三年,DJI SDK也不知道更新了多少版。我也从一个Android实习改做后端,现在又改做架构。
刚刚查看了一下,现在获取I帧的方式与以前不同了,也许是DJI的机型增多,变焦的也出了,不能手动下载I帧文件了。
可以看他的Github
现在对于解码的描述在这个文件里:DJIVideoStreamDecoder.java
以下是对步骤说明的一些摘抄:
/**
* This class is a helper class for hardware decoding. Please follow the following steps to use it:
*
* 1. Initialize and set the instance as a listener of NativeDataListener to receive the frame data.
*
* 2. Send the raw data from camera to ffmpeg for frame parsing.
*
* 3. Get the parsed frame data from ffmpeg parsing frame callback and cache the parsed framed data into the frameQueue.
*
* 4. Initialize the MediaCodec as a decoder and then check whether there is any i-frame in the MediaCodec. If not, get
* the default i-frame from sdk resource and insert it at the head of frameQueue. Then dequeue the framed data from the
* frameQueue and feed it(which is Byte buffer) into the MediaCodec.
*
* 5. Get the output byte buffer from MediaCodec, if a surface(Video Previewing View) is configured in the MediaCodec,
* the output byte buffer is only need to be released. If not, the output yuv data should invoke the callback and pass
* it out to external listener, it should also be released too.
*
* 6. Release the ffmpeg and the MediaCodec, stop the decoding thread.
*/
现在的I帧被内置在了SDK的raw文件里,刚刚给出的类里现在有个getIframeRawId函数,是直接从SDK里获取I帧的,使用一个一个的Switch-Case和If判别R.raw的ID,得到资源ID。
/**
* Get the resource ID of the IDR frame.
* @param pModel Product model of connecting DJI product.
* @param width Width of current video stream.
* @return Resource ID of the IDR frame
*/
public int getIframeRawId(Model pModel, int width) {
int iframeId = R.raw.iframe_1280x720_ins;
switch(pModel) {
case PHANTOM_3_ADVANCED:
case PHANTOM_3_STANDARD:
if (width == 960) {
//for photo mode, 960x720, GDR
iframeId = R.raw.iframe_960x720_3s;
} else if (width == 640){
iframeId = R.raw.iframe_640x368_osmo_gop;
} else {
//for record mode, 1280x720, GDR
iframeId = R.raw.iframe_1280x720_3s;
}
break;
case INSPIRE_1: {
DataCameraGetPushStateInfo.CameraType cameraType = DataCameraGetPushStateInfo.getInstance().getCameraType();
if (cameraType == DataCameraGetPushStateInfo.CameraType.DJICameraTypeCV600) {
//ZENMUSE_Z3
if (width == 960) {
//for photo mode, 960x720, GDR
iframeId = R.raw.iframe_960x720_3s;
} else if (width == 640){
iframeId = R.raw.iframe_640x368_osmo_gop;
} else {
//for record mode, 1280x720, GDR
iframeId = R.raw.iframe_1280x720_3s;
}
}
break;
}
case Phantom_3_4K:
switch(width) {
case 640:
//for P3-4K with resolution 640*480
iframeId = R.raw.iframe_640x480;
break;
case 848:
//for P3-4K with resolution 848*480
iframeId = R.raw.iframe_848x480;
break;
case 896:
iframeId = R.raw.iframe_896x480;
break;
case 960:
//DJILog.i(TAG, "Selected Iframe=iframe_960x720_3s");
//for photo mode, 960x720, GDR
iframeId = R.raw.iframe_960x720_3s;
break;
default:
iframeId = R.raw.iframe_1280x720_3s;
break;
}
break