技术标签: app java android 大疆MSDK开发
配置代码运行环境
(1)下载Moblie SDK:DJI MSDK下载。
(2)准备Android开发环境,推荐使用Android Studio 2.1.1以上版本。官网下载网址:Download Android Studio或者 Android Studio
从GitHub上下载Android demo project:Mobile-SDK-Android 。
在大疆官网创建APP KEY。在大疆官网的个人中心的应用一栏中可以生成APP KEY
在创建界面需要注意的是Package Name中需要填写的是实际代码程序中的包名,其余选项随意填写
点击run运行程序。
手机连接无人机遥控器测试APP.
在大疆官网文档中详细的介绍了如何完成一个对无人机视频流进行解码:Android Video Stream Decoding Sample - DJI Mobile SDK Documentation.
1.初始化一个NativeHelper的实例对象,来监听来自无人机高空的视频数据。
2.将原始的H.264视频数据送入FFmpeg中解析。
3.将解析完成的视频数据从FFmpeg中取出,并将解析后的数据缓存到图像帧序列中
4.将MediaCodec作为一个解码器,然后对视频中的I帧进行捕获。
5.解码完成后,可为MediaCodec的输出数据配置一个TextureView或SurfaceView用来对视频画面进行预览,或者调用监听器对解码数据进行监听完成其他操作。
6.释放FFmpeg和MediaCodec资源。
通过上述流程我们能够知道要能够在设备上显示无人机相机画面大体上分为三步:接受无人机视频流、对视频流解码以及把解码后数据输出画面。那么为什么要把无人机传输过来的视频解码为YUV格式呢?
原因是在Android系统中,图像是以YUVImage的格式传递的,因此,在存储数据的时候我们就需要使用YUV图像格式。
我们可以下载官网给出的 sample project:Github Page ,在项目代码中大疆给出了两种进行解码的方式:利用大疆自己编写的DJIViedeoStreamDecoder解码器以及利用之前大疆就编写好的DJICodecManager解码器。在布局中分别在livestream_preview_ttv和livestream_preview_sf中显示画面。
在代码meida的DJIVideoStreamDecoder.java文件中我们可以看到初始化解码器MediaCodec的源代码:
/**
* Initialize the hardware decoder.
*/
private void initCodec() {
if (width == 0 || height == 0) {
return;
}
if (codec != null) {
releaseCodec();
}
loge("initVideoDecoder----------------------------------------------------------");
loge("initVideoDecoder video width = " + width + " height = " + height);
// create the media format
MediaFormat format = MediaFormat.createVideoFormat(VIDEO_ENCODING_FORMAT, width, height);
if (surface == null) {
logd("initVideoDecoder: yuv output");
// The surface is null, which means that the yuv data is needed, so the color format should
// be set to YUV420.
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
} else {
logd("initVideoDecoder: display");
// The surface is set, so the color format should be set to format surface.
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
}
try {
// Create the codec instance.
codec = MediaCodec.createDecoderByType(VIDEO_ENCODING_FORMAT);
logd( "initVideoDecoder create: " + (codec == null));
// Configure the codec. What should be noted here is that the hardware decoder would not
// output any yuv data if a surface is configured into, which mean that if you want the yuv
// frames, you should set "null" surface when calling the "configure" method of MediaCodec.
//就是说要配置解码器之前要把surface设为"null"
codec.configure(format, surface, null, 0);
logd( "initVideoDecoder configure");
// codec.configure(format, null, null, 0);
if (codec == null) {
loge("Can't find video info!");
return;
}
// Start the codec
codec.start();
} catch (Exception e) {
loge("init codec failed, do it again: " + e);
e.printStackTrace();
}
}
下面是解析为YUV格式(MainActivity.java中)的源代码:
@Override
public void onYuvDataReceived(MediaFormat format, final ByteBuffer yuvFrame, int dataSize, final int width, final int height) {
//In this demo, we test the YUV data by saving it into JPG files.
//DJILog.d(TAG, "onYuvDataReceived " + dataSize);
if (count++ % 30 == 0 && yuvFrame != null) {
final byte[] bytes = new byte[dataSize];
yuvFrame.get(bytes);
//DJILog.d(TAG, "onYuvDataReceived2 " + dataSize);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
// two samples here, it may has other color format.
//两种像素格式 NV12或者YUV420P
int colorFormat = format.getInteger(MediaFormat.KEY_COLOR_FORMAT);
switch (colorFormat) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
//NV12
if (Build.VERSION.SDK_INT <= 23) {
//android API<=23
oldSaveYuvDataToJPEG(bytes, width, height);
} else {
newSaveYuvDataToJPEG(bytes, width, height);
}
break;
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
//YUV420P
newSaveYuvDataToJPEG420P(bytes, width, height);
break;
default:
break;
}
}
});
}
}
代码如果有不明白的方法调用或者类名,可以去大疆官网查询相关的API。在本次开发过程中我选择了相对更加稳定和延迟更小的livestream_preview_sf.
初步理解代码内容后,我们可以Run示例程序查看结果。
本步骤参考DJI MSDK开发文档中的Simulator 模拟器功能实现部分,代码以及文件参考: Github Page.
在能显示无人机相机视角的基础上,我们现在的目标是添加飞机的自动起飞和降落以及能操控无人机的虚拟摇杆的功能。
首先自动起飞和自动降落功能在大疆API已经有了,我们只需要很简单的调用以及在Android布局中增加两个按钮即可。
对于按钮点击事件进行判断如下:
//无人机起飞动作实现
case R.id.btn_take_off:
if (mFlightController != null){
mFlightController.startTakeoff(
new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (djiError != null) {
showToast(djiError.getDescription());
} else {
showToast("Take off Success");
}
}
}
);
}
break;
//无人机降落动作实现
case R.id.btn_land:
if (mFlightController != null){
mFlightController.startLanding(
djiError -> {
if (djiError != null) {
showToast(djiError.getDescription());
} else {
showToast("Start Landing");
}
}
);
}
我们第二步是增加两个虚拟摇杆,而为了操控杆控件,我们基于OnScreenJoystick文件实施操纵杆控制 。我们主要用到两个文件OnScreenJoystick.java和OnScreenJoystickListener.java.
在OnScreenJoystickListener.java中看到当我们触摸到虚拟摇杆视图时会触发的监听方法onTouch.
public interface OnScreenJoystickListener {
/** Called when the joystick is touched.
* @param joystick The joystick which has been touched.
* @param pX The x coordinate of the knob. Values are between -1 (left) and 1 (right).
* @param pY The y coordinate of the knob. Values are between -1 (down) and 1 (up).
*/
public void onTouch(final OnScreenJoystick joystick, final float pX, final float pY);
}
方法具体的实现代码如下:
@Override
public boolean onTouch(final View arg0, final MotionEvent pEvent) {
final float x = pEvent.getX();
final float y = pEvent.getY();
switch (pEvent.getAction()) {
//返回动作类型
case MotionEvent.ACTION_UP: //手指离开摇杆时触发
if (isAutoCentering()) {
mKnobX = Math.round((mBackgroundSize - mKnobSize) * 0.5f);
mKnobY = Math.round((mBackgroundSize - mKnobSize) * 0.5f);
}
break;
default:
// Check if coordinates are in bounds. If they aren't move the knob
// to the closest coordinate inbounds.
if (checkBounds(x, y)) {
mKnobX = Math.round(x - mKnobSize * 0.5f);
mKnobY = Math.round(y - mKnobSize * 0.5f);
} else {
final double angle = Math.atan2(y - mRadius, x - mRadius);
mKnobX = (int) (Math.round(mRadius
+ (mRadius - mKnobSize * 0.5f) * Math.cos(angle)) - mKnobSize * 0.5f);
mKnobY = (int) (Math.round(mRadius
+ (mRadius - mKnobSize * 0.5f) * Math.sin(angle)) - mKnobSize * 0.5f);
}
}
if (mJoystickListener != null) {
mJoystickListener.onTouch(this,
(0.5f - (mKnobX / (mRadius * 2 - mKnobSize))) * -2,
(0.5f - (mKnobY / (mRadius * 2 - mKnobSize))) * 2);
}
return true;
}
然后把需要配置文件添加到res中,在大疆文档实现UI界面的第四步配置资源中已经描述的非常清楚了,此处把内容复制在下面:
<color name="black_overlay">#66000000</color>
<string name="success">Success</string>
<!-- Common button style -->
<style name="common_button">
<item name="android:layout_width">100dp</item>
<item name="android:layout_height">45dp</item>
<item name="android:layout_marginTop">10dp</item>
<item name="android:background">@drawable/round_btn</item>
<item name="android:paddingLeft">5dp</item>
<item name="android:paddingRight">5dp</item>
<item name="android:textAllCaps">false</item>
<item name="android:textColor">@android:color/white</item>
<item name="android:textSize">14sp</item>
</style>
随后在布局中加入两个摇杆
<com.dji.simulatorDemo.OnScreenJoystick
android:id="@+id/directionJoystickLeft"
android:layout_width="130dp"
android:layout_height="130dp"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:background="@mipmap/joystick_bg"/>
<com.dji.simulatorDemo.OnScreenJoystick
android:id="@+id/directionJoystickRight"
android:layout_width="130dp"
android:layout_height="130dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:background="@mipmap/joystick_bg"/>
在MainActivity中我们初始化UI元素变量,重写onTouch()
和onClick()
方法。
确认根据文档步骤无误后,Run代码查看是否出现问题。
后面我又加入了拍照和录象的功能,因为在大疆文档中都有很详细的介绍所以此处不过多赘述。
最后做出来的UI界面如下:
本文只是非常基础的根据大疆官方文档所做的一个练习,供大家参考。
文章浏览阅读710次,点赞13次,收藏7次。它的名称有点不同 - aarch64-linux-android-addr2line。尽管该实用程序的名称不包含单词arm,但它成功地解密了arm64-v8下的堆栈跟踪,并通常感知arm64-v8的字符。是 NDK 自带的调试工具,可以用来分析 so 崩溃时输出的的内存地址。之后就是通过 cmd 进入到这个路径。找到了 64 位所需的实用程序。_arm-linux-androideabi
javaweb-邮件发送 摘要: 本文介绍了邮件传输协议(SMTP和POP3)以及电子邮件的发送和接收过程。还讨论了纯文本文件、带图片和附件的邮件发送方法,以及通过servlet方式注册邮箱和使用springboot框架发送邮件的实现。
文章浏览阅读4.3k次,点赞6次,收藏11次。在切换不同页面时(被 keep-alive 缓存的组件间切换),页面中的element-ui table的滚动条位置没有停留在原来的位置。目前需要切换不同的页面返回来后,滚动条保持在原来的位置。_element table 滚动条位置
文章浏览阅读2.6k次。 我设置nowrap和不设置nowrap效果都一样。就是表格随着文字自动伸展,为什么? →回答问题:TD元素noWrap属性的行为与TD元素的width属性有关。 ◆如果未设置TD宽度,则noWrap属性是起作用的。◆如果设置了TD宽度,则noWrap属性是不起作用的。 http://www.blueidea.com/tech/web/2003/943.as_属性值[session.getattribute("strpath")]引用["],在值内使用时必须被转义。
文章浏览阅读3.4k次,点赞2次,收藏3次。 前端时间在编写程序时遇到这样一个问题,即在前端页面通过一些js框架自带的异步请求返回的数据类型为Object数据类型,笔者根据网上查阅到的资料,找到以下这种简单的方式://把Object类型转为Json数据格式,再通过console命令在控制台中打印出来console.log("xhr的值为:"+JSON.st..._前端怎么通过控制台查看字段取值
文章浏览阅读556次。1. cc.Button添加按钮的方法 2种方式 (1)直接创建带Button组件的节点; (2) 先创建节点,再添加组件;按钮组件, 按钮是游戏中最常用的组件, 点击然后响应事件;按钮的过渡效果: 过渡: 普通状态, 鼠标滑动到物体上, 按下状态, 禁用状态 (1)没有过渡,只有响应事件; (2)颜色过渡, 过渡效果中使用颜色; (3)精灵..._cc button.start
文章浏览阅读245次。【PConline海选导购】晃眼间,秋风又起,让人振奋的开学季又要到来了!虽然说没有学习压力的暑假,在家着实悠哉,但想到回校后可以跟小伙伴们一起各种开黑吃鸡,是不是就感到很兴奋呢?说到“吃鸡”这种吃配置的游戏,就不得不说游戏本了,毕竟普通的笔记本电脑很难给我们带来畅快的游戏体验。而近年来游戏本市场俨然成为了各大厂商的必争之地,而随着开学季的到来,各大厂商更是推出了众多促销活动,下面就让我们一起来看..._计应专业游戏本
看到codepen里面有的按钮搞得很炫酷,但其实也不是很难,就学习记录一下。
文章浏览阅读408次。时间:2021-08-11编辑:hxh斗罗大陆魂师对决火雨队怎么搭配?火雨队是近期非常热门的一套阵容,不少玩家想了解该阵容,那么下面就让小编给大家带来斗罗大陆魂师对决火雨队阵容推荐,感兴趣的小伙伴们一起来看看吧。时间:2021-08-11编辑:hxh玩家在巅峰演武副本中不知道怎么打秦明,秦明的盾很厚不知道怎么破?那么下面就让小编给大家带来斗罗大陆魂师对决巅峰演武秦明破盾攻略,感兴趣的小伙伴们一起来...
在 GNU Radio OFDM 系统中,一个非常重要的环节是在接收端准确地同步和检测发送端发出的信号。这就是 Schmidl & Cox 同步算法发挥作用的地方。Schmidl & Cox 算法是一种用于 OFDM 信号的时间同步的技术。本文对其底层 C++ 源码进行学习记录。
REST,表述性状态转换,他是一种软件架构风格使用URL定位资源,HTTP动词描述操作根据发出请求类型来区分操作此为风格,是约定方式,可以打破描述模块的功能通常使用复数,也就是加s的格式来描述,表示此类资源,而非单个资源。
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。