官方给了直播的方法:startStream,最近公司需求是大疆无人机做直播推流给后台服务器,然后在后台可以看到无人机拍摄的内容,实时监控无人机的状态
因为这个最新的大疆官方给出了方法,所以做起来是非常简单的,我是根据官方的demo修改来的,有疑问的请评论和私信,代码在我的csdn下载,可以找到资源
项目地址:https://github.com/wrs13634194612/cf8833.git
先看效果图:手机上的推流和电脑上的推流显示一致
手机效果图是这样的:下面有一个editText,输入你的推流服务器地址,注意和下面的vlc保持一致
写一下整个项目的流程吧
1.连接无人机和遥控器,然后手机连接遥控器,打开app,获取到直播流,然后调用直播方法,在电脑上使用vlc进行接收,有一个推流服务器,手机推流到这个服务器地址,vlc打开这个地址,如果收到推流的话,就可以在电脑上进行显示
2.下载vlc官方地址:https://www.videolan.org/vlc/index.html
3.下载好了以后,点击安装。vlc安装步骤我就不说了,反正就是一直点下一步就行,语言选择中文,到了主界面,
选择 “媒体” --> “打开网络串流” ---> 输入你的服务器地址就可以了
4.开始写代码吧 app下面的builder.gradle
目录结构:
apply plugin: 'com.android.application'
// 源代码我传到我的csdn下载里面了,如果没有请私信,联系方式Q:2494075190
android {
compileSdkVersion 28
defaultConfig {
multiDexEnabled true
ndk {
abiFilters 'armeabi-v7a'
}
}
packagingOptions {
exclude 'META-INF/rxjava.properties'
}
packagingOptions{
doNotStrip "*/*/libdjivideo.so"
doNotStrip "*/*/libSDKRelativeJNI.so"
doNotStrip "*/*/libFlyForbid.so"
doNotStrip "*/*/libduml_vision_bokeh.so"
doNotStrip "*/*/libyuv2.so"
doNotStrip "*/*/libGroudStation.so"
doNotStrip "*/*/libFRCorkscrew.so"
doNotStrip "*/*/libUpgradeVerify.so"
doNotStrip "*/*/libFR.so"
}
}
dependencies {
implementation 'com.android.support:multidex:1.0.2'
implementation 'com.squareup:otto:1.3.8'
implementation('com.dji:dji-sdk:4.9', {
// Uncomment the following line if your app does not need Anti Distortion for
// Mavic 2 Pro and Mavic 2 Zoom. It will greatly reducing the size of the APK:
exclude module: 'library-anti-distortion'
})
compileOnly 'com.dji:dji-sdk-provided:4.9'
}
2.主要的application
package com.example.admin.ztest;
import android.app.Application;
import android.content.Context;
import android.os.Process;
import android.text.TextUtils;
import com.secneo.sdk.Helper;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class MApplication extends Application {
private FPVDemoApplication fpvDemoApplication;
@Override
protected void attachBaseContext(Context paramContext) {
super.attachBaseContext(paramContext);
Helper.install(MApplication.this);
if (fpvDemoApplication == null) {
fpvDemoApplication = new FPVDemoApplication();
fpvDemoApplication.setContext(this);
}
}
@Override
public void onCreate() {
super.onCreate();
fpvDemoApplication.onCreate();
}
}
3.主界面:
package com.example.admin.ztest;
import android.app.Activity;
import android.content.Intent;
import android.graphics.SurfaceTexture;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import dji.common.camera.SettingsDefinitions;
import dji.common.camera.SystemState;
import dji.common.error.DJIError;
import dji.common.product.Model;
import dji.common.useraccount.UserAccountState;
import dji.common.util.CommonCallbacks;
import dji.log.IFileFormat;
import dji.sdk.base.BaseProduct;
import dji.sdk.camera.Camera;
import dji.sdk.camera.VideoFeeder;
import dji.sdk.codec.DJICodecManager;
import dji.sdk.useraccount.UserAccountManager;
public class MainActivity extends Activity implements TextureView.SurfaceTextureListener, View.OnClickListener {
private static final String TAG = MainActivity.class.getName();
protected VideoFeeder.VideoDataListener mReceivedVideoDataListener = null;
protected DJICodecManager mCodecManager = null;
protected TextureView mVideoSurface = null;
private Button mCaptureBtn, mShootPhotoModeBtn, mRecordVideoModeBtn, btn_battery;
private ToggleButton mRecordBtn;
private TextView recordingTime;
private Handler handler;
//otto同样需要注册和取消注册、订阅事件
private boolean isConnect;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler();
initUI();
//用于接收摄像机实时视图的原始H264视频数据的回调
mReceivedVideoDataListener = new VideoFeeder.VideoDataListener() {
@Override
public void onReceive(byte[] videoBuffer, int size) {
if (mCodecManager != null) {
mCodecManager.sendDataToDecoder(videoBuffer, size);
}
}
};
final Camera camera = FPVDemoApplication.getCameraInstance();
if (camera != null) {
camera.setSystemStateCallback(new SystemState.Callback() {
@Override
public void onUpdate(@NonNull SystemState cameraSystemState) {
if (null != cameraSystemState) {
final int recordTime = cameraSystemState.getCurrentVideoRecordingTimeInSeconds();
int minutes = (recordTime % 3600) / 60;
int seconds = recordTime % 60;
final String timeString = String.format("%02d:%02d", minutes, seconds);
final boolean isVideorecording = cameraSystemState.isRecording();
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
recordingTime.setText(timeString);
if (isVideorecording) {
recordingTime.setVisibility(View.VISIBLE);
} else {
recordingTime.setVisibility(View.INVISIBLE);
}
}
});
}
}
});
}
}
protected void onProductChange() {
initPreviewer();
loginAccount();
}
private void loginAccount() {
UserAccountManager.getInstance().logIntoDJIUserAccount(this, new CommonCallbacks.CompletionCallbackWith<UserAccountState>() {
@Override
public void onSuccess(UserAccountState userAccountState) {
Log.e(TAG, "Login success");
}
@Override
public void onFailure(DJIError djiError) {
showToast("Login Error" + djiError.getDescription());
}
});
}
@Override
protected void onResume() {
super.onResume();
initPreviewer();
onProductChange();
if (mVideoSurface == null) {
Log.e(TAG, "mVideosurface is nnull");
}
}
@Override
protected void onPause() {
super.onPause();
uninitPreviewer();
}
@Override
protected void onStop() {
super.onStop();
}
public void onReturn(View view) {
this.finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
uninitPreviewer();
}
private void initUI() {
mVideoSurface = (TextureView) findViewById(R.id.video_previewer_surface);
recordingTime = (TextView) findViewById(R.id.timer);
mCaptureBtn = (Button) findViewById(R.id.btn_capture);
btn_battery = (Button) findViewById(R.id.btn_battery);
mRecordBtn = (ToggleButton) findViewById(R.id.btn_record);
mShootPhotoModeBtn = (Button) findViewById(R.id.btn_shoot_photo_mode);
mRecordVideoModeBtn = (Button) findViewById(R.id.btn_record_video_mode);
if (null != mVideoSurface) {
mVideoSurface.setSurfaceTextureListener(this);
}
mCaptureBtn.setOnClickListener(this);
mRecordBtn.setOnClickListener(this);
mShootPhotoModeBtn.setOnClickListener(this);
mRecordVideoModeBtn.setOnClickListener(this);
recordingTime.setVisibility(View.INVISIBLE);
mRecordBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
startRecord();
} else {
stopRecord();
}
}
});
btn_battery.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Intent intent = new Intent(MainActivity.this, BattryActivity.class);
// startActivity(intent);
}
});
}
private void initPreviewer() {
BaseProduct product = FPVDemoApplication.getProductInstance();
if (product == null || !product.isConnected()) {
showToast(getString(R.string.disconnected));
} else {
if (null != mVideoSurface) {
mVideoSurface.setSurfaceTextureListener(this);
}
if (!product.getModel().equals(Model.UNKNOWN_AIRCRAFT)) {
VideoFeeder.getInstance().getPrimaryVideoFeed().addVideoDataListener(mReceivedVideoDataListener);
}
}
}
private void uninitPreviewer() {
Camera camera = FPVDemoApplication.getCameraInstance();
if (camera != null) {
VideoFeeder.getInstance().getPrimaryVideoFeed().addVideoDataListener(null);
}
}
public void showToast(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
if (mCodecManager == null) {
mCodecManager = new DJICodecManager(this, surface, width, height);
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
if (mCodecManager != null) {
mCodecManager.cleanSurface();
mCodecManager = null;
}
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_capture: {
captureAction();
break;
}
case R.id.btn_shoot_photo_mode: {
switchCameraMode(SettingsDefinitions.CameraMode.SHOOT_PHOTO);
break;
}
case R.id.btn_record_video_mode: {
switchCameraMode(SettingsDefinitions.CameraMode.RECORD_VIDEO);
break;
}
default:
break;
}
}
private void switchCameraMode(SettingsDefinitions.CameraMode cameraMode) {
Camera camera = FPVDemoApplication.getCameraInstance();
if (camera != null) {
camera.setMode(cameraMode, new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (djiError == null) {
showToast("Switch Camera Mode Succeeded");
} else {
showToast(djiError.getDescription());
}
}
});
}
}
private void captureAction() {
final Camera camera = FPVDemoApplication.getCameraInstance();
if (camera != null) {
SettingsDefinitions.ShootPhotoMode photoMode = SettingsDefinitions.ShootPhotoMode.SINGLE;
camera.setShootPhotoMode(photoMode, new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (null == djiError) {
handler.postDelayed(new Runnable() {
@Override
public void run() {
//延时2s执行
camera.startShootPhoto(new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (djiError == null) {
showToast("take photo :success");
} else {
showToast(djiError.getDescription());
}
}
});
}
}, 2000);
}
}
});
}
}
private void startRecord() {
final Camera camera = FPVDemoApplication.getCameraInstance();
if (camera != null) {
camera.startRecordVideo(new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (djiError == null) {
showToast("Record video success");
} else {
showToast(djiError.getDescription());
}
}
});
}
}
private void stopRecord() {
Camera camera = FPVDemoApplication.getCameraInstance();
if (camera != null) {
camera.stopRecordVideo(new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (djiError == null) {
showToast("Stop recording success");
} else {
showToast(djiError.getDescription());
}
}
});
}
}
}
4.连接无人机:
package com.example.admin.ztest;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.widget.Toast;
import dji.common.error.DJIError;
import dji.common.error.DJISDKError;
import dji.sdk.base.BaseComponent;
import dji.sdk.base.BaseProduct;
import dji.sdk.battery.Battery;
import dji.sdk.camera.Camera;
import dji.sdk.products.Aircraft;
import dji.sdk.products.HandHeld;
import dji.sdk.sdkmanager.DJISDKManager;
public class FPVDemoApplication extends Application {
public static final String FLAG_CONNECTION_CHANGE = "fpv_tutorial_connection_change";
private DJISDKManager.SDKManagerCallback mDJISDKManagerCallback;
private static BaseProduct mProduct;
public Handler mHandler;
private Application instance;
public void setContext(Application application) {
instance = application;
}
@Override
public Context getApplicationContext() {
return instance;
}
public FPVDemoApplication() {
}
/**
* This function is used to get the instance of DJIBaseProduct.
* If no product is connected, it returns null.
*/
public static synchronized BaseProduct getProductInstance() {
if (null == mProduct) {
mProduct = DJISDKManager.getInstance().getProduct();
}
return mProduct;
}
//电池
public static synchronized Battery getBatteryInstance(){
if (getProductInstance() == null){
return null;
}
Battery battery = null;
BaseProduct productInstance = getProductInstance();
if (productInstance instanceof Aircraft){
battery = ((Aircraft)productInstance).getBattery();
//手持云台
}else if (productInstance instanceof HandHeld){