WebRTC做大疆无人机直播

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

1、自定义一个CapturerAircraft类来实现VideoCapturer接口

public class CapturerDefault implements VideoCapturer {@Overridepublic void initialize(SurfaceTextureHelper surfaceTextureHelper, Context context, CapturerObserver capturerObserver) {}@Overridepublic void startCapture(int i, int i1, int i2) {}@Overridepublic void stopCapture() throws InterruptedException {}@Overridepublic void changeCaptureFormat(int i, int i1, int i2) {}@Overridepublic void dispose() {}@Overridepublic boolean isScreencast() {return false;}
}

2、构造函数传入DJICodecManager参数

    public CapturerAircraft(DJICodecManager manager) {dji_code_manager_ = manager;dji_code_manager_.resetKeyFrame();if (isM300Product()) {OcuSyncLink ocuSyncLink = DJILogIn.getProductInstance().getAirLink().getOcuSyncLink();// If your MutltipleLensCamera is set at right or top, you need to change the PhysicalSource to RIGHT_CAM or TOP_CAM.ocuSyncLink.assignSourceToPrimaryChannel(PhysicalSource.LEFT_CAM, PhysicalSource.FPV_CAM, new CommonCallbacks.CompletionCallback() {@Overridepublic void onResult(DJIError error) {if (error == null) {
//                        showToast("assignSourceToPrimaryChannel success.");} else {
//                        showToast("assignSourceToPrimaryChannel fail, reason: "+ error.getDescription());}}});}}public static boolean isM300Product() {if (DJISDKManager.getInstance().getProduct() == null) {return false;}Model model = DJISDKManager.getInstance().getProduct().getModel();return model == Model.MATRICE_300_RTK;}

3、initialize函数里面创建一个DJIYuvDataCallback实例

DJIYuvDataCallback的onYuvDataReceived函数用来接收每一帧的yuv数据,再实例化一个CapturerObserver对象,用来将无人机的视频捕获作为webrtc能使用的I420视频流

    @Overridepublic void initialize(SurfaceTextureHelper surfaceTextureHelper, Context context, CapturerObserver capturerObserver) {//For M300RTK, you need to actively request an I frame.dji_yuv_data_callback_ = new DJIYuvDataCallback();capturer_observer_ = capturerObserver;}private class DJIYuvDataCallback implements DJICodecManager.YuvDataCallback {@Overridepublic void onYuvDataReceived(MediaFormat mediaFormat, ByteBuffer byteBuffer, int dataSize, int width, int height) {if (is_rate_time) {//is_rate_time = false;JavaI420Buffer buffer = JavaI420Buffer.allocate(width, height);ByteBuffer dataY = buffer.getDataY();ByteBuffer dataU = buffer.getDataU();ByteBuffer dataV = buffer.getDataV();final byte[] bytes = new byte[dataSize];byteBuffer.get(bytes);int yLen = width * height;int uLen = (width + 1) / 2 * ((height + 1) / 2);int vLen = uLen;int color_format = mediaFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT);switch (color_format) {case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar://NV12byte[] ubytes = new byte[uLen];byte[] vbytes = new byte[vLen];for (int i = 0; i < uLen; i++) {ubytes[i] = bytes[yLen + 2 * i];vbytes[i] = bytes[yLen + 2 * i + 1];}dataY.put(bytes, 0, yLen);dataU.put(ubytes, 0, uLen);dataV.put(vbytes, 0, vLen);break;case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar://YUV420P=I420int yPos = 0;int uPos = yPos + yLen;int vPos = uPos + uLen;dataY.put(bytes, yPos, yLen);dataU.put(bytes, uPos, uLen);dataV.put(bytes, vPos, vLen);break;default:break;}long captureTimeNs = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime());VideoFrame videoFrame = new VideoFrame(buffer, 0, captureTimeNs);capturer_observer_.onFrameCaptured(videoFrame);videoFrame.release();}}}

4、startCapture中用setYuvDataCallback设置yuv帧回调

    @Overridepublic void startCapture(int width, int height, int framerate) {this.frame_width_ = width;this.frame_height_ = height;this.frame_rate_ = framerate;this.timer.schedule(this.tickTask, 0L, (long)(1000 / frame_rate_));dji_code_manager_.enabledYuvData(true);dji_code_manager_.setYuvDataCallback(dji_yuv_data_callback_);}

以上就完成CapturerAircraft了,完整代码如下:

package com.example.livebroadcastfordrone;import android.content.Context;
import android.media.MediaFormat;
import android.media.MediaCodecInfo;
import android.os.SystemClock;import org.webrtc.CapturerObserver;
import org.webrtc.JavaI420Buffer;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoFrame;import java.nio.ByteBuffer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;import dji.common.airlink.PhysicalSource;
import dji.common.error.DJIError;
import dji.common.product.Model;
import dji.common.util.CommonCallbacks;
import dji.sdk.airlink.OcuSyncLink;
import dji.sdk.codec.DJICodecManager;
import dji.sdk.sdkmanager.DJISDKManager;public class CapturerAircraft implements VideoCapturer{private DJICodecManager dji_code_manager_;private DJIYuvDataCallback dji_yuv_data_callback_;private CapturerObserver capturer_observer_;private int frame_width_;private int frame_height_;private int frame_rate_;private boolean is_rate_time = false;private final Timer timer = new Timer();private final TimerTask tickTask = new TimerTask() {public void run() {is_rate_time = true;}};public CapturerAircraft(DJICodecManager manager) {dji_code_manager_ = manager;dji_code_manager_.resetKeyFrame();if (isM300Product()) {OcuSyncLink ocuSyncLink = DJILogIn.getProductInstance().getAirLink().getOcuSyncLink();// If your MutltipleLensCamera is set at right or top, you need to change the PhysicalSource to RIGHT_CAM or TOP_CAM.ocuSyncLink.assignSourceToPrimaryChannel(PhysicalSource.LEFT_CAM, PhysicalSource.FPV_CAM, new CommonCallbacks.CompletionCallback() {@Overridepublic void onResult(DJIError error) {if (error == null) {
//                        showToast("assignSourceToPrimaryChannel success.");} else {
//                        showToast("assignSourceToPrimaryChannel fail, reason: "+ error.getDescription());}}});}}public static boolean isM300Product() {if (DJISDKManager.getInstance().getProduct() == null) {return false;}Model model = DJISDKManager.getInstance().getProduct().getModel();return model == Model.MATRICE_300_RTK;}@Overridepublic void initialize(SurfaceTextureHelper surfaceTextureHelper, Context context, CapturerObserver capturerObserver) {//For M300RTK, you need to actively request an I frame.dji_yuv_data_callback_ = new DJIYuvDataCallback();capturer_observer_ = capturerObserver;}private void checkNotDisposed() {}@Overridepublic void startCapture(int width, int height, int framerate) {this.frame_width_ = width;this.frame_height_ = height;this.frame_rate_ = framerate;this.timer.schedule(this.tickTask, 0L, (long)(1000 / frame_rate_));dji_code_manager_.enabledYuvData(true);dji_code_manager_.setYuvDataCallback(dji_yuv_data_callback_);}@Overridepublic void stopCapture() throws InterruptedException {this.timer.cancel();dji_code_manager_.enabledYuvData(false);dji_code_manager_.setYuvDataCallback(null);}@Overridepublic void changeCaptureFormat(int width, int height, int framerate) {}@Overridepublic void dispose() {}@Overridepublic boolean isScreencast() {return false;}private class DJIYuvDataCallback implements DJICodecManager.YuvDataCallback {@Overridepublic void onYuvDataReceived(MediaFormat mediaFormat, ByteBuffer byteBuffer, int dataSize, int width, int height) {if (is_rate_time) {//is_rate_time = false;JavaI420Buffer buffer = JavaI420Buffer.allocate(width, height);ByteBuffer dataY = buffer.getDataY();ByteBuffer dataU = buffer.getDataU();ByteBuffer dataV = buffer.getDataV();final byte[] bytes = new byte[dataSize];byteBuffer.get(bytes);int yLen = width * height;int uLen = (width + 1) / 2 * ((height + 1) / 2);int vLen = uLen;int color_format = mediaFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT);switch (color_format) {case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar://NV12byte[] ubytes = new byte[uLen];byte[] vbytes = new byte[vLen];for (int i = 0; i < uLen; i++) {ubytes[i] = bytes[yLen + 2 * i];vbytes[i] = bytes[yLen + 2 * i + 1];}dataY.put(bytes, 0, yLen);dataU.put(ubytes, 0, uLen);dataV.put(vbytes, 0, vLen);break;case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar://YUV420P=I420int yPos = 0;int uPos = yPos + yLen;int vPos = uPos + uLen;dataY.put(bytes, yPos, yLen);dataU.put(bytes, uPos, uLen);dataV.put(bytes, vPos, vLen);break;default:break;}long captureTimeNs = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime());VideoFrame videoFrame = new VideoFrame(buffer, 0, captureTimeNs);capturer_observer_.onFrameCaptured(videoFrame);videoFrame.release();}}}
}

注:DJICodecManager实例要在主线程中创建

自定义一个VideoCapturer(WebRTC)用于获取大疆无人机实时视频相关推荐

  1. SpringBoot获取大疆无人机的飞行数据

    一.项目前提 随着无人机技术的发展,细分市场领域的需求增长,无人机的应用正展现出越来越丰富的可能性.航拍.农业.植保.自拍.快递运输.灾难救援.观察野生动物.监控传染病.测绘.新闻报道.电力巡检.救灾 ...

  2. java获取大疆无人机飞行数据包括:无人机名称、海拔高度、经纬度信息、起飞状态、电池电量、飞行时间、上升速度、前行速度等飞行数据

    前言 无人机名称.海拔高度.经纬度信息.起飞状态.电池电量.飞行时间.上升速度.前行速度等飞行数据. 软件链接为:http://zjxf.kmdns.net:18701/dj-login   (测试账 ...

  3. 关于大疆无人机的Webrtc

    关于大疆无人机的Webrtc 阐述问题 流程说明 实现VideoCapturer接口 阐述问题 在做webrtc期间,都是获取本地的摄像头,那么问题来了,如果没有本地摄像头怎么办?就像大疆无人机,摄像 ...

  4. 给你一台大疆无人机,你能用来做点啥?(一)----------获取正射影像

    专业级航测对于处在象牙塔的学生来说或许有一定距离,但是近年来轻小型无人机的出现为我们另辟蹊径.作为一位伪GISer,今天在这里介绍如何用大疆无人机获取正射影像. ------------------- ...

  5. 三种方式获取大疆照片的EXIF/XMP信息(附测试代码)

    目录 软件方式 在线方式 Python方式 第一种:pyexiv2 第二种:pyexif 测试代码:三种方式获取大疆照片的EXIF/XMP信息(附测试代码) - 小锋学长生活大爆炸 (xfxuezha ...

  6. 大疆无人机安卓Mobile Sdk开发(二)连接无人机,获取无人机信息

    大疆无人机安卓Mobile Sdk开发(一)简单介绍 大疆无人机安卓Mobile Sdk开发(二)连接无人机,获取无人机信息 大疆无人机安卓Mobile Sdk开发(三)制定航点任务WaypointM ...

  7. java自定义一个方法,用于返回两个整数的和

    java自定义一个方法,用于返回两个整数的和 /*** 自定义一个方法* 用于返回两个整数的和*/ public class Test17 {public static int getSum(int ...

  8. 自己封装的一个js方法用于获取显示的星期和日期时间

    自己封装的一个js方法用于获取显示的星期和日期时间 /*** 获取用于显示的星期和日期时间* @param date* @returns {string}*/ function getWeek(dat ...

  9. 大疆无人机安卓Mobile Sdk开发(五)解决M300Rtk H20相机无法获取图片视频的问题

    大疆无人机安卓Mobile Sdk开发(一)简单介绍 大疆无人机安卓Mobile Sdk开发(二)连接无人机,获取无人机信息 大疆无人机安卓Mobile Sdk开发(三)制定航点任务WaypointM ...

最新文章

  1. C++ 笔记(03)— 命名空间(概念、定义、调用、using name 指令、嵌套命名空间)
  2. Glide和Govendor安装和使用
  3. web前端【第九篇】JS的DOM对象三
  4. 用apache的httpclient发请求和接受数据
  5. BBC“20世纪最伟大科学家”,屠呦呦入围,与爱因斯坦并列
  6. 程序员必备软技能之科技趋势(一)
  7. java复习系列[6] - Java集合
  8. 如何设计一个安全的对外接口,老司机总结了这几点...
  9. 前端开发---ppt展示页面评论区展示
  10. 怎么取消打开文件的安全警告?
  11. Python程序-打印九九乘法表
  12. Python自制日常办公辅助工具之:批量视频截图,子集固定尺寸截图+序列化命名
  13. 伴随矩阵介绍及C++实现
  14. Arcgis实现nc数据区域裁剪并输出nc文件
  15. 字节跳动开源隐私合规检测工具appshark
  16. 正版Mincraft登录问题:微软账号不能登录
  17. 怎样在word中批量替换文字?Word替换文字这一招你会吗?
  18. 【uniapp】如何设置单个页面背景颜色
  19. tornado完成一个简单的登录界面/图片的上传
  20. 台式计算机耳机有杂音怎么办,耳机有杂音,小编教你电脑耳机有杂音怎么办

热门文章

  1. matlab解方程的程序,matlab算法程序解方程.ppt
  2. -XX:ReservedCodeCacheSize设置太大导致regionserve无法启动
  3. eclipse引入jquery不起作用
  4. 记北京7月21日大雨日
  5. rr与hr_统计基本功:OR、RR和HR的区别和选择
  6. 如何挑选购买葡萄酒?
  7. activity状态保存的bundl对象存放位置的思考
  8. 「 30天整理 |2W字长篇」用一篇文章明确前端学习路线并构筑知识体系
  9. 公司能不能监控到我的微信聊天?
  10. 基于stm32的汽车酒精检测汽车防撞报警系统(实物图+源程序+原理图+PCB+参考论文)