官方给了直播的方法: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 28defaultConfig {multiDexEnabled truendk {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;@Overrideprotected void attachBaseContext(Context paramContext) {super.attachBaseContext(paramContext);Helper.install(MApplication.this);if (fpvDemoApplication == null) {fpvDemoApplication = new FPVDemoApplication();fpvDemoApplication.setContext(this);}}@Overridepublic 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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);handler = new Handler();initUI();//用于接收摄像机实时视图的原始H264视频数据的回调mReceivedVideoDataListener = new VideoFeeder.VideoDataListener() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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>() {@Overridepublic void onSuccess(UserAccountState userAccountState) {Log.e(TAG, "Login success");}@Overridepublic void onFailure(DJIError djiError) {showToast("Login Error" + djiError.getDescription());}});}@Overrideprotected void onResume() {super.onResume();initPreviewer();onProductChange();if (mVideoSurface == null) {Log.e(TAG, "mVideosurface is nnull");}}@Overrideprotected void onPause() {super.onPause();uninitPreviewer();}@Overrideprotected void onStop() {super.onStop();}public void onReturn(View view) {this.finish();}@Overrideprotected 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() {@Overridepublic void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {if (isChecked) {startRecord();} else {stopRecord();}}});btn_battery.setOnClickListener(new View.OnClickListener() {@Overridepublic 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() {@Overridepublic void run() {Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();}});}@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {if (mCodecManager == null) {mCodecManager = new DJICodecManager(this, surface, width, height);}}@Overridepublic void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {if (mCodecManager != null) {mCodecManager.cleanSurface();mCodecManager = null;}return false;}@Overridepublic void onSurfaceTextureUpdated(SurfaceTexture surface) {}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onResult(DJIError djiError) {if (null == djiError) {handler.postDelayed(new Runnable() {@Overridepublic void run() {//延时2s执行camera.startShootPhoto(new CommonCallbacks.CompletionCallback() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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;}@Overridepublic 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){}return battery;}//相机public static synchronized Camera getCameraInstance() {if (getProductInstance() == null) return null;Camera camera = null;if (getProductInstance() instanceof Aircraft) {camera = ((Aircraft) getProductInstance()).getCamera();} else if (getProductInstance() instanceof HandHeld) {camera = ((HandHeld) getProductInstance()).getCamera();}return camera;}//直播===//飞机连接public static synchronized Aircraft getAircraftInstance() {if (!isAircraftConnected()) {return null;}return (Aircraft) getProductInstance();}public static boolean isAircraftConnected() {return getProductInstance() != null && getProductInstance() instanceof Aircraft;}@Overridepublic void onCreate() {super.onCreate();mHandler = new Handler(Looper.getMainLooper());/*** When starting SDK services, an instance of interface DJISDKManager.DJISDKManagerCallback will be used to listen to* the SDK Registration result and the product changing.*/mDJISDKManagerCallback = new DJISDKManager.SDKManagerCallback() {//Listens to the SDK registration result@Overridepublic void onRegister(DJIError djiError) {if (djiError == DJISDKError.REGISTRATION_SUCCESS) {Handler handler = new Handler(Looper.getMainLooper());handler.post(new Runnable() {@Overridepublic void run() {Toast.makeText(getApplicationContext(), "Register Success", Toast.LENGTH_LONG).show();}});DJISDKManager.getInstance().startConnectionToProduct();} else {Handler handler = new Handler(Looper.getMainLooper());handler.post(new Runnable() {@Overridepublic void run() {Toast.makeText(getApplicationContext(), "Register sdk fails, check network is available", Toast.LENGTH_LONG).show();}});}Log.e("TAG", djiError.toString());}@Overridepublic void onProductDisconnect() {Log.d("TAG", "onProductDisconnect");notifyStatusChange();}@Overridepublic void onProductConnect(BaseProduct baseProduct) {Log.d("TAG", String.format("onProductConnect newProduct:%s", baseProduct));notifyStatusChange();}@Overridepublic void onComponentChange(BaseProduct.ComponentKey componentKey, BaseComponent oldComponent,BaseComponent newComponent) {if (newComponent != null) {newComponent.setComponentListener(new BaseComponent.ComponentListener() {@Overridepublic void onConnectivityChange(boolean isConnected) {Log.d("TAG", "onComponentConnectivityChanged: " + isConnected);notifyStatusChange();}});}Log.d("TAG",String.format("onComponentChange key:%s, oldComponent:%s, newComponent:%s",componentKey,oldComponent,newComponent));}};//Check the permissions before registering the application for android system 6.0 above.int permissionCheck = ContextCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.WRITE_EXTERNAL_STORAGE);int permissionCheck2 = ContextCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.READ_PHONE_STATE);if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || (permissionCheck == 0 && permissionCheck2 == 0)) {//This is used to start SDK services and initiate SDK.DJISDKManager.getInstance().registerApp(getApplicationContext(), mDJISDKManagerCallback);Toast.makeText(getApplicationContext(), "registering, pls wait...", Toast.LENGTH_LONG).show();} else {Toast.makeText(getApplicationContext(), "Please check if the permission is granted.", Toast.LENGTH_LONG).show();}}private void notifyStatusChange() {mHandler.removeCallbacks(updateRunnable);mHandler.postDelayed(updateRunnable, 500);}private Runnable updateRunnable = new Runnable() {@Overridepublic void run() {Intent intent = new Intent(FLAG_CONNECTION_CHANGE);getApplicationContext().sendBroadcast(intent);}};
}

5.连接注册,连接状态

package com.example.admin.ztest;import android.Manifest;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;import com.example.admin.ztest.live.LiveStreamView;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;import dji.common.error.DJIError;
import dji.common.error.DJISDKError;
import dji.log.DJILog;
import dji.sdk.base.BaseComponent;
import dji.sdk.base.BaseProduct;
import dji.sdk.products.Aircraft;
import dji.sdk.sdkmanager.DJISDKManager;public class ConnectionActivity extends Activity implements View.OnClickListener {private static final String TAG = ConnectionActivity.class.getName();private TextView mTextConnectionStatus;private TextView mTextProduct;private TextView mVersionTv;private Button mBtnOpen;private static final String[] REQUIRED_PERMISSION_LIST = new String[]{Manifest.permission.VIBRATE,Manifest.permission.INTERNET,Manifest.permission.ACCESS_WIFI_STATE,Manifest.permission.WAKE_LOCK,Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_NETWORK_STATE,Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.CHANGE_WIFI_STATE,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.BLUETOOTH,Manifest.permission.BLUETOOTH_ADMIN,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.READ_PHONE_STATE,};private List<String> missingPermission = new ArrayList<>();private AtomicBoolean isRegistrationInProgress = new AtomicBoolean(false);private static final int REQUEST_PERMISSION_CODE = 12345;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);checkAndRequestPermissions();setContentView(R.layout.activity_connection);initUI();// Register the broadcast receiver for receiving the device connection's changes.IntentFilter filter = new IntentFilter();filter.addAction(FPVDemoApplication.FLAG_CONNECTION_CHANGE);registerReceiver(mReceiver, filter);}/*** Checks if there is any missing permissions, and* requests runtime permission if needed.*/private void checkAndRequestPermissions() {// Check for permissionsfor (String eachPermission : REQUIRED_PERMISSION_LIST) {if (ContextCompat.checkSelfPermission(this, eachPermission) != PackageManager.PERMISSION_GRANTED) {missingPermission.add(eachPermission);}}// Request for missing permissionsif (!missingPermission.isEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {ActivityCompat.requestPermissions(this,missingPermission.toArray(new String[missingPermission.size()]),REQUEST_PERMISSION_CODE);}}/*** Result of runtime permission request*/@Overridepublic void onRequestPermissionsResult(int requestCode,@NonNull String[] permissions,@NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);// Check for granted permission and remove from missing listif (requestCode == REQUEST_PERMISSION_CODE) {for (int i = grantResults.length - 1; i >= 0; i--) {if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {missingPermission.remove(permissions[i]);}}}// If there is enough permission, we will start the registrationif (missingPermission.isEmpty()) {startSDKRegistration();} else {showToast("Missing permissions!!!");}}private void startSDKRegistration() {if (isRegistrationInProgress.compareAndSet(false, true)) {AsyncTask.execute(new Runnable() {@Overridepublic void run() {showToast("registering, pls wait...");DJISDKManager.getInstance().registerApp(getApplicationContext(), new DJISDKManager.SDKManagerCallback() {@Overridepublic void onRegister(DJIError djiError) {if (djiError == DJISDKError.REGISTRATION_SUCCESS) {DJILog.e("App registration", DJISDKError.REGISTRATION_SUCCESS.getDescription());DJISDKManager.getInstance().startConnectionToProduct();showToast("Register Success");} else {showToast("Register sdk fails, check network is available");}Log.v(TAG, djiError.getDescription());}@Overridepublic void onProductDisconnect() {Log.d(TAG, "onProductDisconnect");showToast("Product Disconnected");}@Overridepublic void onProductConnect(BaseProduct baseProduct) {Log.d(TAG, String.format("onProductConnect newProduct:%s", baseProduct));showToast("Product Connected");}@Overridepublic void onComponentChange(BaseProduct.ComponentKey componentKey, BaseComponent oldComponent,BaseComponent newComponent) {if (newComponent != null) {newComponent.setComponentListener(new BaseComponent.ComponentListener() {@Overridepublic void onConnectivityChange(boolean isConnected) {Log.d(TAG, "onComponentConnectivityChanged: " + isConnected);}});}Log.d(TAG,String.format("onComponentChange key:%s, oldComponent:%s, newComponent:%s",componentKey,oldComponent,newComponent));}});}});}}@Overridepublic void onResume() {Log.e(TAG, "onResume");super.onResume();}@Overridepublic void onPause() {Log.e(TAG, "onPause");super.onPause();}@Overridepublic void onStop() {Log.e(TAG, "onStop");super.onStop();}@Overrideprotected void onDestroy() {Log.e(TAG, "onDestroy");unregisterReceiver(mReceiver);super.onDestroy();}private void initUI() {mTextConnectionStatus = (TextView) findViewById(R.id.text_connection_status);mTextProduct = (TextView) findViewById(R.id.text_product_info);mBtnOpen = (Button) findViewById(R.id.btn_open);mBtnOpen.setOnClickListener(this);mBtnOpen.setEnabled(false);mVersionTv = (TextView) findViewById(R.id.textView2);mVersionTv.setText(getResources().getString(R.string.sdk_version, DJISDKManager.getInstance().getSDKVersion()));}protected BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {refreshSDKRelativeUI();}};private void refreshSDKRelativeUI() {BaseProduct mProduct = FPVDemoApplication.getProductInstance();if (null != mProduct && mProduct.isConnected()) {Log.v(TAG, "refreshSDK: True");mBtnOpen.setEnabled(true);String str = mProduct instanceof Aircraft ? "DJIAircraft" : "DJIHandHeld";mTextConnectionStatus.setText("Status: " + str + " connected");if (null != mProduct.getModel()) {mTextProduct.setText("" + mProduct.getModel().getDisplayName());} else {mTextProduct.setText(R.string.product_information);}} else {Log.v(TAG, "refreshSDK: False");mBtnOpen.setEnabled(false);mTextProduct.setText(R.string.product_information);mTextConnectionStatus.setText(R.string.connection_loose);}}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_open: {Intent intent = new Intent(this, LiveStreamView.class);startActivity(intent);break;}default:break;}}private void showToast(final String toastMsg) {runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(getApplicationContext(), toastMsg, Toast.LENGTH_LONG).show();}});}
}

6.获取电池的类,其实没什么用,也可以看看吧

package com.example.admin.ztest;import android.app.Activity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.widget.Toast;import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;import dji.common.battery.BatteryState;
import dji.common.error.DJIError;
import dji.common.util.CommonCallbacks;
import dji.sdk.battery.Battery;
import dji.sdk.camera.Camera;public class BattryActivity extends AppCompatActivity implements BatteryState.Callback {/** view*/private TextView createTimeTv;private TextView createFreTv;private TextView batteryFullChargeCapacityTv;private TextView batteryChargeRemainingTv;private TextView batteryChargeRemainingInPercentTv;private TextView batteryIsBeingChargedTv;private TextView batterySelfHeatingStateTv;private TextView batteryVoltageTv;private TextView batteryCurrentTv;private TextView batteryLifetimeRemainingTv;private TextView batteryTemperatureTv;private TextView batteryNumberOfDischargesTv;private TextView batterySnTv;/*流程参数*/private Date createTime = new Date();//当前更新时间//读取电池信息状态private Timer timer = new Timer();private Battery mBattery;private TimerTask task = new TimerTask() {@Overridepublic void run() {BattryActivity.this.mBattery = FPVDemoApplication.getBatteryInstance();if (mBattery != null) {mBattery.setStateCallback(BattryActivity.this);mBattery.getSerialNumber(new CommonCallbacks.CompletionCallbackWith<String>() {@Overridepublic void onSuccess(final String s) {runOnUiThread(new Runnable() {@Overridepublic void run() {batterySnTv.setText("SN:" + s);}});}@Overridepublic void onFailure(DJIError djiError) {}});task.cancel();timer.cancel();}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_showbattery);createFreTv = findViewById(R.id.tv_create_fre);createTimeTv = findViewById(R.id.tv_create_time);batteryFullChargeCapacityTv = findViewById(R.id.bfcctv);batteryChargeRemainingTv = findViewById(R.id.bcrtv);batteryChargeRemainingInPercentTv = findViewById(R.id.bcriptv);batteryIsBeingChargedTv = findViewById(R.id.bibctv);batterySelfHeatingStateTv = findViewById(R.id.bshstv);batteryVoltageTv = findViewById(R.id.bvtv);batteryCurrentTv = findViewById(R.id.bctv);batteryLifetimeRemainingTv = findViewById(R.id.blrtv);batteryTemperatureTv = findViewById(R.id.bttv);batteryNumberOfDischargesTv = findViewById(R.id.bnodtv);batterySnTv = findViewById(R.id.tv_sn);//获取时间timer.schedule(task, 100, 1000);}@Overridepublic void onUpdate(final BatteryState batteryState) {if (batteryState == null) {return;}runOnUiThread(new Runnable() {@Overridepublic void run() {//刷新当前更新时间Date nowDate = new Date();createTimeTv.setText("更新时间: " + android.text.format.DateFormat.format("yyyy年MM月dd日,kk:mm:ss", nowDate));long fre = nowDate.getTime() - createTime.getTime();createFreTv.setText("刷新频率:" + (fre / 1000) + "s/次");createTime = nowDate;//更新状态try {//总能量batteryFullChargeCapacityTv.setText("总电量:" + batteryState.getFullChargeCapacity() + " mAh");//剩余能量batteryChargeRemainingTv.setText("剩余电量:" + batteryState.getChargeRemaining() + " mAh");//剩余电量百分比batteryChargeRemainingInPercentTv.setText("剩余电量百分比:" + batteryState.getChargeRemainingInPercent() + " %");//是否正在充电batteryIsBeingChargedTv.setText("是否正在充电:" + (batteryState.isBeingCharged() ? "是" : "否"));//自发热状态if (batteryState.getSelfHeatingState() != null) {batterySelfHeatingStateTv.setText("自发热状态:" + batteryState.getSelfHeatingState().value());}//电压batteryVoltageTv.setText("当前电压:" + batteryState.getVoltage() + " mV");//当前状态batteryCurrentTv.setText("当前状态:" + (batteryState.getCurrent() > 0 ? "正在充电" : "正在放电"));//剩余寿命batteryLifetimeRemainingTv.setText("剩余寿命:" + batteryState.getLifetimeRemaining() + "%");//电池温度batteryTemperatureTv.setText("电池温度:" + batteryState.getTemperature() + " ℃");//总放电次数batteryNumberOfDischargesTv.setText("总放电次数:" + batteryState.getNumberOfDischarges());} catch (Exception e) {Toast.makeText(BattryActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();}}});}
}

7.Helper live包里面的几个类,都是官方写的,我稍微修改了一下

package com.example.admin.ztest.live;import android.app.Activity;
import android.widget.TextView;
import android.widget.Toast;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import dji.common.product.Model;
import dji.sdk.sdkmanager.DJISDKManager;public class Helper {public Helper() {}/*** Shows message on current activity.** @param activity The activity the user want to show toast.* @param msg      The String that the user want to put in the message.*/public static void showToast(final Activity activity, final String msg) {activity.runOnUiThread(new Runnable() {public void run() {Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show();}});}public static void showText(final Activity activity, final TextView tv, final String msg) {if (tv == null) {showToast(activity, "The current textView is null. ");return;}activity.runOnUiThread(new Runnable() {public void run() {tv.setText(msg);}});}/*** Transfers to list from enum array.** @param o An object array* @return An ArrayList object of String for enum.*/public ArrayList<String> makeList(Object[] o) {ArrayList<String> list = new ArrayList<String>();for (int i = 0; i < o.length; i++) {list.add(o[i].toString());}return list;}public ArrayList<String> makeList(int[] o) {ArrayList<String> list = new ArrayList<String>();for (int i = 0; i < o.length; i++) {list.add(Integer.valueOf(o[i]).toString());}return list;}public ArrayList<String> makeList(List o) {ArrayList<String> list = new ArrayList<String>();Iterator iterator = o.iterator();while (iterator.hasNext()) {list.add(iterator.next().toString());}return list;}public static String getString(byte[] bytes) {if (null == bytes) {return "";}// 去除NULL字符byte zero = 0x00;byte no = (byte) 0xFF;for (int i = 0; i < bytes.length; i++) {if (bytes[i] == zero || bytes[i] == no) {bytes = readBytes(bytes, 0, i);break;}}return getString(bytes, "GBK");}private static String getString(byte[] bytes, String charsetName) {return new String(bytes, Charset.forName(charsetName));}public static byte[] readBytes(byte[] source, int from, int length) {byte[] result = new byte[length];System.arraycopy(source, from, result, 0, length);return result;}public static byte[] getBytes(String data) {return getBytes(data, "GBK");}private static byte[] getBytes(String data, String charsetName) {Charset charset = Charset.forName(charsetName);return data.getBytes(charset);}public static String getStringUTF8(byte[] bytes, int start, int length) {if (null == bytes || bytes.length == 0) {return "";}// 去除NULL字符byte zero = 0x00;for (int i = start; i < length && i < bytes.length; i++) {if (bytes[i] == zero) {length = i - start;break;}}return getString(bytes, start, length, "UTF-8");}private static String getString(byte[] bytes, int start, int length, String charsetName) {return new String(bytes, start, length, Charset.forName(charsetName));}public static String byte2hex(byte[] buffer) {String h = "";if (null == buffer) {return h;}for (int i = 0; i < buffer.length; i++) {String temp = Integer.toHexString(buffer[i] & 0xFF);if (temp.length() == 1) {temp = "0" + temp;}h = h + " " + temp;}return h;}public static String timeStamp2Date(String format) {if (format == null || format.isEmpty()) {format = "yyyy-MM-dd-HH-mm-ss";}SimpleDateFormat sdf = new SimpleDateFormat(format);long time = System.currentTimeMillis();return sdf.format(new Date(time));}public static boolean isMultiStreamPlatform() {if (DJISDKManager.getInstance() == null) {return false;}Model model = DJISDKManager.getInstance().getProduct().getModel();return model != null && (model == Model.INSPIRE_2|| model == Model.MATRICE_200|| model == Model.MATRICE_210|| model == Model.MATRICE_210_RTK|| model == Model.MATRICE_600|| model == Model.MATRICE_600_PRO|| model == Model.A3|| model == Model.N3);}
}

8.非常重要的推流类,核心都在这里,如果您是要做推流功能的话,这个类是核心

package com.example.admin.ztest.live;import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;import com.example.admin.ztest.FPVDemoApplication;
import com.example.admin.ztest.R;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;import dji.sdk.base.BaseProduct;
import dji.sdk.camera.VideoFeeder;
import dji.sdk.sdkmanager.DJISDKManager;
import dji.sdk.sdkmanager.LiveStreamManager;/*** Class for live stream demo.** @author Hoker* @date 2019/1/28* <p>* Copyright (c) 2019, DJI All Rights Reserved.*/
public class LiveStreamView extends Activity implements PresentableView, View.OnClickListener {private String liveShowUrl = "please input your live show url here";private VideoFeedView primaryVideoFeedView;private VideoFeedView fpvVideoFeedView;private EditText showUrlInputEdit;private Button startLiveShowBtn;private Button enableVideoEncodingBtn;private Button disableVideoEncodingBtn;private Button stopLiveShowBtn;private Button soundOnBtn;private Button soundOffBtn;private Button isLiveShowOnBtn;private Button showInfoBtn;private Button showLiveStartTimeBtn;private Button showCurrentVideoSourceBtn;private Button changeVideoSourceBtn;private LiveStreamManager.OnLiveChangeListener listener;private LiveStreamManager.LiveStreamVideoSource currentVideoSource = LiveStreamManager.LiveStreamVideoSource.Primary;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.view_live_stream);initUI();initListener();}private void initUI() {primaryVideoFeedView = (VideoFeedView) findViewById(R.id.video_view_primary_video_feed);primaryVideoFeedView.registerLiveVideo(VideoFeeder.getInstance().getPrimaryVideoFeed(), true);fpvVideoFeedView = (VideoFeedView) findViewById(R.id.video_view_fpv_video_feed);fpvVideoFeedView.registerLiveVideo(VideoFeeder.getInstance().getSecondaryVideoFeed(), false);if (Helper.isMultiStreamPlatform()) {fpvVideoFeedView.setVisibility(View.VISIBLE);}showUrlInputEdit = (EditText) findViewById(R.id.edit_live_show_url_input);showUrlInputEdit.setText(liveShowUrl);showUrlInputEdit.setOnClickListener(this);startLiveShowBtn = (Button) findViewById(R.id.btn_start_live_show);enableVideoEncodingBtn = (Button) findViewById(R.id.btn_enable_video_encode);disableVideoEncodingBtn = (Button) findViewById(R.id.btn_disable_video_encode);stopLiveShowBtn = (Button) findViewById(R.id.btn_stop_live_show);soundOnBtn = (Button) findViewById(R.id.btn_sound_on);soundOffBtn = (Button) findViewById(R.id.btn_sound_off);isLiveShowOnBtn = (Button) findViewById(R.id.btn_is_live_show_on);showInfoBtn = (Button) findViewById(R.id.btn_show_info);showLiveStartTimeBtn = (Button) findViewById(R.id.btn_show_live_start_time);showCurrentVideoSourceBtn = (Button) findViewById(R.id.btn_show_current_video_source);changeVideoSourceBtn = (Button) findViewById(R.id.btn_change_video_source);startLiveShowBtn.setOnClickListener(this);enableVideoEncodingBtn.setOnClickListener(this);disableVideoEncodingBtn.setOnClickListener(this);stopLiveShowBtn.setOnClickListener(this);soundOnBtn.setOnClickListener(this);soundOffBtn.setOnClickListener(this);isLiveShowOnBtn.setOnClickListener(this);showInfoBtn.setOnClickListener(this);showLiveStartTimeBtn.setOnClickListener(this);showCurrentVideoSourceBtn.setOnClickListener(this);changeVideoSourceBtn.setOnClickListener(this);}private void initListener() {showUrlInputEdit.addTextChangedListener(new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {liveShowUrl = s.toString();}@Overridepublic void afterTextChanged(Editable s) {}});listener = new LiveStreamManager.OnLiveChangeListener() {@Overridepublic void onStatusChanged(int i) {//wang
//                ToastUtils.setResultToToast("status changed : " + i);}};}@Overridepublic void onAttachedToWindow() {super.onAttachedToWindow();BaseProduct product = FPVDemoApplication.getProductInstance();if (product == null || !product.isConnected()) {Toast.makeText(getApplicationContext(),"Disconnect",Toast.LENGTH_SHORT).show();//wang
//            ToastUtils.setResultToToast("Disconnect");return;}if (isLiveStreamManagerOn()) {DJISDKManager.getInstance().getLiveStreamManager().registerListener(listener);}}@Overridepublic void onDetachedFromWindow() {super.onDetachedFromWindow();if (isLiveStreamManagerOn()) {DJISDKManager.getInstance().getLiveStreamManager().unregisterListener(listener);}}@Overridepublic int getDescription() {return R.string.component_listview_live_stream;}@NonNull@Overridepublic String getHint() {return this.getClass().getSimpleName() + ".java";}void startLiveShow() {//wangToast.makeText(getApplicationContext(),"Start Live Show",Toast.LENGTH_SHORT).show();
//        ToastUtils.setResultToToast("Start Live Show");if (!isLiveStreamManagerOn()) {return;}if (DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {//wangToast.makeText(getApplicationContext(),"already started!",Toast.LENGTH_SHORT).show();
//            ToastUtils.setResultToToast("already started!");return;}new Thread() {@Overridepublic void run() {DJISDKManager.getInstance().getLiveStreamManager().setLiveUrl("rtmp://118.24.55.19:1935/live/test");int result = DJISDKManager.getInstance().getLiveStreamManager().startStream();DJISDKManager.getInstance().getLiveStreamManager().setStartTime();//wangToast.makeText(getApplicationContext(),"startLive:" + result +"\n isVideoStreamSpeedConfigurable:" + DJISDKManager.getInstance().getLiveStreamManager().isVideoStreamSpeedConfigurable() +"\n isLiveAudioEnabled:" + DJISDKManager.getInstance().getLiveStreamManager().isLiveAudioEnabled(),Toast.LENGTH_SHORT).show();/*  ToastUtils.setResultToToast("startLive:" + result +"\n isVideoStreamSpeedConfigurable:" + DJISDKManager.getInstance().getLiveStreamManager().isVideoStreamSpeedConfigurable() +"\n isLiveAudioEnabled:" + DJISDKManager.getInstance().getLiveStreamManager().isLiveAudioEnabled());*/}}.start();}private void enableReEncoder() {if (!isLiveStreamManagerOn()) {return;}DJISDKManager.getInstance().getLiveStreamManager().setVideoEncodingEnabled(true);//wangToast.makeText(getApplicationContext(),"Force Re-Encoder Enabled!",Toast.LENGTH_SHORT).show();/// ToastUtils.setResultToToast("Force Re-Encoder Enabled!");}private void disableReEncoder() {if (!isLiveStreamManagerOn()) {return;}DJISDKManager.getInstance().getLiveStreamManager().setVideoEncodingEnabled(false);//wangToast.makeText(getApplicationContext(),"Disable Force Re-Encoder!",Toast.LENGTH_SHORT).show();//    ToastUtils.setResultToToast("Disable Force Re-Encoder!");}private void stopLiveShow() {if (!isLiveStreamManagerOn()) {return;}DJISDKManager.getInstance().getLiveStreamManager().stopStream();//wangToast.makeText(getApplicationContext(),"Stop Live Show",Toast.LENGTH_SHORT).show();//  ToastUtils.setResultToToast("Stop Live Show");}private void soundOn() {if (!isLiveStreamManagerOn()) {return;}DJISDKManager.getInstance().getLiveStreamManager().setAudioMuted(false);//wangToast.makeText(getApplicationContext(),"Sound On",Toast.LENGTH_SHORT).show();//    ToastUtils.setResultToToast("Sound On");}private void soundOff() {if (!isLiveStreamManagerOn()) {return;}DJISDKManager.getInstance().getLiveStreamManager().setAudioMuted(true);//wangToast.makeText(getApplicationContext(),"Sound Off",Toast.LENGTH_SHORT).show();// ToastUtils.setResultToToast("Sound Off");}private void isLiveShowOn() {if (!isLiveStreamManagerOn()) {return;}//wangToast.makeText(getApplicationContext(),"Is Live Show On:" + DJISDKManager.getInstance().getLiveStreamManager().isStreaming(),Toast.LENGTH_SHORT).show();//ToastUtils.setResultToToast("Is Live Show On:" + DJISDKManager.getInstance().getLiveStreamManager().isStreaming());}private void showInfo() {if (!isLiveStreamManagerOn()) {return;}StringBuilder sb = new StringBuilder();sb.append("Video BitRate:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoBitRate()).append(" kpbs\n");sb.append("Audio BitRate:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveAudioBitRate()).append(" kpbs\n");sb.append("Video FPS:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoFps()).append("\n");sb.append("Video Cache size:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoCacheSize()).append(" frame");//wangToast.makeText(getApplicationContext(),sb.toString(),Toast.LENGTH_SHORT).show();//    ToastUtils.setResultToToast(sb.toString());}private void showLiveStartTime() {if (!isLiveStreamManagerOn()) {return;}if (!DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {//wangToast.makeText(getApplicationContext(),"Please Start Live First",Toast.LENGTH_SHORT).show();//     ToastUtils.setResultToToast("Please Start Live First");return;}long startTime = DJISDKManager.getInstance().getLiveStreamManager().getStartTime();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());String sd = sdf.format(new Date(Long.parseLong(String.valueOf(startTime))));//wangToast.makeText(getApplicationContext(),"Live Start Time: " + sd,Toast.LENGTH_SHORT).show();//      ToastUtils.setResultToToast("Live Start Time: " + sd);}private void changeVideoSource() {if (!isLiveStreamManagerOn()) {return;}if (!isSupportSecondaryVideo()) {return;}if (DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {//wangToast.makeText(getApplicationContext(),"Before change live source, you should stop live stream!",Toast.LENGTH_SHORT).show();//    ToastUtils.setResultToToast("Before change live source, you should stop live stream!");return;}currentVideoSource = (currentVideoSource == LiveStreamManager.LiveStreamVideoSource.Primary) ?LiveStreamManager.LiveStreamVideoSource.Secoundary :LiveStreamManager.LiveStreamVideoSource.Primary;DJISDKManager.getInstance().getLiveStreamManager().setVideoSource(currentVideoSource);//wangToast.makeText(getApplicationContext(),"Change Success ! Video Source : " + currentVideoSource.name(),Toast.LENGTH_SHORT).show();//   ToastUtils.setResultToToast("Change Success ! Video Source : " + currentVideoSource.name());}private void showCurrentVideoSource() {//    ToastUtils.setResultToToast("Video Source : " + currentVideoSource.name());Toast.makeText(getApplicationContext(),"Video Source : " + currentVideoSource.name(),Toast.LENGTH_SHORT).show();//wang}private boolean isLiveStreamManagerOn() {if (DJISDKManager.getInstance().getLiveStreamManager() == null) {//    ToastUtils.setResultToToast("No live stream manager!");//wangToast.makeText(getApplicationContext(),"No live stream manager!",Toast.LENGTH_SHORT).show();return false;}return true;}private boolean isSupportSecondaryVideo() {if (!Helper.isMultiStreamPlatform()) {// ToastUtils.setResultToToast("No secondary video!");//wangToast.makeText(getApplicationContext(),"No secondary video!",Toast.LENGTH_SHORT).show();return false;}return true;}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_start_live_show:startLiveShow();break;case R.id.btn_enable_video_encode:enableReEncoder();break;case R.id.btn_disable_video_encode:disableReEncoder();break;case R.id.btn_stop_live_show:stopLiveShow();break;case R.id.btn_sound_on:soundOn();break;case R.id.btn_sound_off:soundOff();break;case R.id.btn_is_live_show_on:isLiveShowOn();break;case R.id.btn_show_info:showInfo();break;case R.id.btn_show_live_start_time:showLiveStartTime();break;case R.id.btn_show_current_video_source:showCurrentVideoSource();break;case R.id.btn_change_video_source:changeVideoSource();break;case R.id.edit_live_show_url_input:showUrlInputEdit.setText("");break;default:break;}}
}

9.推流的一些初始化的类

package com.example.admin.ztest.live;/*** Created by wrs on 2019/6/19,11:53* projectName: Ztest5* packageName: com.example.admin.ztest.live*/import android.support.annotation.Nullable;import com.example.admin.ztest.FPVDemoApplication;import dji.common.product.Model;
import dji.sdk.accessory.AccessoryAggregation;
import dji.sdk.accessory.beacon.Beacon;
import dji.sdk.accessory.speaker.Speaker;
import dji.sdk.accessory.spotlight.Spotlight;
import dji.sdk.base.BaseProduct;
import dji.sdk.flightcontroller.FlightController;
import dji.sdk.flightcontroller.Simulator;
import dji.sdk.products.Aircraft;
import dji.sdk.products.HandHeld;/*** Created by dji on 16/1/6.*/
public class ModuleVerificationUtil {public static boolean isProductModuleAvailable() {return (null != FPVDemoApplication.getProductInstance());}public static boolean isAircraft() {return FPVDemoApplication.getProductInstance() instanceof Aircraft;}public static boolean isHandHeld() {return FPVDemoApplication.getProductInstance() instanceof HandHeld;}//这个是拍照的public static boolean isCameraModuleAvailable() {return isProductModuleAvailable() && (null != FPVDemoApplication.getProductInstance().getCamera());}public static boolean isPlaybackAvailable() {return isCameraModuleAvailable() && (null != FPVDemoApplication.getProductInstance().getCamera().getPlaybackManager());}public static boolean isMediaManagerAvailable() {return isCameraModuleAvailable() && (null != FPVDemoApplication.getProductInstance().getCamera().getMediaManager());}public static boolean isRemoteControllerAvailable() {return isProductModuleAvailable() && isAircraft() && (null != FPVDemoApplication.getAircraftInstance().getRemoteController());}public static boolean isFlightControllerAvailable() {return isProductModuleAvailable() && isAircraft() && (null != FPVDemoApplication.getAircraftInstance().getFlightController());}public static boolean isCompassAvailable() {return isFlightControllerAvailable() && isAircraft() && (null != FPVDemoApplication.getAircraftInstance().getFlightController().getCompass());}public static boolean isFlightLimitationAvailable() {return isFlightControllerAvailable() && isAircraft();}public static boolean isGimbalModuleAvailable() {return isProductModuleAvailable() && (null != FPVDemoApplication.getProductInstance().getGimbal());}public static boolean isAirlinkAvailable() {return isProductModuleAvailable() && (null != FPVDemoApplication.getProductInstance().getAirLink());}public static boolean isWiFiLinkAvailable() {return isAirlinkAvailable() && (null != FPVDemoApplication.getProductInstance().getAirLink().getWiFiLink());}public static boolean isLightbridgeLinkAvailable() {return isAirlinkAvailable() && (null != FPVDemoApplication.getProductInstance().getAirLink().getLightbridgeLink());}public static AccessoryAggregation getAccessoryAggregation() {Aircraft aircraft = (Aircraft) FPVDemoApplication.getProductInstance();if (aircraft != null && null != aircraft.getAccessoryAggregation()) {return aircraft.getAccessoryAggregation();}return null;}public static Speaker getSpeaker() {Aircraft aircraft = (Aircraft) FPVDemoApplication.getProductInstance();if (aircraft != null && null != aircraft.getAccessoryAggregation() && null != aircraft.getAccessoryAggregation().getSpeaker()) {return aircraft.getAccessoryAggregation().getSpeaker();}return null;}public static Beacon getBeacon() {Aircraft aircraft = (Aircraft) FPVDemoApplication.getProductInstance();if (aircraft != null && null != aircraft.getAccessoryAggregation() && null != aircraft.getAccessoryAggregation().getBeacon()) {return aircraft.getAccessoryAggregation().getBeacon();}return null;}public static Spotlight getSpotlight() {Aircraft aircraft = (Aircraft) FPVDemoApplication.getProductInstance();if (aircraft != null && null != aircraft.getAccessoryAggregation() && null != aircraft.getAccessoryAggregation().getSpotlight()) {return aircraft.getAccessoryAggregation().getSpotlight();}return null;}@Nullablepublic static Simulator getSimulator() {Aircraft aircraft = FPVDemoApplication.getAircraftInstance();if (aircraft != null) {FlightController flightController = aircraft.getFlightController();if (flightController != null) {return flightController.getSimulator();}}return null;}@Nullablepublic static FlightController getFlightController() {Aircraft aircraft = FPVDemoApplication.getAircraftInstance();if (aircraft != null) {return aircraft.getFlightController();}return null;}@Nullablepublic static boolean isMavic2Product() {BaseProduct baseProduct = FPVDemoApplication.getProductInstance();if (baseProduct != null) {return baseProduct.getModel() == Model.MAVIC_2_PRO || baseProduct.getModel() == Model.MAVIC_2_ZOOM;}return false;}}

10接口类:


import android.support.annotation.NonNull;
import android.support.annotation.StringRes;public interface PresentableView {/*** Returns string id for the description of this View. This might be shown inside the View itself.*/@StringResint getDescription();/*** Return the hint to user on how to find this View in code* @return*/@NonNullString getHint();
}

11自定义的一个视频显示的控件,主要是可以自己去修改属性,你不自定义也行,直接用android自带的那个控件TextureView

package com.example.admin.ztest.live;import android.content.Context;
import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.util.AttributeSet;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
import android.view.View;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;import dji.midware.usb.P3.UsbAccessoryService;
import dji.sdk.camera.VideoFeeder;
import dji.sdk.codec.DJICodecManager;
import dji.thirdparty.rx.Observable;
import dji.thirdparty.rx.android.schedulers.AndroidSchedulers;
import dji.thirdparty.rx.functions.Action1;/*** VideoView will show the live video for the given video feed.*/
//TextureView 看到没有  就是这个控件   大疆用了自定义的public class VideoFeedView extends TextureView implements SurfaceTextureListener {//region Propertiesprivate final static String TAG = "DULFpvWidget";private DJICodecManager codecManager = null;private VideoFeeder.VideoDataListener videoDataListener = null;private int videoWidth;private int videoHeight;private boolean isPrimaryVideoFeed;private View coverView;private final long WAIT_TIME = 500; // Half of a secondprivate AtomicLong lastReceivedFrameTime = new AtomicLong(0);private Observable timer =Observable.timer(100, TimeUnit.MICROSECONDS).observeOn(AndroidSchedulers.mainThread()).repeat();//endregion//region Life-Cyclepublic VideoFeedView(Context context) {this(context, null, 0);}public VideoFeedView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public VideoFeedView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}public void setCoverView(View view) {coverView = view;}private void init() {// Avoid the rending exception in the Android Studio Preview view.if (isInEditMode()) {return;}setSurfaceTextureListener(this);videoDataListener = new VideoFeeder.VideoDataListener() {@Overridepublic void onReceive(byte[] videoBuffer, int size) {lastReceivedFrameTime.set(System.currentTimeMillis());if (codecManager != null) {codecManager.sendDataToDecoder(videoBuffer,size,isPrimaryVideoFeed? UsbAccessoryService.VideoStreamSource.Camera.getIndex(): UsbAccessoryService.VideoStreamSource.Fpv.getIndex());}}};timer.subscribe(new Action1() {@Overridepublic void call(Object o) {final long now = System.currentTimeMillis();final long ellapsedTime = now - lastReceivedFrameTime.get();if (coverView != null) {if (ellapsedTime > WAIT_TIME && !ModuleVerificationUtil.isMavic2Product()) {if (coverView.getVisibility() == INVISIBLE) {coverView.setVisibility(VISIBLE);}} else {if (coverView.getVisibility() == VISIBLE) {coverView.setVisibility(INVISIBLE);}}}}});}@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {if (codecManager == null) {codecManager = new DJICodecManager(this.getContext(),surface,width,height,isPrimaryVideoFeed? UsbAccessoryService.VideoStreamSource.Camera: UsbAccessoryService.VideoStreamSource.Fpv);}}@Overridepublic void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {//Ignore}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {if (codecManager != null) {codecManager.cleanSurface();codecManager.destroyCodec();codecManager = null;}return false;}@Overridepublic void onSurfaceTextureUpdated(SurfaceTexture surface) {if (videoHeight != codecManager.getVideoHeight() || videoWidth != codecManager.getVideoWidth()) {videoWidth = codecManager.getVideoWidth();videoHeight = codecManager.getVideoHeight();adjustAspectRatio(videoWidth, videoHeight);}}//endregion//region Logicpublic 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;}public void changeSourceResetKeyFrame() {if (codecManager != null) {codecManager.resetKeyFrame();}}//endregion//region Helper method/*** This method should not to be called until the size of `TextureView` is fixed.*/private void adjustAspectRatio(int videoWidth, int videoHeight) {int viewWidth = this.getWidth();int viewHeight = this.getHeight();double aspectRatio = (double) videoHeight / videoWidth;int newWidth, newHeight;if (viewHeight > (int) (viewWidth * aspectRatio)) {// limited by narrow width; restrict heightnewWidth = viewWidth;newHeight = (int) (viewWidth * aspectRatio);} else {// limited by short height; restrict widthnewWidth = (int) (viewHeight / aspectRatio);newHeight = viewHeight;}int xoff = (viewWidth - newWidth) / 2;int yoff = (viewHeight - newHeight) / 2;Matrix txform = new Matrix();this.getTransform(txform);txform.setScale((float) newWidth / viewWidth, (float) newHeight / viewHeight);txform.postTranslate(xoff, yoff);this.setTransform(txform);}//endregion
}

12最重要的清单文件,有一大堆的权限和属性,大疆的注册等等,我测试的时候,是手动打开了app的所有的权限,你们在测试的时候,最好也手动打开,不要因为权限问题,导致无法推流成功,会非常尴尬

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"package="com.example.admin.ztest"><uses-permission android:name="android.permission.READ_LOGS"tools:ignore="ProtectedPermissions" /><!-- DJI SDK need permission --><uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.VIBRATE" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.WAKE_LOCK" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /><uses-permission android:name="android.permission.READ_PHONE_STATE" /><uses-feature android:name="android.hardware.camera" /><uses-feature android:name="android.hardware.camera.autofocus" /><uses-featureandroid:name="android.hardware.usb.host"android:required="false" /><uses-featureandroid:name="android.hardware.usb.accessory"android:required="true" /><!-- SDK requirement permission end --><applicationandroid:name=".MApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><!-- DJI SDK --><uses-library android:name="com.android.future.usb.accessory" /><meta-dataandroid:name="com.dji.sdk.API_KEY"android:value="your app key" /><activityandroid:name="dji.sdk.sdkmanager.DJIAoaControllerActivity"android:theme="@android:style/Theme.Translucent"></activity><service android:name="dji.sdk.sdkmanager.DJIGlobalService"></service><!-- DJI SDK --><activityandroid:name=".ConnectionActivity"android:configChanges="orientation"android:screenOrientation="portrait"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter><!--这个是点击usb配件弹框 要启动的activity--><intent-filter><action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /></intent-filter><meta-dataandroid:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"android:resource="@xml/accessory_filter" /></activity><activity android:name=".MainActivity" /><activity android:name=".BattryActivity" /><activity android:name="com.example.admin.ztest.live.LiveStreamView" /></application></manifest>

布局简单的贴两个吧,缺了点布局,请自己补:

1.activity_connection,这个是连接无人机的界面布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/text_connection_status"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignBottom="@+id/text_product_info"android:layout_centerHorizontal="true"android:layout_marginBottom="89dp"android:gravity="center"android:text="Status: No Product Connected"android:textColor="@android:color/black"android:textSize="20dp"android:textStyle="bold" /><TextViewandroid:id="@+id/text_product_info"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_marginTop="270dp"android:gravity="center"android:text="@string/product_information"android:textColor="@android:color/black"android:textSize="20dp"android:textStyle="bold" /><Buttonandroid:id="@+id/btn_open"android:layout_width="150dp"android:layout_height="55dp"android:layout_centerHorizontal="true"android:layout_marginTop="350dp"android:background="@drawable/round_btn"android:text="Open"android:textColor="@color/colorWhite"android:textSize="20dp" /><TextViewandroid:id="@+id/textView2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_marginTop="430dp"android:text="@string/sdk_version"android:textSize="15dp" /><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentTop="true"android:layout_centerHorizontal="true"android:layout_marginTop="58dp"android:text="DJIFPVDemo"android:textAppearance="?android:attr/textAppearanceSmall"android:textColor="@color/colorBlack"android:textSize="20dp"android:textStyle="bold" /></RelativeLayout>
</RelativeLayout>

2.这个是推流界面的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ScrollViewandroid:layout_width="match_parent"android:layout_height="wrap_content"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><com.example.admin.ztest.live.VideoFeedViewandroid:id="@+id/video_view_primary_video_feed"style="@style/main_camera_view"android:layout_alignParentBottom="false"android:layout_marginTop="30dp" /><com.example.admin.ztest.live.VideoFeedViewandroid:id="@+id/video_view_fpv_video_feed"style="@style/main_camera_view"android:layout_alignParentBottom="false"android:layout_below="@+id/video_view_primary_video_feed"android:layout_marginTop="10dp"android:visibility="gone" /><EditTextandroid:id="@+id/edit_live_show_url_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@+id/video_view_fpv_video_feed"android:inputType="textUri" /><Buttonandroid:id="@+id/btn_start_live_show"style="@style/common_button"android:layout_below="@id/edit_live_show_url_input"android:layout_toLeftOf="@+id/btn_enable_video_encode"android:text="Start Live Show" /><Buttonandroid:id="@+id/btn_enable_video_encode"style="@style/common_button"android:layout_below="@id/edit_live_show_url_input"android:layout_centerHorizontal="true"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:text="Enable Video Encode" /><Buttonandroid:id="@+id/btn_disable_video_encode"style="@style/common_button"android:layout_below="@id/edit_live_show_url_input"android:layout_toRightOf="@+id/btn_enable_video_encode"android:text="Disable Video Encode" /><Buttonandroid:id="@+id/btn_stop_live_show"style="@style/common_button"android:layout_below="@id/btn_start_live_show"android:layout_toLeftOf="@+id/btn_sound_on"android:text="Stop Live Show" /><Buttonandroid:id="@+id/btn_sound_on"style="@style/common_button"android:layout_below="@id/btn_start_live_show"android:layout_centerHorizontal="true"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:text="Sound On" /><Buttonandroid:id="@+id/btn_sound_off"style="@style/common_button"android:layout_below="@id/btn_start_live_show"android:layout_toRightOf="@+id/btn_sound_on"android:text="Sound Off" /><Buttonandroid:id="@+id/btn_is_live_show_on"style="@style/common_button"android:layout_below="@id/btn_sound_on"android:layout_toLeftOf="@+id/btn_show_info"android:text="Is Live Show On" /><Buttonandroid:id="@+id/btn_show_info"style="@style/common_button"android:layout_below="@id/btn_sound_on"android:layout_centerHorizontal="true"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:text="Show Info" /><Buttonandroid:id="@+id/btn_show_live_start_time"style="@style/common_button"android:layout_below="@id/btn_sound_on"android:layout_toRightOf="@+id/btn_show_info"android:text="Live Start Time" /><Buttonandroid:id="@+id/btn_show_current_video_source"style="@style/common_button"android:layout_below="@id/btn_show_info"android:layout_toLeftOf="@+id/btn_change_video_source"android:text="Show Video Source" /><Buttonandroid:id="@+id/btn_change_video_source"style="@style/common_button"android:layout_below="@id/btn_show_info"android:layout_centerHorizontal="true"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:text="Change Video Source" /></RelativeLayout></ScrollView>
</LinearLayout>

android大疆无人机直播推流VLC相关推荐

  1. 心得:大疆无人机RTMP推流直播(Windows版本已成功)

    大疆无人机rtmp推流直播到电脑(Windows版本已成功) 一.所需资源 二.安装 三.ffmpeg推流 四.无人机rtmp推流 五.Python获取无人机实时视频 一.所需资源 1.nginx的G ...

  2. 大疆无人机直播延迟的解决对策

    大疆无人机直播延迟的解决对策 这一个多周的时间,在使用大疆无人机御Air2 SDK 在进行直播的二次开发,把无人机的回传的图像,通过推流到服务器,然后拉流进行视频播放,在网上看到各种推流到流媒体服务器 ...

  3. Android 大疆无人机Mobile Sdk开发,如何输出Log日志

    职场小白迷上优美句子: <断章> -- 卞之琳 你在桥上看风景, 看风景的人在楼上看你. 明月装饰了你的窗子, 你装饰了别人的梦. 通过大疆 "桥" 连接,查看日志 精 ...

  4. Android大疆无人机对接大牛直播sdk视频H.264码推流

    前期准备 1:app/src/main下建立JniLibs文件包 复制大牛工程的so包以及jar包 jar包右键导入项目出现'>' 2:java下建立com.daniulive.smartpub ...

  5. Android大疆无人机对接声网sdk

    项目下gradle导入 implementation 'io.agora.rtc:agora-full-beta:4.0.0-beta.1' 版本根据自身修改 public class AgoraUt ...

  6. 自定义一个VideoCapturer(WebRTC)用于获取大疆无人机实时视频

    WebRTC做大疆无人机直播 大疆带屏遥控器有直播功能,用的是rtmp,但是延时有点大,所以在遥控器里安装自己的软件,用webrtc来做一个无人机视频实时传输.需要自定义一个VideoCapturer ...

  7. 大疆无人机基于RTMP服务推流直播

    流程:配置nginx服务器--->打开服务器---->配置无人机rtmp地址,将无人机画面推流到服务器上---->运行vlc从服务器上拉取视频流播放. 学习视频链接(可借鉴):htt ...

  8. 大疆无人机推流至EasyCVR平台出现画面模糊是什么原因?

    EasyCVR视频融合云服务支持接入多协议(标准协议如GB/T28181.RTMP.RTSP/Onvif,与私有协议,包括海康Ehome协议.海康SDK.大华SDK等).多类型的设备,以及能分发RTS ...

  9. 大疆无人机自定义直播

    大疆无人机自定义直播

最新文章

  1. 用Async函数简化异步代码
  2. Mac下的比较器工具DeltaWalker的试用期延长法
  3. Python自动化开发学习15-css补充内容
  4. Windows与Linux下tftp服务的使用
  5. lol云顶之奕助手_关于云顶之奕的感想
  6. php输出语句中怎么嵌套计算,PHP嵌套输出缓冲代码实例
  7. 达芬奇调色软件被曝两个远程代码执行缺陷
  8. RSAC|苹果设备的最大威胁是逆向后重部署的恶意软件
  9. matlab实现m-k突变的,用matlab进行mk趋势分析与突变检验.pdf
  10. VUE调用打印机打印页面
  11. Altium Designer 19卡顿的解决方法
  12. uIP编译时配置选项
  13. STM32开发,串口和PC机通信(串口中断、FIFO机制),安富莱+正点原子程序合并
  14. mysql 5.7 lbs_使用mysql来实现lbs(地理位置服务)功能
  15. 软件开发外包:你有什么选择
  16. 矩阵理论(二)特征值分解和SVD分解
  17. Android自定义View之数字密码锁
  18. 贝叶斯网络(BN)的理解
  19. [Jzoj] 3461. 小麦亩产一千八
  20. MySQL数据库——数据库系统是什么?它由哪几部分组成?

热门文章

  1. Spring+quartz 动态任务调度
  2. 计算机里显示不出来桌面内容,电脑中毒了,开机后桌面上什么都显示不出来,是怎么回事啊?...
  3. NPS:使用 Windows NPS Server 部署 802.1X 无线认证(1)
  4. 单片机STC89C52RC实现矩阵键盘(汇编语言版)
  5. 天蓝色在ps中的色值_加强天蓝色政策
  6. Node微服务之Seneca的使用
  7. PV、UV、IP含义及关系
  8. kali 虚拟机下 mana-toolkit 的安装
  9. error: C2039: “staticMetaObject”: 不是“QGraphicsItem”的成员
  10. 3.1 51单片机-LED灯模块