M300挂载相机,无法获取相机画面
Completed运行官方的示例demo,在M210上挂载一个相机,demo里面可以显示FPV以及挂载的相机的画面,一共两个,因为目前没有第二个相机,所以没有测试按钮left+right。但当把相机装到M300RTK无人机中之后,只能获取fpv的画面,另外一个画面显示未知。
之后我设置了相机源,LEFT_CAM、RIGHT_CAM、TOP_CAM三个显示的都是未知,只有FPV_CAM可以显示fpv画面。
-
我重新对M300和M210进行了测试(挂载同一个相机),M210可以显示相机画面以及FPV画面,由于只挂载了一个相机,所以选择左相机还是右相机的那些按钮都是被禁用的(没挂第三个相机,默认不能选?),视频源也能显示,一个是LEFT_CAM,一个是FPV_CAM。当M300挂载同样的相机时,显示不了相机源,而且画面是黑的,显示来源为UNKONWN,另外一个则可以显示FPV画面,显示来源FPV_CAM。我想问一下,官方示例demo(V4)的获取相机源是在哪里实现的啊?因为GimbalCapabilityView.java和VideoFeederView.java这两个类都有用到相机。调用相机的语句没找到。
-
![](https://djisdksupport.zendesk.com/attachments/token/r7tj7ZzyAsQVdc6eg3jBlcfqN/?name=image.png) 这里的含义是MSDK识别到camera ,camera的index为0,它是一个第三方相机,挂载在index为0的云台位置上。 因为你使用的是第三方相机,第三方相机在实现的时候可以选择输出的视频流格式是DJI H264还是CUS H264。两种格式在画面渲染上有所不同。 getPrimaryVideoFeed()和getSecondaryVideoFeed()可以获取到的是DJI H264。你可以参考这篇文章里的获取CUS H264视频流的方法能否获取到视频流数据:MSDK 4.14如何显示PSDK开发的相机的视频流? 获取到视频流之后将其送入到codecManager.sendDataToDecoder进行编解码,因为codecManager在初始化的时候已经传入了View,编解码之后的数据就会渲染到对应的view里。 -
我确认了一遍,视频流格式确实是CUS H264。但是有个疑问,为什么M210可以显示视频?我想先确认问题再进行解决。目前就是云台相机是CUS H264格式的,有两个飞机M210和M300以及他们各自的遥控器。我APP(demo)写在iPad上,用数据线连接遥控器。M210可以显示视频流,M300不能显示视频流。如果是解码有问题的话,为什么同一个demo,M210可以显示视频流啊? -->应该与M300上新增了数据传输通道有关。 M300为了传输新增了MOP通道,因此视频格式不同采用的传输数据通道也不同。如果是CUS H264建议都使用这篇文章方法:MSDK 4.14如何显示PSDK开发的相机的视频流? Demo中getPrimaryVideoFeed()和getSecondaryVideoFeed()获取到的是DJI H264。也有经过codecManager.sendDataToDecoder进行编解码吗? -->是的,获取到的视频流数据都会送到codecManager进行编解码。 -
无人机有FPV,如果直接重写了,是否会影响到FPV的画面获取?是否会影响到FPV的画面获取?我要在哪个地方重写?(初步设想是在PayloadSendGetDataActivity.java类中进行重写,这个可以吗?)。而且重写之后,不再使用getPrimaryVideoFeed()和getSecondaryVideoFeed()了吗?原来使用这两个方法函数获取视频流的要怎么改?
如果不在payload下改的话,是在这个地方改吗?
实在是不知道重写在哪里,因为我看官方的文档,setVideoDataReceivedCallback有三个重载,void sendDataToDecoder(byte[] videoBuffer, int size, VideoSource source)和void sendDataToDecoder(byte[] videoBuffer, int size, int source)我不知道他们的区别,以及他们是通过哪个参数来知道是获取的格式是DJI H264的还是CUS H264的。
-
1.getPrimaryVideoFeed和getSecondaryVideoFeed获取到的是主副视频源视频流。当你需要获取FPV画面,且FPV是主副视频流中的一个的时候需要使用到这两个接口获取流数据。M300需要配分视频源:M300开机后为什么不显示视频流? 2.sendDataToDecoder有三个重载函数,可以只传入videoBuffer和size。sendDataToDecoder不区分DJI H264还是CUS H264,都可以使用。 3.根据上面两点,videoFeedView是用于获取视频流数据进行编解码,因此基本上不需要改动。如果需改动可以将第三个参数省掉。 因为你使用的是第三方负载,需要在videoFeederView里修改获取视频源数据的逻辑,也就是这篇文章提到的内容:MSDK 4.14如何显示PSDK开发的相机的视频流? -
你介绍的那篇文字,实在太短了。我对照着Simple Demo找,发现VideoFeederView.java和VideoFeedView.java都没有用到payload的,不知道是不是写在了你们的SDK里面,所以,我实在不知道
payload.setVideoDataReceivedCallback(new Payload.VideoDataReceivedCallback() {
@Override
public void onVideoDataReceived(byte[] var1, int var2) {
if (mCodecManager != null) {
mCodecManager.sendDataToDecoder(var1, var2, DJICodecManager.VideoSource.SECONDARY_CAMERA);
}
}
});这个重写代码片段要在哪里实现才能用,而且哪怕写在了VideoFeederView.java里面了,如何使用啊?
-
目前问题就在这里,我做了一次判断,如果是DJI H264,则正常的用getPrimaryVideoFeed拿视频流数据,并使用registerLiveVideo函数。registerLiveVideo函数内会进行videoFeed.addVideoDataListener。
现在是如果不是DJI H264编码的,我要怎么进行拿数据?并且要添加videoFeed.addVideoDataListener。
如果依旧是使用原来的registerLiveVideo方法,第一个参数如何得到(DJI H264是用getPrimaryVideoFeed得到的),如果是用payload.setVideoDataReceivedCallback,如何能得到第一个参数的类型,以进行传参?
-
现在是如果不是DJI H264编码的,我要怎么进行拿数据?并且要添加videoFeed.addVideoDataListener。 -->M300获取视频流可以来自两种设备,一种是DJI云台相机,一种是第三方负载,第三方负载根据使用的视频格式分为DJI H254和CUS H264。DJI H264和DJI云台获取视频流使用的是同一种方法,也就是videoFeederView示例代码中的方法:VideoFeeder.getPrimaryVideoFeed再通过addVideoDataListener获取数据。 开发的时候需要判断你挂载的相机是camera还是payload,可以通过getCameras和Aircraft.getpayload获取到飞机当前挂载两种负载的列表,然后通过getIndex获取到负载的位置以判断负载,也可以通过负载名称进行判断。 如果是payload负载,此时可以通过isDJIVideoStreamingChannelAvailable判断是哪种类型。在确认是CUS H264之后,通过payload.setVideoDataReceivedCallback获取视频流数据。sample将注册DJI格式的视频流回调放置在了videoFeedView里,CUS H264需要另外处理。至于将处理CUS H264的代码放置在哪里需要由您自己决定。你可以在videoFeedView新增一个register方法用来实现cus H264的回调注册,逻辑可以参考原本的registerLiveVideo方法。 -
**payload.setVideoDataReceivedCallback。重写了方法,如何使用?这个函数获取视频流数据,是如何绑定监听的?** **改用payload.setVideoDataReceivedCallback,如何监听?** -->payload.setVideoDataReceivedCallback是用于传入回调的接口,你可以理解为setVideoDataReceivedCallback就是设置了相机视频流数据的监听。MSDK内部对外是透明的,您可以不需要关注具体实现。简单来说,您按照setVideoDataReceivedCallback要求传入对应的callblack就可以实现相机挂载在M300上被识别且成功运行之后获取到负载的视频流数据,如下: ![](https://djisdksupport.zendesk.com/attachments/token/bNu6MDWsMkt4R5sIZWFcWWmhh/?name=image.png) 参数定义: ![](https://djisdksupport.zendesk.com/attachments/token/XoWd8DrA7IBrb0dUOJyuTJT96/?name=image.png) **他是怎么和视图绑在一起的?他是怎么和视图绑在一起的?(换句话说,APP怎么知道执行这个方法函数后应该把视频显示在哪个视图上的?)** -->DJICodecManager作为编解码器,它绑定了视图,即下面红色箭头所指的参数。 ![](https://djisdksupport.zendesk.com/attachments/token/KwaiUcynyRzPnp3561Q27Hw97/?name=image.png) ![](https://djisdksupport.zendesk.com/attachments/token/14PN761lSRPR7fC5KXjRhUwhE/?name=image.png) ![](https://djisdksupport.zendesk.com/attachments/token/zJplqlori75roCFs7oP0DjR8F/?name=image.png) 您可以先利用sample和API文档学习一下如何使用MSDK的接口,API文档中会描述函数的作用以及传入参数的含义。API文档:https://developer.dji.com/api-reference/android-api/Components/SDKManager/DJISDKManager.html -
你说的后面那个我懂,就是前面的payload.setVideoDataReceivedCallback有问题。
您按照setVideoDataReceivedCallback要求传入对应的callblack就可以实现相机挂载在M300上被识别且成功运行之后获取到负载的视频流数据-->就是这一块有问题!上面那个是回调函数,但是我不知道哪里调用他,传入哪个对应的callblack。
目前我在V4的官方Demo。目前就是:我有两个VideoFeedView类型的变量。这个VideoFeedView类下有各自的View,并且绑定好了对应的(不用考虑)。同时VideoFeedView类下同时也有codecManager,进行了
codecManager = new DJICodecManager(context,
surfaceHolder,
getWidth(),
getHeight(),
isPrimaryVideoFeed
? UsbAccessoryService.VideoStreamSource.Camera
: UsbAccessoryService.VideoStreamSource.Fpv)绑定,这些我知道。
现在我之所以不会,是因为不知道setVideoDataReceivedCallback是需要在哪里调用的,传入什么样的参数。
我初步设想是
payload.setVideoDataReceivedCallback(new Payload.VideoDataReceivedCallback() {
@Override
public void onVideoDataReceived(byte[] var1, int var2) {
if (codecManager != null) {
codecManager.sendDataToDecoder(var1, var2, DJICodecManager.VideoSource.CAMERA);
}
}
})将这个方法写在VideoFeedView.java这个类下,因为这样拿的codecManager就是已经绑定好视图的codecManager。写完这个方法,它并不会自动调用啊,需要我们去调用吧,还有就是解码完,总有监听吧?
像示例写的那个:
public VideoFeeder.VideoDataListener registerLiveVideo(VideoFeeder.VideoFeed videoFeed, boolean isPrimary) {
isPrimaryVideoFeed = isPrimary;
if (videoDataListener != null && videoFeed != null && !videoFeed.getListeners().contains(videoDataListener)) {
videoFeed.addVideoDataListener(videoDataListener);
return videoDataListener;
}
return null;
}本来我也打算仿写,但是没有第一个参数VideoFeeder.VideoFeed videoFeed,因为这个参数刚刚好就是VideoFeeder.getPrimaryVideoFeed拿到的,而我们使用的是setVideoDataReceivedCallback。我拿不到任何一个像VideoFeeder.VideoFeed类型的变量,来添加监听。
-
还是说
payload.setVideoDataReceivedCallback(new Payload.VideoDataReceivedCallback() {
@Override
public void onVideoDataReceived(byte[] var1, int var2) {
if (codecManager != null) {
codecManager.sendDataToDecoder(var1, var2, DJICodecManager.VideoSource.CAMERA);
}
}
})这段函数写在VideoFeedView.java这个类下就行了,我们并不需要去调用它,它自己就能发挥作用了?
我们只需要在我们模仿registerLiveVideo写一个类似的方法,依旧用VideoFeeder.getPrimaryVideoFeed拿到protected VideoFeed primaryVideoFeed变量,然后addVideoDataListener就可以了?
-
我应该是解决了编码的问题了。但是好像产生了通道占用?就是不加下面这段代码时:
payload.setVideoDataReceivedCallback(new Payload.VideoDataReceivedCallback() {
@Override
public void onVideoDataReceived(byte[] var1, int var2) {
if (codecManager != null) {
codecManager.sendDataToDecoder(var1, var2, DJICodecManager.VideoSource.CAMERA);
}
}
})第三方负载相机确实没画面。在我加了这段代码之后,我测试过了,会有一瞬间的画面,然后就被fpv的画面占用了。如果不加上面那段代码,那一瞬间的画面都没有,加了之后,有一瞬间的画面,之后就迅速被fpv画面挤占,而第二个画面一直是黑屏的。
-
这是VideoFeederView.java上的关键代码。
因为我固定知道挂载相机是CUS H264编码的,FPV是DJI H264,所以就没判断了,而是一个用CUSregisterLiveVideo函数,一个用registerLiveVideo
private void setVideoFeederListeners(boolean isOpen) {
if (VideoFeeder.getInstance() == null) return;
final BaseProduct product = DJISDKManager.getInstance().getProduct();
updateM210SeriesButtons();
updateM300Buttons();
if (product != null) {
payload=product.getPayload();
VideoFeeder.VideoDataListener primaryVideoDataListener = primaryVideoFeed.CUSregisterLiveVideo(VideoFeeder.getInstance().getPrimaryVideoFeed(), true);
VideoFeeder.VideoDataListener secondaryVideoDataListener = fpvVideoFeed.registerLiveVideo(VideoFeeder.getInstance().getSecondaryVideoFeed(), false);
if (isOpen) {
String newText =
"Primary Source: " + VideoFeeder.getInstance().getPrimaryVideoFeed().getVideoSource().name();
ToastUtils.setResultToText(primaryVideoFeedTitle, newText);
if (Helper.isMultiStreamPlatform()) {
String newTextFpv = "Secondary Source: " + VideoFeeder.getInstance()
.getSecondaryVideoFeed()
.getVideoSource()
.name();
ToastUtils.setResultToText(fpvVideoFeedTitle, newTextFpv);
}
VideoFeeder.getInstance().addPhysicalSourceListener(sourceListener);
} else {
VideoFeeder.getInstance().removePhysicalSourceListener(sourceListener);
VideoFeeder.getInstance().getPrimaryVideoFeed().removeVideoDataListener(primaryVideoDataListener);
if (Helper.isMultiStreamPlatform()) {
VideoFeeder.getInstance()
.getSecondaryVideoFeed()
.removeVideoDataListener(secondaryVideoDataListener);
}
}
}
}这是VideoFeedView.java上关键代码:
public VideoFeeder.VideoDataListener registerLiveVideo(VideoFeeder.VideoFeed videoFeed, boolean isPrimary) {
isPrimaryVideoFeed = isPrimary;
if (videoDataListener != null && videoFeed != null && !videoFeed.getListeners().contains(videoDataListener)) {
videoFeed.addVideoDataListener(videoDataListener);
return videoDataListener;
}
return null;
}
//修改3
public VideoFeeder.VideoDataListener CUSregisterLiveVideo(VideoFeeder.VideoFeed videoFeed, boolean isPrimary) {
isPrimaryVideoFeed = isPrimary;
if(ModuleVerificationUtil.isPayloadAvailable()){
payload = DJISampleApplication.getAircraftInstance().getPayload();
payload.setVideoDataReceivedCallback(new Payload.VideoDataReceivedCallback() {
@Override
public void onVideoDataReceived(byte[] var1, int var2) {
if (codecManager != null) {
//修改2
codecManager.sendDataToDecoder(var1, var2, DJICodecManager.VideoSource.CAMERA);
}
}
});
}
if (videoDataListener != null && videoFeed != null && !videoFeed.getListeners().contains(videoDataListener)) {
videoFeed.addVideoDataListener(videoDataListener);
return videoDataListener;
}
return null;
}
Please sign in to leave a comment.
Comments
48 comments