add new module trafficLive and rename package of class
This commit is contained in:
2
.idea/gradle.xml
generated
2
.idea/gradle.xml
generated
@@ -13,6 +13,7 @@
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
<option value="$PROJECT_DIR$/foudations" />
|
||||
<option value="$PROJECT_DIR$/foudations/mogo-common" />
|
||||
<option value="$PROJECT_DIR$/foudations/mogo-httpdns" />
|
||||
<option value="$PROJECT_DIR$/foudations/mogo-live" />
|
||||
<option value="$PROJECT_DIR$/foudations/mogo-network" />
|
||||
@@ -25,7 +26,6 @@
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
<option name="useQualifiedModuleNames" value="true" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
@@ -42,6 +42,8 @@ dependencies {
|
||||
implementation rootProject.ext.dependencies.androidxconstraintlayout
|
||||
implementation rootProject.ext.dependencies.rxjava
|
||||
implementation rootProject.ext.dependencies.rxandroid
|
||||
// 从车机获取视频流
|
||||
implementation 'com.zhidao.carmanager:common:1.0.23@aar'
|
||||
|
||||
if (Boolean.valueOf(RELEASE)) {
|
||||
implementation "com.mogo.cloud:tanlu:${MOGO_TANLU_VERSION}"
|
||||
@@ -49,6 +51,7 @@ dependencies {
|
||||
} else {
|
||||
implementation project(":modules:mogo-tanlu")
|
||||
implementation project(":modules:mogo-realtime")
|
||||
implementation project(":foudations:mogo-live")
|
||||
}
|
||||
|
||||
annotationProcessor 'com.elegant.spi:compiler:1.0.3' //编译时库
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.cloud">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00020000"
|
||||
android:required="true" />
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
|
||||
<application
|
||||
android:name="com.mogo.cloud.MoGoApplication"
|
||||
android:allowBackup="true"
|
||||
@@ -17,29 +32,21 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".PassPortActivity"
|
||||
android:label="鉴权测试">
|
||||
|
||||
</activity>
|
||||
|
||||
android:name=".LivePushActivity"
|
||||
android:label="直播播放" />
|
||||
<activity
|
||||
android:name=".LivePlayActivity"
|
||||
android:label="直播推送" />
|
||||
<activity
|
||||
android:name="com.mogo.cloud.network.NetworkActivity"
|
||||
android:label="网络测试">
|
||||
|
||||
</activity>
|
||||
|
||||
android:label="网络测试" />
|
||||
<activity
|
||||
android:name=".RealTimeActivity"
|
||||
android:label="实时数据测试">
|
||||
|
||||
</activity>
|
||||
|
||||
android:label="实时数据测试" />
|
||||
<activity
|
||||
android:name=".RoadConditionActivity"
|
||||
android:label="路况服务">
|
||||
</activity>
|
||||
android:label="路况服务" />
|
||||
|
||||
</application>
|
||||
|
||||
|
||||
142
app/src/main/java/com/mogo/cloud/BaseLiveActivity.java
Normal file
142
app/src/main/java/com/mogo/cloud/BaseLiveActivity.java
Normal file
@@ -0,0 +1,142 @@
|
||||
package com.mogo.cloud;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.ToggleButton;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.mogo.cloud.util.YuvToolUtils;
|
||||
import com.zhidao.manager.camera.FrameBufferCallBack;
|
||||
import com.zhidao.manager.camera.ZDCameraManager;
|
||||
import com.zhidao.manager.camera.ZDCameraParams;
|
||||
|
||||
|
||||
public abstract class BaseLiveActivity extends AppCompatActivity {
|
||||
private String TAG = "TestCarRecorderLiveActivity";
|
||||
|
||||
private String yuvSavePath = "/sdcard/Movies/TestYuvNV12.yuv";
|
||||
|
||||
public static int cameraId = 0; // 要打开的摄像头的ID
|
||||
|
||||
public int videoWidth = 1280;
|
||||
public int videoHeight = 720;
|
||||
|
||||
// 开始直播
|
||||
protected ToggleButton btnLive;
|
||||
// 保存文件到本地
|
||||
private ToggleButton btnSaveFile;
|
||||
|
||||
// 相机数据预览
|
||||
protected SurfaceView surfaceView;
|
||||
|
||||
private ZDCameraManager zdCameraManager;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_live_push);
|
||||
|
||||
surfaceView = findViewById(R.id.surfaceView);
|
||||
btnLive = findViewById(R.id.btnLive);
|
||||
btnLive.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
Toast.makeText(getApplicationContext(), buttonView.getText(), Toast.LENGTH_SHORT).show();
|
||||
toggleLive(isChecked);
|
||||
});
|
||||
|
||||
btnSaveFile = findViewById(R.id.btnSaveFile);
|
||||
btnSaveFile.setOnCheckedChangeListener((btnSaveFile, isChecked) -> {
|
||||
Toast.makeText(getApplicationContext(), btnSaveFile.getText(), Toast.LENGTH_SHORT).show();
|
||||
YuvToolUtils.isSaveFile = isChecked;
|
||||
if (isChecked) {
|
||||
YuvToolUtils.connectYUVFile(yuvSavePath);
|
||||
}
|
||||
});
|
||||
|
||||
initCamer();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化相机
|
||||
*/
|
||||
private void initCamer() {
|
||||
zdCameraManager = ZDCameraManager.getInstance();
|
||||
zdCameraManager.init(getApplicationContext());
|
||||
ZDCameraParams camParam = zdCameraManager.getCameraParam();
|
||||
camParam.setStoragePath("/sdcard/DCIM/");
|
||||
camParam.setVideoWidth(0, videoWidth);
|
||||
camParam.setVideoHeight(0, videoHeight);
|
||||
zdCameraManager.setCameraParam(camParam);
|
||||
|
||||
// 这里是获取 YUV-NV12 格式的视频流
|
||||
ZDCameraManager.getInstance().setFrontBufferCallBack(new FrameBufferCallBack() {
|
||||
@Override
|
||||
public void onFrame(byte[] bytes, int i) {
|
||||
//Log.d(TAG, "duanmu OnFrame ,bytes:" + bytes + " i:" + i);
|
||||
// 回碉给业务侧进行处理
|
||||
// 这里对所有传入的YUV数据进行对应类型的转码,为I420
|
||||
byte[] yuv420p = YuvToolUtils.convertData(bytes, videoWidth, videoHeight, 4);
|
||||
|
||||
onVideoFrame(yuv420p, i);
|
||||
|
||||
// 保存yuv文件
|
||||
if (YuvToolUtils.isSaveFile) {
|
||||
try {
|
||||
// 往队列里面添加数据
|
||||
YuvToolUtils.queueYUV.put(yuv420p);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 这里是纯预览
|
||||
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.i(TAG, "addSurfaceCallBack id");
|
||||
zdCameraManager.startPreview(holder, cameraId, videoWidth, videoHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
zdCameraManager.stopPreview(cameraId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (zdCameraManager != null) {
|
||||
zdCameraManager.stopPreview(cameraId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* F 车机从相机获取的视频数据回调
|
||||
* <p>
|
||||
* TODO 一般在这里调用直播SDK的接口进行直播操作,一定要先卸载 ADAS com.zhidao.autopilot
|
||||
*
|
||||
* @param yuv420p YUV数据 I420
|
||||
* @param bytesLength 数据长度
|
||||
*/
|
||||
public abstract void onVideoFrame(byte[] yuv420p, int bytesLength);
|
||||
|
||||
|
||||
/**
|
||||
* 开关直播状态
|
||||
*
|
||||
* @param isLive true-开启直播,false-关闭直播
|
||||
*/
|
||||
public abstract void toggleLive(boolean isLive);
|
||||
|
||||
}
|
||||
101
app/src/main/java/com/mogo/cloud/LivePlayActivity.java
Normal file
101
app/src/main/java/com/mogo/cloud/LivePlayActivity.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package com.mogo.cloud;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
import android.widget.ToggleButton;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.mogo.cloud.live.listener.ILiveProgressListener;
|
||||
import com.mogo.cloud.live.manager.ZeGoLiveManager;
|
||||
import com.mogo.cloud.util.Devices;
|
||||
|
||||
|
||||
public class LivePlayActivity extends AppCompatActivity {
|
||||
private String TAG = "LiveActivity";
|
||||
|
||||
private SurfaceView surfaceView;
|
||||
private ToggleButton liveToggleBtn;
|
||||
private EditText etLookRoomId;
|
||||
|
||||
private boolean isLoginSuccess = false;
|
||||
private String mStreamId = "STREAM_ID_";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_live_play);
|
||||
surfaceView = findViewById(R.id.surfaceView);
|
||||
etLookRoomId = findViewById(R.id.etLookRoomId);
|
||||
liveToggleBtn = findViewById(R.id.liveToggleBtn);
|
||||
liveToggleBtn.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
Toast.makeText(getApplicationContext(), buttonView.getText(), Toast.LENGTH_SHORT).show();
|
||||
if (isChecked) {
|
||||
String roomId = etLookRoomId.getText().toString().trim();
|
||||
mStreamId = ZeGoLiveManager.STREAM_ID_PREFIX + roomId;
|
||||
ZeGoLiveManager.getInstance().init(this.getApplication(), null);
|
||||
ZeGoLiveManager.getInstance().loginRoom("F803EB2046PZD00140", roomId);
|
||||
ZeGoLiveManager.getInstance().setLiveProgressListener(listener);
|
||||
} else {
|
||||
ZeGoLiveManager.getInstance().stopLive(mStreamId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ILiveProgressListener listener = new ILiveProgressListener() {
|
||||
|
||||
@Override
|
||||
public void onConnecting() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(String roomId) {
|
||||
Log.i(TAG, "onConnected:" + roomId);
|
||||
isLoginSuccess = true;
|
||||
toggleLive(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisConnect() {
|
||||
Log.i(TAG, "onDisConnect:");
|
||||
isLoginSuccess = false;
|
||||
toggleLive(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDebugError(int errorCode, String funcName, String errorInfo) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRoomStreamUpdate(String streamId, boolean isLive) {
|
||||
Log.i(TAG, "onRoomStreamUpdate:" + streamId);
|
||||
if (streamId != null && isLive) {
|
||||
Toast.makeText(LivePlayActivity.this, "主播开始直播了", Toast.LENGTH_SHORT).show();
|
||||
mStreamId = streamId;
|
||||
} else {
|
||||
Toast.makeText(LivePlayActivity.this, "主播已离线", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void toggleLive(boolean isChecked) {
|
||||
Log.i(TAG, "toggleLive status : " + isChecked + " , mStreamId : " + mStreamId);
|
||||
if (isChecked) {
|
||||
ZeGoLiveManager.getInstance().startLive(mStreamId, surfaceView);
|
||||
} else {
|
||||
ZeGoLiveManager.getInstance().stopLive(mStreamId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
ZeGoLiveManager.getInstance().onDestroyLive();
|
||||
}
|
||||
}
|
||||
50
app/src/main/java/com/mogo/cloud/LivePushActivity.java
Normal file
50
app/src/main/java/com/mogo/cloud/LivePushActivity.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.mogo.cloud;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.mogo.cloud.live.manager.CameraFrameManager;
|
||||
import com.mogo.cloud.live.server.PushService;
|
||||
import com.mogo.cloud.util.Devices;
|
||||
|
||||
|
||||
/**
|
||||
* 推流页面
|
||||
*/
|
||||
public class LivePushActivity extends BaseLiveActivity {
|
||||
public static final String TAG = "PushActivity";
|
||||
|
||||
private boolean isLive = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoFrame(byte[] bytes, int bytesLength) {
|
||||
if (!isLive) {
|
||||
return;
|
||||
}
|
||||
//Log.i(TAG, "onVideoFrame byte length: " + bytesLength);
|
||||
CameraFrameManager.getInstance().notifyYUVData(bytes, 1280, 720, 3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleLive(boolean isLive) {
|
||||
Log.i(TAG, "toggleLive : " + isLive);
|
||||
this.isLive = isLive;
|
||||
if (isLive) {
|
||||
PushService.startService(this, PushService.ACTION_START_RTMP_PUSH, Devices.getSn());
|
||||
} else {
|
||||
PushService.startService(this, PushService.ACTION_STOP_RTMP_PUSH, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
PushService.startService(this, PushService.ACTION_STOP_RTMP_PUSH, null);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
private Button btnJumpNetWorkPort;
|
||||
private Button btnJumpRealTime;
|
||||
private Button btnJumpRoadCondition;
|
||||
private Button btnJumpLivePush;
|
||||
private Button btnJumpLivePlay;
|
||||
|
||||
private TextView tvSn;
|
||||
private TextView tvToken;
|
||||
@@ -57,6 +59,18 @@ public class MainActivity extends AppCompatActivity {
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
btnJumpLivePush = findViewById(R.id.btnJumpLivePush);
|
||||
btnJumpLivePush.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(MainActivity.this, LivePushActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
btnJumpLivePlay = findViewById(R.id.btnJumpLivePlay);
|
||||
btnJumpLivePlay.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(MainActivity.this, LivePlayActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
MoGoAiCloudClient.getInstance().addTokenCallbacks(new IMoGoTokenCallback() {
|
||||
@Override
|
||||
public void onTokenGot(String token, String sn) {
|
||||
|
||||
33
app/src/main/java/com/mogo/cloud/util/Devices.java
Normal file
33
app/src/main/java/com/mogo/cloud/util/Devices.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.mogo.cloud.util;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class Devices {
|
||||
|
||||
private static final String PROPERTIES = "android.os.SystemProperties";
|
||||
private static final String GSM_SERIAL = "gsm.serial";
|
||||
private static final String GET = "get";
|
||||
|
||||
public static String getSn(){
|
||||
return getSystemProperties(GSM_SERIAL);
|
||||
}
|
||||
|
||||
public static String getSystemProperties(String name ) {
|
||||
String value = "";
|
||||
try {
|
||||
Class< ? > c = Class.forName( PROPERTIES );
|
||||
Method get = c.getMethod( GET, String.class );
|
||||
value = (String) get.invoke( c, name );
|
||||
} catch ( ClassNotFoundException var3 ) {
|
||||
var3.printStackTrace();
|
||||
} catch ( NoSuchMethodException var4 ) {
|
||||
var4.printStackTrace();
|
||||
} catch ( InvocationTargetException var5 ) {
|
||||
var5.printStackTrace();
|
||||
} catch ( IllegalAccessException var6 ) {
|
||||
var6.printStackTrace();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
197
app/src/main/java/com/mogo/cloud/util/YuvToolUtils.java
Normal file
197
app/src/main/java/com/mogo/cloud/util/YuvToolUtils.java
Normal file
@@ -0,0 +1,197 @@
|
||||
package com.mogo.cloud.util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.zhidao.libyuv.Key;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* YUV与RGB转换工具类
|
||||
*/
|
||||
public class YuvToolUtils {
|
||||
private static final String TAG = "YuvToolUtils";
|
||||
|
||||
private static final int DST_WIDTH = 1280;
|
||||
private static final int DST_HEIGHT = 720;
|
||||
|
||||
private static byte[] dst = null;
|
||||
private static byte[] tmp = null;
|
||||
|
||||
/**
|
||||
* @param data origin yuv data
|
||||
* @param width data width
|
||||
* @param height data height
|
||||
* @param type 1:YV12 2:NV21 3:I420 4 nv12
|
||||
*/
|
||||
public static byte[] convertData(byte[] data, int width, int height, int type) {
|
||||
if (dst == null) {
|
||||
dst = new byte[DST_WIDTH * DST_HEIGHT * 3 / 2];
|
||||
}
|
||||
|
||||
if (tmp == null) {
|
||||
tmp = new byte[848 * 480 * 3 / 2];
|
||||
}
|
||||
|
||||
if (type == 4) {
|
||||
if (DST_WIDTH == width && DST_HEIGHT == height) {
|
||||
com.zhidao.libyuv.YuvUtils.NV12ToI420(data, dst, width, height);
|
||||
return dst;
|
||||
} else {
|
||||
com.zhidao.libyuv.YuvUtils.NV12ToI420(data, tmp, width, height);
|
||||
com.zhidao.libyuv.YuvUtils.I420Scale(tmp, width, height, dst, DST_WIDTH, DST_HEIGHT, Key.SCALE_MODE_NONE, false);
|
||||
return dst;
|
||||
}
|
||||
} else if (type == 3) {
|
||||
if (DST_WIDTH == width && DST_HEIGHT == height) {
|
||||
return data;
|
||||
} else {
|
||||
com.zhidao.libyuv.YuvUtils.I420Scale(data, width, height, dst, DST_WIDTH, DST_HEIGHT, Key.SCALE_MODE_NONE, false);
|
||||
return dst;
|
||||
}
|
||||
} else if (type == 2) {
|
||||
if (DST_WIDTH == width && DST_HEIGHT == height) {
|
||||
com.zhidao.libyuv.YuvUtils.NV21ToI420(data, dst, width, height, false);
|
||||
return dst;
|
||||
} else {
|
||||
com.zhidao.libyuv.YuvUtils.NV21ToI420(data, tmp, width, height, false);
|
||||
com.zhidao.libyuv.YuvUtils.I420Scale(tmp, width, height, dst, DST_WIDTH, DST_HEIGHT, Key.SCALE_MODE_NONE, false);
|
||||
return dst;
|
||||
}
|
||||
} else if (type == 1) {
|
||||
if (DST_WIDTH == width && DST_HEIGHT == height) {
|
||||
swapYV12toI420(data, dst, width, height);
|
||||
return dst;
|
||||
} else {
|
||||
swapYV12toI420(data, tmp, width, height);
|
||||
com.zhidao.libyuv.YuvUtils.I420Scale(tmp, width, height, dst, DST_WIDTH, DST_HEIGHT, Key.SCALE_MODE_NONE, false);
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public static synchronized void release() {
|
||||
dst = null;
|
||||
tmp = null;
|
||||
Log.d(TAG, "release 释放临时帧");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 YV12 格式转换为 I420
|
||||
*
|
||||
* @param yv12bytes 原始格式数据
|
||||
* @param i420bytes 转出格式数据
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
*/
|
||||
public static void swapYV12toI420(byte[] yv12bytes, byte[] i420bytes, int width, int height) {
|
||||
System.arraycopy(yv12bytes, 0, i420bytes, 0, width * height);
|
||||
int srcPos = width * height + width * height / 4;
|
||||
// 这里利用的是 YV12 和 I420 存储特性 ,将 U 和 V 数据进行调换
|
||||
System.arraycopy(yv12bytes, srcPos, i420bytes, width * height, width * height / 4);
|
||||
System.arraycopy(yv12bytes, width * height, i420bytes, srcPos, width * height / 4);
|
||||
}
|
||||
|
||||
|
||||
public static ArrayList<byte[]> splitYUVI420(byte[] i420bytes, int width, int height) {
|
||||
|
||||
ArrayList<byte[]> yuvList = new ArrayList<>();
|
||||
|
||||
byte[] yData = new byte[width * height];
|
||||
byte[] uData = new byte[width * height / 4];
|
||||
byte[] vData = new byte[width * height / 4];
|
||||
|
||||
System.arraycopy(i420bytes, 0, yData, 0, width * height);
|
||||
System.arraycopy(i420bytes, width * height, uData, 0, width * height / 4);
|
||||
System.arraycopy(i420bytes, width * height + width * height / 4, vData, 0, width * height / 4);
|
||||
|
||||
yuvList.add(yData);
|
||||
yuvList.add(uData);
|
||||
yuvList.add(vData);
|
||||
|
||||
return yuvList;
|
||||
}
|
||||
|
||||
|
||||
public static LinkedBlockingQueue<byte[]> queueYUV = new LinkedBlockingQueue<>();
|
||||
public static boolean isSaveFile = true;
|
||||
|
||||
public static void connectYUVFile(String filePath) {
|
||||
Thread writeThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
boolean isOk = file.createNewFile();
|
||||
if (isOk) {
|
||||
Log.d(TAG, filePath + " 文件创建成功");
|
||||
} else {
|
||||
Log.w(TAG, filePath + " 文件已经存在");
|
||||
}
|
||||
}
|
||||
FileOutputStream out = new FileOutputStream(filePath);
|
||||
// 子线程中死循环
|
||||
while (isSaveFile) {
|
||||
// 拿数据
|
||||
byte[] data = queueYUV.take();
|
||||
// 往本地文件写入
|
||||
out.write(data);
|
||||
// 刷新
|
||||
out.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
writeThread.start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 写入文件
|
||||
*
|
||||
* @param path 文件路径
|
||||
* @param data YUV 数据
|
||||
* @return 是否写入成功
|
||||
*/
|
||||
public static boolean writeFile(String path, byte[] data) {
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
File file = new File(path);
|
||||
File parent = file.getParentFile();
|
||||
if (parent != null && !parent.exists())
|
||||
parent.mkdirs();
|
||||
if (!file.exists()) {
|
||||
boolean isOk = file.createNewFile();
|
||||
if (isOk) {
|
||||
out = new FileOutputStream(path);
|
||||
out.write(data);
|
||||
FileDescriptor fd = out.getFD();
|
||||
fd.sync();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
Log.d(TAG, path + " 文件已经存在");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (out != null)
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
35
app/src/main/res/layout/activity_live_play.xml
Normal file
35
app/src/main/res/layout/activity_live_play.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".LivePlayActivity">
|
||||
|
||||
<SurfaceView
|
||||
android:id="@+id/surfaceView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etLookRoomId"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="请输入要查看的车机SN"
|
||||
android:text="F803EB2046PZD00149"
|
||||
android:textColor="#FFFF"
|
||||
app:layout_constraintBottom_toTopOf="@+id/liveToggleBtn"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/liveToggleBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textOff="开始拉流"
|
||||
android:textOn="停止拉流"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
50
app/src/main/res/layout/activity_live_push.xml
Normal file
50
app/src/main/res/layout/activity_live_push.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<SurfaceView
|
||||
android:id="@+id/surfaceView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<!-- <com.baidu.rtc.RTCVideoView-->
|
||||
<!-- android:id="@+id/rtcView"-->
|
||||
<!-- android:layout_width="860px"-->
|
||||
<!-- android:layout_height="540px"-->
|
||||
<!-- android:layout_below="@+id/surfaceView" />-->
|
||||
|
||||
<!-- <com.baidu.rtc.RTCVideoView-->
|
||||
<!-- android:id="@+id/rtcRemoteView"-->
|
||||
<!-- android:layout_width="900px"-->
|
||||
<!-- android:layout_height="540px"-->
|
||||
<!-- android:layout_alignParentRight="true"-->
|
||||
<!-- android:layout_below="@+id/surfaceView" />-->
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/flTestPanel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/btnLive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:textOff="@string/start"
|
||||
android:textOn="@string/stop" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/btnSaveFile"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:textOff="开始录制"
|
||||
android:textOn="停止录制" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -65,6 +65,17 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="路况服务测试" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnJumpLivePush"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="直播SDK推流测试" />
|
||||
<Button
|
||||
android:id="@+id/btnJumpLivePlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="直播SDK观看测试" />
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
@@ -1,3 +1,8 @@
|
||||
<resources>
|
||||
<string name="app_name">MoGoAiCloudSdk</string>
|
||||
|
||||
|
||||
<string name="start">开始直播</string>
|
||||
<string name="stop">停止直播</string>
|
||||
|
||||
</resources>
|
||||
@@ -86,25 +86,19 @@ ext {
|
||||
// crash
|
||||
crashSdk : "com.zhidaoauto.crash.log:library:1.0.5",
|
||||
|
||||
//探路
|
||||
videoarmv7 : "com.shuyu:gsyVideoPlayer-armv7a:7.1.2",
|
||||
videoarm64 : "com.shuyu:gsyVideoPlayer-arm64:7.1.2",
|
||||
videojava : "com.shuyu:gsyVideoPlayer-java:7.1.2",
|
||||
eventbus : "org.greenrobot:eventbus:3.1.1",
|
||||
videoprocessor : "com.zhidao.video:video-processor:1.0.2.1",
|
||||
livesdk : "com.tencent.liteavsdk:LiteAVSDK_Smart:7.4.9211",
|
||||
|
||||
// 直播SDK
|
||||
live_sdk_zego : "im.zego:express-video:2.0.1",
|
||||
|
||||
//
|
||||
coroutinescore : "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3",
|
||||
coroutinesandroid : "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3",
|
||||
|
||||
aspectj : "org.aspectj:aspectjrt:1.8.9",
|
||||
|
||||
adasapi : "com.zhidao.autopilot.support:adas:1.0.6.6",
|
||||
adasconfigapi : "com.zhidao.adasconfig:adasconfig:1.1.5.2",
|
||||
|
||||
// 个人中心的SDK
|
||||
personalsdk : "com.zhidaoauto.person.info:data:1.0.1",
|
||||
|
||||
// obu sdk
|
||||
obusdk : "com.zhidao.enterprise.smartv2x:smartv2x:1.0.0.3",
|
||||
|
||||
|
||||
1
foudations/mogo-common/.gitignore
vendored
Normal file
1
foudations/mogo-common/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
36
foudations/mogo-common/build.gradle
Normal file
36
foudations/mogo-common/build.gradle
Normal file
@@ -0,0 +1,36 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.android.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.android.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.android.targetSdkVersion
|
||||
|
||||
versionCode 1
|
||||
versionName "${MOGO_LIVE_VERSION}"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation rootProject.ext.dependencies.androidxappcompat
|
||||
|
||||
}
|
||||
0
foudations/mogo-common/consumer-rules.pro
Normal file
0
foudations/mogo-common/consumer-rules.pro
Normal file
4
foudations/mogo-common/gradle.properties
Normal file
4
foudations/mogo-common/gradle.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
GROUP=com.mogo.cloud
|
||||
POM_ARTIFACT_ID=common
|
||||
VERSION_CODE=1
|
||||
VERSION_NAME=1.0.2-SNAPSHOT
|
||||
21
foudations/mogo-common/proguard-rules.pro
vendored
Normal file
21
foudations/mogo-common/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
5
foudations/mogo-common/src/main/AndroidManifest.xml
Normal file
5
foudations/mogo-common/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.cloud.common">
|
||||
|
||||
/
|
||||
</manifest>
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.mogo.cloud.common.utils;
|
||||
|
||||
/**
|
||||
* 设备信息
|
||||
*/
|
||||
public class DevicesUtils {
|
||||
}
|
||||
@@ -20,11 +20,32 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation rootProject.ext.dependencies.androidxappcompat
|
||||
implementation rootProject.ext.dependencies.live_sdk_zego
|
||||
|
||||
api 'com.zhidao.libyuv:libyuv:1.0.1.0'
|
||||
implementation 'com.zhidao.ptech:connsvr-protoco:0.1.23'
|
||||
implementation 'com.google.protobuf:protobuf-java:3.5.1'
|
||||
|
||||
if (Boolean.valueOf(RELEASE)) {
|
||||
implementation "com.mogo.cloud:network:${MOGO_NETWORK_VERSION}"
|
||||
implementation "com.mogo.cloud:socket:${MOGO_SOCKET_VERSION}"
|
||||
} else {
|
||||
implementation project(":foudations:mogo-network")
|
||||
implementation project(":foudations:mogo-socket")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.cloud.live">
|
||||
|
||||
/
|
||||
<application>
|
||||
<!--直播推流服务-->
|
||||
<service android:name=".server.PushService" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.mogo.cloud.live.listener;
|
||||
|
||||
/**
|
||||
* 直播进度监听
|
||||
*/
|
||||
public interface ILiveProgressListener {
|
||||
|
||||
/**
|
||||
* 开始
|
||||
*/
|
||||
default void onStart() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束
|
||||
*/
|
||||
default void onStop() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接成功
|
||||
*/
|
||||
void onConnecting();
|
||||
|
||||
/**
|
||||
* 连接成功
|
||||
*
|
||||
* @param roomId 房间ID
|
||||
*/
|
||||
void onConnected(String roomId);
|
||||
|
||||
/**
|
||||
* 断开连接
|
||||
*/
|
||||
void onDisConnect();
|
||||
|
||||
/**
|
||||
* 当房间数据流更新
|
||||
*
|
||||
* @param streamId 直播流ID
|
||||
* @param isLive 是否是直播
|
||||
*/
|
||||
default void onRoomStreamUpdate(String streamId, boolean isLive) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试错误信息回调
|
||||
*
|
||||
* @param errorCode 错误码,详情请参考 常见错误码文档 https://doc-zh.zego.im/zh/4378.html
|
||||
* @param funcName 函数名
|
||||
* @param errorInfo 错误的详细信息
|
||||
*/
|
||||
void onDebugError(int errorCode, String funcName, String errorInfo);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.mogo.cloud.live.listener;
|
||||
|
||||
/**
|
||||
* 直播状态回调用
|
||||
*/
|
||||
public interface ILiveStatusListener {
|
||||
void onChange(String camId, int status);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.mogo.cloud.live.listener;
|
||||
|
||||
/**
|
||||
* YUV数据回调
|
||||
*/
|
||||
public interface IYUVDataListener {
|
||||
void onFrame(byte[] data);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.mogo.cloud.live.manager;
|
||||
|
||||
|
||||
import com.mogo.cloud.live.listener.IYUVDataListener;
|
||||
import com.zhidao.libyuv.Key;
|
||||
import com.zhidao.libyuv.YuvUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 视频帧管理
|
||||
*/
|
||||
public class CameraFrameManager {
|
||||
private static final int DST_WIDTH = 1280;
|
||||
private static final int DST_HEIGHT = 720;
|
||||
|
||||
byte[] dst = null;
|
||||
byte[] tmp = null;
|
||||
|
||||
private final Set<IYUVDataListener> globalDataListener = new HashSet<>();
|
||||
|
||||
public static CameraFrameManager getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
private static final class SingletonHolder {
|
||||
private static final CameraFrameManager INSTANCE = new CameraFrameManager();
|
||||
}
|
||||
|
||||
public void notifyYUVData(byte[] data, int width, int height, int type) {
|
||||
dispatchData(handleData(data, width, height, type));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data origin yuv data
|
||||
* @param width data width
|
||||
* @param height data height
|
||||
* @param type 1:YV12 2:NV21 3:I420 4 nv12
|
||||
*/
|
||||
private byte[] handleData(byte[] data, int width, int height, int type) {
|
||||
if (dst == null) {
|
||||
dst = new byte[DST_WIDTH * DST_HEIGHT * 3 / 2];
|
||||
}
|
||||
|
||||
if (tmp == null) {
|
||||
tmp = new byte[848 * 480 * 3 / 2];
|
||||
}
|
||||
|
||||
if (type == 4) {
|
||||
if (DST_WIDTH == width && DST_HEIGHT == height) {
|
||||
YuvUtils.NV12ToI420(data, dst, width, height);
|
||||
return dst;
|
||||
} else {
|
||||
YuvUtils.NV12ToI420(data, tmp, width, height);
|
||||
YuvUtils.I420Scale(tmp, width, height, dst, DST_WIDTH, DST_HEIGHT, Key.SCALE_MODE_NONE, false);
|
||||
return dst;
|
||||
}
|
||||
} else if (type == 3) {
|
||||
if (DST_WIDTH == width && DST_HEIGHT == height) {
|
||||
return data;
|
||||
} else {
|
||||
YuvUtils.I420Scale(data, width, height, dst, DST_WIDTH, DST_HEIGHT, Key.SCALE_MODE_NONE, false);
|
||||
return dst;
|
||||
}
|
||||
} else if (type == 2) {
|
||||
if (DST_WIDTH == width && DST_HEIGHT == height) {
|
||||
YuvUtils.NV21ToI420(data, dst, width, height, false);
|
||||
return dst;
|
||||
} else {
|
||||
YuvUtils.NV21ToI420(data, tmp, width, height, false);
|
||||
YuvUtils.I420Scale(tmp, width, height, dst, DST_WIDTH, DST_HEIGHT, Key.SCALE_MODE_NONE, false);
|
||||
return dst;
|
||||
}
|
||||
} else if (type == 1) {
|
||||
if (DST_WIDTH == width && DST_HEIGHT == height) {
|
||||
swapYV12toI420(data, dst, width, height);
|
||||
return dst;
|
||||
} else {
|
||||
swapYV12toI420(data, tmp, width, height);
|
||||
YuvUtils.I420Scale(tmp, width, height, dst, DST_WIDTH, DST_HEIGHT, Key.SCALE_MODE_NONE, false);
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public synchronized void addYuvDataCallback(IYUVDataListener callback) {
|
||||
if (callback != null) {
|
||||
globalDataListener.add(callback);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void rmYuvDataCallback(IYUVDataListener callback) {
|
||||
if (callback != null) {
|
||||
globalDataListener.remove(callback);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void release() {
|
||||
dst = null;
|
||||
tmp = null;
|
||||
}
|
||||
|
||||
private void dispatchData(byte[] data) {
|
||||
if (globalDataListener != null && globalDataListener.size() > 0) {
|
||||
for (IYUVDataListener callback : globalDataListener) {
|
||||
if (callback != null) {
|
||||
callback.onFrame(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void swapYV12toI420(byte[] yv12bytes, byte[] i420bytes, int width, int height) {
|
||||
System.arraycopy(yv12bytes, 0, i420bytes, 0, width * height);
|
||||
System.arraycopy(yv12bytes, width * height + width * height / 4, i420bytes, width * height, width * height / 4);
|
||||
System.arraycopy(yv12bytes, width * height, i420bytes, width * height + width * height / 4, width * height / 4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.mogo.cloud.live.manager;
|
||||
|
||||
/**
|
||||
* 直播配置对象
|
||||
*/
|
||||
public class MGLivePushConfig {
|
||||
private int mWidth = 1280;
|
||||
private int mHeight = 720;
|
||||
private int mVideoBitrate = 1200;
|
||||
private int mVideoFPS = 20;
|
||||
private int mAudioSampleRate = 44100;
|
||||
private int mAudioChannels = 1;
|
||||
private int mAudioFormat = 2;
|
||||
private boolean isMute = true;
|
||||
|
||||
public MGLivePushConfig() {
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return this.mWidth;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.mWidth = width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return this.mHeight;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.mHeight = height;
|
||||
}
|
||||
|
||||
public int getVideoBitrate() {
|
||||
return this.mVideoBitrate;
|
||||
}
|
||||
|
||||
public void setVideoBitrate(int videoBitrate) {
|
||||
this.mVideoBitrate = videoBitrate;
|
||||
}
|
||||
|
||||
public int getVideoFPS() {
|
||||
return this.mVideoFPS;
|
||||
}
|
||||
|
||||
public void setVideoFPS(int videoFPS) {
|
||||
this.mVideoFPS = videoFPS;
|
||||
}
|
||||
|
||||
public int getAudioSampleRate() {
|
||||
return this.mAudioSampleRate;
|
||||
}
|
||||
|
||||
public void setAudioSampleRate(int audioSampleRate) {
|
||||
this.mAudioSampleRate = audioSampleRate;
|
||||
}
|
||||
|
||||
public int getAudioChannels() {
|
||||
return this.mAudioChannels;
|
||||
}
|
||||
|
||||
public void setAudioChannels(int audioChannels) {
|
||||
this.mAudioChannels = audioChannels;
|
||||
}
|
||||
|
||||
public int getAudioFormat() {
|
||||
return this.mAudioFormat;
|
||||
}
|
||||
|
||||
public void setAudioFormat(int audioFormat) {
|
||||
this.mAudioFormat = audioFormat;
|
||||
}
|
||||
|
||||
public boolean isMute() {
|
||||
return this.isMute;
|
||||
}
|
||||
|
||||
public void setMute(boolean mute) {
|
||||
this.isMute = mute;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,443 @@
|
||||
package com.mogo.cloud.live.manager;
|
||||
|
||||
import android.app.Application;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
import com.mogo.cloud.live.listener.ILiveProgressListener;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import im.zego.zegoexpress.ZegoExpressEngine;
|
||||
import im.zego.zegoexpress.callback.IZegoCustomVideoCaptureHandler;
|
||||
import im.zego.zegoexpress.callback.IZegoEventHandler;
|
||||
import im.zego.zegoexpress.constants.ZegoEngineState;
|
||||
import im.zego.zegoexpress.constants.ZegoNetworkMode;
|
||||
import im.zego.zegoexpress.constants.ZegoPlayerState;
|
||||
import im.zego.zegoexpress.constants.ZegoPublishChannel;
|
||||
import im.zego.zegoexpress.constants.ZegoPublisherState;
|
||||
import im.zego.zegoexpress.constants.ZegoRoomState;
|
||||
import im.zego.zegoexpress.constants.ZegoScenario;
|
||||
import im.zego.zegoexpress.constants.ZegoUpdateType;
|
||||
import im.zego.zegoexpress.constants.ZegoVideoBufferType;
|
||||
import im.zego.zegoexpress.constants.ZegoVideoCodecID;
|
||||
import im.zego.zegoexpress.constants.ZegoVideoFrameFormat;
|
||||
import im.zego.zegoexpress.constants.ZegoViewMode;
|
||||
import im.zego.zegoexpress.entity.ZegoCanvas;
|
||||
import im.zego.zegoexpress.entity.ZegoCustomVideoCaptureConfig;
|
||||
import im.zego.zegoexpress.entity.ZegoEngineConfig;
|
||||
import im.zego.zegoexpress.entity.ZegoLogConfig;
|
||||
import im.zego.zegoexpress.entity.ZegoPlayStreamQuality;
|
||||
import im.zego.zegoexpress.entity.ZegoStream;
|
||||
import im.zego.zegoexpress.entity.ZegoUser;
|
||||
import im.zego.zegoexpress.entity.ZegoVideoConfig;
|
||||
import im.zego.zegoexpress.entity.ZegoVideoFrameParam;
|
||||
|
||||
/**
|
||||
* 即构直播管理
|
||||
*/
|
||||
public class ZeGoLiveManager {
|
||||
public static final String TAG = "ZeGoLiveManager";
|
||||
|
||||
/**
|
||||
* 即构平台分配 APP_ID
|
||||
*/
|
||||
private long appId = 1759603340;
|
||||
/**
|
||||
* 即构平台分配的 APP_KEY
|
||||
*/
|
||||
private String appKey = "a9b02ca6122c585b434c498417bb623c5e9f07c71e8a1b5143dc3dc2e9e4439d";
|
||||
/**
|
||||
* 存储即构的日志路径
|
||||
*/
|
||||
private static final String ZEGO_LOG_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/ZegoLog";
|
||||
/**
|
||||
* 直播推送配置对象
|
||||
*/
|
||||
private MGLivePushConfig mLivePushConfig;
|
||||
/**
|
||||
* 直播会议ID,使用推流端的车机信息建立房间ID,这里采用格式为:ROOM_ID_车机SN编号
|
||||
*/
|
||||
public static final String ROOM_ID_PREFIX = "ROOM_ID_";
|
||||
/**
|
||||
* 直播用户名称的
|
||||
*/
|
||||
public static final String NAME_PREFIX = "MoGoCar_";
|
||||
/**
|
||||
* 直播流ID,发起推送的直播流ID
|
||||
*/
|
||||
public static final String STREAM_ID_PREFIX = "STREAM_ID_"; //+ "_" + System.currentTimeMillis()
|
||||
/**
|
||||
* 定义 即构SDK 引擎对象
|
||||
*/
|
||||
private ZegoExpressEngine mExpressEngine;
|
||||
/**
|
||||
* 创建自定义视频采集对象
|
||||
*/
|
||||
private ZegoCustomVideoCaptureConfig customVideoCaptureConfig;
|
||||
/**
|
||||
* 直播进度回调用
|
||||
*/
|
||||
private ILiveProgressListener listener;
|
||||
/**
|
||||
* 当前的房间ID
|
||||
*/
|
||||
private String currentRoomId = "";
|
||||
/**
|
||||
* 推送状态,true-推流中,false-没有推流
|
||||
*/
|
||||
private boolean isPushing;
|
||||
/**
|
||||
* 播放状态,true-播放中,false-没有播放
|
||||
*/
|
||||
private boolean isPlaying;
|
||||
|
||||
private static final class Holder {
|
||||
private static final ZeGoLiveManager manager = new ZeGoLiveManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取即构直播管理
|
||||
*/
|
||||
public static ZeGoLiveManager getInstance() {
|
||||
return Holder.manager;
|
||||
}
|
||||
|
||||
private ZeGoLiveManager() {
|
||||
}
|
||||
|
||||
public void setAppId(long appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
public void setAppKey(String appKey) {
|
||||
this.appKey = appKey;
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
return isPlaying;
|
||||
}
|
||||
|
||||
public boolean isPushing() {
|
||||
return isPushing;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化直播SDK
|
||||
*
|
||||
* @param application 上下文
|
||||
* @param livePushConfig 是否需要配置
|
||||
*/
|
||||
public void init(Application application, MGLivePushConfig livePushConfig) {
|
||||
mLivePushConfig = livePushConfig;
|
||||
// 通过 advancedConfig 设置 uuid 过滤字段,设置之后 SDK 只会抛出前 12 个字节为开发者所设置 uuid 的 SEI
|
||||
ZegoEngineConfig zegoEngineConfig = new ZegoEngineConfig();
|
||||
// 设置即构的直播日志配置
|
||||
ZegoLogConfig zegoLogConfig = new ZegoLogConfig();
|
||||
// 存储日志路径
|
||||
zegoLogConfig.logPath = ZEGO_LOG_PATH;
|
||||
// 配置日志到引擎中
|
||||
zegoEngineConfig.logConfig = zegoLogConfig;
|
||||
// 设置配置到引擎中
|
||||
ZegoExpressEngine.setEngineConfig(zegoEngineConfig);
|
||||
// 创建 enging 对象, appID, appSign 为开发者在 ZEGO 管理控制台申请的凭证信息,
|
||||
// 未上线的开发者 isTestEnvironment 为 true, application 为安卓应用的上下文
|
||||
mExpressEngine =
|
||||
ZegoExpressEngine.createEngine(
|
||||
appId,
|
||||
appKey,
|
||||
true,
|
||||
ZegoScenario.GENERAL,
|
||||
application,
|
||||
mEventHandler);
|
||||
if (mLivePushConfig != null) {
|
||||
// 创建自定义视频采集对象
|
||||
customVideoCaptureConfig = new ZegoCustomVideoCaptureConfig();
|
||||
// 设置自定义视频采集视频帧数据类型
|
||||
customVideoCaptureConfig.bufferType = ZegoVideoBufferType.RAW_DATA;
|
||||
// true 表示静音(关闭)
|
||||
mExpressEngine.muteMicrophone(true);
|
||||
// 开始或停止自定义视频采集,支持设置其他通道的推流
|
||||
mExpressEngine.enableCustomVideoCapture(true, customVideoCaptureConfig, ZegoPublishChannel.MAIN);
|
||||
// 设置自定义视频采集回调
|
||||
mExpressEngine.setCustomVideoCaptureHandler(new IZegoCustomVideoCaptureHandler() {
|
||||
@Override
|
||||
public void onStart(ZegoPublishChannel channel) {
|
||||
super.onStart(channel);
|
||||
Log.i(TAG, "setCustomVideoCaptureHandler onStart");
|
||||
if (listener != null) {
|
||||
listener.onStart();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(ZegoPublishChannel channel) {
|
||||
super.onStop(channel);
|
||||
Log.i(TAG, "setCustomVideoCaptureHandler onStop");
|
||||
if (listener != null) {
|
||||
listener.onStop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 视频配追
|
||||
ZegoVideoConfig zegoVideoConfig = new ZegoVideoConfig();
|
||||
// 采集分辨率,控制摄像头图像采集的分辨率。SDK 要求将宽和高设置为偶数。
|
||||
zegoVideoConfig.setCaptureResolution(mLivePushConfig.getWidth(), mLivePushConfig.getHeight());
|
||||
// 编码分辨率,控制编码器编码推流的图像分辨率。SDK 要求将宽和高设置为偶数。推流前后设置均可生效
|
||||
zegoVideoConfig.setEncodeResolution(mLivePushConfig.getWidth(), mLivePushConfig.getHeight());
|
||||
// 设置视频帧率
|
||||
zegoVideoConfig.setVideoFPS(mLivePushConfig.getVideoFPS());
|
||||
// 设置视频比特率
|
||||
zegoVideoConfig.setVideoBitrate(mLivePushConfig.getVideoBitrate());
|
||||
// 设置转码的ID,使用H.264
|
||||
zegoVideoConfig.setCodecID(ZegoVideoCodecID.DEFAULT);
|
||||
// 将视频信息设置到推流引擎
|
||||
mExpressEngine.setVideoConfig(zegoVideoConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件回调用
|
||||
*/
|
||||
private IZegoEventHandler mEventHandler = new IZegoEventHandler() {
|
||||
|
||||
@Override
|
||||
public void onDebugError(int errorCode, String funcName, String info) {
|
||||
super.onDebugError(errorCode, funcName, info);
|
||||
if (listener != null) {
|
||||
listener.onDebugError(errorCode, funcName, info);
|
||||
}
|
||||
// 停止推送数据
|
||||
stopPublishingStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEngineStateUpdate(ZegoEngineState state) {
|
||||
super.onEngineStateUpdate(state);
|
||||
Log.i(TAG, "onEngineStateUpdate state : " + state.name());
|
||||
if (state == ZegoEngineState.START) {
|
||||
if (listener != null) {
|
||||
listener.onStart();
|
||||
}
|
||||
} else {
|
||||
if (listener != null) {
|
||||
listener.onStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 房间状态变化通知
|
||||
@Override
|
||||
public void onRoomStateUpdate(String roomID, ZegoRoomState state, int errorCode, JSONObject extendedData) {
|
||||
super.onRoomStateUpdate(roomID, state, errorCode, extendedData);
|
||||
//房间状态更新
|
||||
if (state == ZegoRoomState.CONNECTING) {
|
||||
if (listener != null) {
|
||||
listener.onConnecting();
|
||||
}
|
||||
} else if (state == ZegoRoomState.CONNECTED) {
|
||||
if (listener != null) {
|
||||
listener.onConnected(roomID);
|
||||
}
|
||||
} else {
|
||||
if (listener != null) {
|
||||
listener.onDisConnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRoomUserUpdate(String roomID, ZegoUpdateType updateType, ArrayList<ZegoUser> userList) {
|
||||
super.onRoomUserUpdate(roomID, updateType, userList);
|
||||
//用户状态更新
|
||||
Log.i(TAG, "onRoomUserUpdate roomId 房间内其他用户增加或减少的通知回调: " + roomID + " , updateType : " + updateType.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRoomStreamUpdate(String roomID, ZegoUpdateType updateType, ArrayList<ZegoStream> streamList, JSONObject extendedData) {
|
||||
super.onRoomStreamUpdate(roomID, updateType, streamList, extendedData);
|
||||
//有用户新推送或者删除音视频时,更新流状态
|
||||
Log.i(TAG, "onRoomStreamUpdate roomId 相同房间内其他用户推的流增加或减少的通知: " + roomID + " , ZegoUpdateType : " + updateType.name());
|
||||
for (ZegoStream stream : streamList) {
|
||||
String streamID = stream.streamID;
|
||||
Log.i(TAG, "onRoomStreamUpdate streamId: " + streamID);
|
||||
if (listener != null && updateType == ZegoUpdateType.ADD) {
|
||||
listener.onRoomStreamUpdate(streamID, true);
|
||||
} else {
|
||||
listener.onRoomStreamUpdate(streamID, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPublisherStateUpdate(String streamID, ZegoPublisherState state,
|
||||
int errorCode, JSONObject extendedData) {
|
||||
super.onPublisherStateUpdate(streamID, state, errorCode, extendedData);
|
||||
Log.i(TAG, "onPublisherStateUpdate streamID 推流状态回调: " + streamID + " , state : " + state.name() + " , errorCode : " + errorCode);
|
||||
isPushing = state == ZegoPublisherState.PUBLISHING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStateUpdate(String streamID, ZegoPlayerState state, int errorCode, JSONObject extendedData) {
|
||||
super.onPlayerStateUpdate(streamID, state, errorCode, extendedData);
|
||||
Log.i(TAG, "onPlayerStateUpdate streamId 拉流状态变更回调: " + streamID + " , state : " + state.name() + " , errorCode : " + errorCode + " , extendData : " + extendedData.toString());
|
||||
isPlaying = state == ZegoPlayerState.PLAYING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerQualityUpdate(String streamID, ZegoPlayStreamQuality quality) {
|
||||
super.onPlayerQualityUpdate(streamID, quality);
|
||||
Log.i(TAG, "onPlayerQualityUpdate quality 拉流质量回调: " + quality.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkModeChanged(ZegoNetworkMode mode) {
|
||||
super.onNetworkModeChanged(mode);
|
||||
Log.i(TAG, "onNetworkModeChanged mode : " + mode.name());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 设置直播进度监听
|
||||
*
|
||||
* @param liveProgressListener 监听回调用
|
||||
*/
|
||||
public void setLiveProgressListener(ILiveProgressListener liveProgressListener) {
|
||||
this.listener = liveProgressListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁即构引擎
|
||||
*/
|
||||
private void destroyEngine() {
|
||||
ZegoExpressEngine.destroyEngine(() -> {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 进入房间
|
||||
*
|
||||
* @param userId 当前用户ID
|
||||
* @param roomId 要进入的房间ID
|
||||
*/
|
||||
public void loginRoom(String userId, String roomId) {
|
||||
currentRoomId = ROOM_ID_PREFIX + roomId;
|
||||
ZegoUser zegoUser = new ZegoUser(userId, NAME_PREFIX + userId);
|
||||
mExpressEngine.loginRoom(currentRoomId, zegoUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出房间
|
||||
*/
|
||||
private void logOutRoom() {
|
||||
mExpressEngine.logoutRoom(currentRoomId);
|
||||
mExpressEngine.enableCustomVideoCapture(false, customVideoCaptureConfig, ZegoPublishChannel.MAIN);
|
||||
mExpressEngine.setEventHandler(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始推送
|
||||
*/
|
||||
public void startPush(String streamId) {
|
||||
mExpressEngine.startPublishingStream(STREAM_ID_PREFIX + streamId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始直播
|
||||
*
|
||||
* @param streamId 直播流ID
|
||||
* @param surfaceView 渲染直播的视图
|
||||
*/
|
||||
public void startLive(String streamId, SurfaceView surfaceView) {
|
||||
ZegoCanvas zegoCanvas = new ZegoCanvas(surfaceView);
|
||||
zegoCanvas.viewMode = ZegoViewMode.SCALE_TO_FILL;
|
||||
mExpressEngine.startPlayingStream(streamId, zegoCanvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止直播
|
||||
*
|
||||
* @param streamId 数据流ID
|
||||
*/
|
||||
public void stopLive(String streamId) {
|
||||
mExpressEngine.stopPlayingStream(streamId);
|
||||
stopPreview();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始发布视频流
|
||||
*
|
||||
* @param byteBuffer 数据缓存
|
||||
* @param dataLength 数据长度
|
||||
* @param referenceTimeMillisecond 视频帧参考时间,UNIX时间戳,以毫秒为单位。音视频同步用的,
|
||||
*/
|
||||
public void startPublishingStream(ByteBuffer byteBuffer, int dataLength, long referenceTimeMillisecond) {
|
||||
ZegoVideoFrameParam zegoVideoFrameParam = new ZegoVideoFrameParam();
|
||||
zegoVideoFrameParam.format = ZegoVideoFrameFormat.I420;
|
||||
zegoVideoFrameParam.width = mLivePushConfig.getWidth();
|
||||
zegoVideoFrameParam.height = mLivePushConfig.getHeight();
|
||||
zegoVideoFrameParam.strides[0] = mLivePushConfig.getWidth();
|
||||
zegoVideoFrameParam.strides[1] = mLivePushConfig.getWidth();
|
||||
mExpressEngine.sendCustomVideoCaptureRawData(byteBuffer, dataLength, zegoVideoFrameParam, referenceTimeMillisecond);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束发布的直播
|
||||
*/
|
||||
public void onDestroyPublish() {
|
||||
stopPublish();
|
||||
logOutRoom();
|
||||
destroyEngine();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始预览
|
||||
*
|
||||
* @param surfaceView 预览承载视图
|
||||
*/
|
||||
public void startPreview(SurfaceView surfaceView) {
|
||||
ZegoCanvas zegoCanvas = new ZegoCanvas(surfaceView);
|
||||
zegoCanvas.viewMode = ZegoViewMode.SCALE_TO_FILL; //填充整个View
|
||||
mExpressEngine.startPreview(zegoCanvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止预览
|
||||
*/
|
||||
private void stopPreview() {
|
||||
ZegoExpressEngine.getEngine().stopPreview();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止推送
|
||||
*/
|
||||
public void stopPublish() {
|
||||
stopPublishingStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止推送数据
|
||||
*/
|
||||
private void stopPublishingStream() {
|
||||
// 停止推送
|
||||
ZegoExpressEngine.getEngine().stopPublishingStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止观看直播
|
||||
*/
|
||||
public void onDestroyLive() {
|
||||
stopPreview();
|
||||
logOutRoom();
|
||||
destroyEngine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.mogo.cloud.live.model;
|
||||
|
||||
import com.elegant.network.NetConstants;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class BaseData implements Serializable, Cloneable {
|
||||
|
||||
public int code = NetConstants.NO_DATA;
|
||||
public String msg;
|
||||
|
||||
public BaseData clone() {
|
||||
BaseData obj = null;
|
||||
try {
|
||||
obj = (BaseData) super.clone();
|
||||
} catch (CloneNotSupportedException var3) {
|
||||
var3.printStackTrace();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.mogo.cloud.live.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LiveStatusInfo extends BaseData{
|
||||
|
||||
public Result result;
|
||||
|
||||
public class Result {
|
||||
public int alarmStatus;
|
||||
public List<LiveData> list;
|
||||
}
|
||||
|
||||
public class LiveData {
|
||||
public int status; // 0开始 1 结束
|
||||
public String livePushUrl;
|
||||
public String videoChannel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mogo.cloud.live.model;
|
||||
|
||||
import com.zhidao.utils.common.TelephoneUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PlaybackModel {
|
||||
private String sn;
|
||||
private String caller;
|
||||
private List<VideoModel> videoList;
|
||||
|
||||
public PlaybackModel() {
|
||||
this.sn = TelephoneUtil.getSerialNumber();
|
||||
//this.caller = CarAlarmConstants.CALLER;
|
||||
}
|
||||
|
||||
public void setVideoList(List<VideoModel> videoList) {
|
||||
this.videoList = videoList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mogo.cloud.live.model;
|
||||
|
||||
public class ResqReportCarAlarm {
|
||||
public String userId;
|
||||
public String sn;
|
||||
public double lat;
|
||||
public double lng;
|
||||
public long clientTime;
|
||||
public String eventId;
|
||||
public String caller;
|
||||
public String sdkVersion;
|
||||
public CamStatus cameraStatus;
|
||||
|
||||
public static class CamStatus {
|
||||
public int C_1;
|
||||
public int C_2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.mogo.cloud.live.model;
|
||||
|
||||
public class StartCarAlarmInfo extends BaseData{
|
||||
public Result result;
|
||||
|
||||
public class Result {
|
||||
public String eventId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.mogo.cloud.live.model;
|
||||
|
||||
/**
|
||||
* 视频
|
||||
*/
|
||||
public class VideoModel {
|
||||
public String videoChannel;
|
||||
public long videoStartTime;
|
||||
public long videoEndTime;
|
||||
private String videoName;
|
||||
public String url;
|
||||
public int status;// 0 未备份 1 已备份 2 已删除,必传:上传元数据时为0,补充url时为1,文件已删除时为2
|
||||
public String localPath;
|
||||
|
||||
public void setVideoName(String path) {
|
||||
//this.videoName = CustomUtils.getFileName(path);
|
||||
}
|
||||
|
||||
public String getVideoName() {
|
||||
return videoName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.mogo.cloud.live.network;
|
||||
|
||||
import com.mogo.cloud.live.model.BaseData;
|
||||
import com.mogo.cloud.live.model.LiveStatusInfo;
|
||||
import com.mogo.cloud.live.model.StartCarAlarmInfo;
|
||||
|
||||
import okhttp3.RequestBody;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.Headers;
|
||||
import retrofit2.http.POST;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* 直播功能使用到的接口
|
||||
*/
|
||||
public interface LiveApiServer {
|
||||
|
||||
@Headers({"Content-Type:application/json", "Accept:application/json"})
|
||||
@POST("/alarm/car/report")
|
||||
Observable<StartCarAlarmInfo> reportAlarm(@Body RequestBody body);
|
||||
|
||||
@Headers({"Content-Type:application/json", "Accept:application/json"})
|
||||
@POST("/alarm/car/report/cancel")
|
||||
Observable<BaseData> cancelAlarm(@Body RequestBody body);
|
||||
|
||||
@Headers({"Content-Type:application/json", "Accept:application/json"})
|
||||
@POST("/alarm/car/v2/video/liveStatus")
|
||||
Observable<LiveStatusInfo> requestLiveStatus(@Body RequestBody body);
|
||||
|
||||
@Headers({"Content-Type:application/json", "Accept:application/json"})
|
||||
@POST("/alarm/car/video/reportCameraStatus")
|
||||
Observable<BaseData> uploadCamStatus(@Body RequestBody body);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.mogo.cloud.live.server;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioFormat;
|
||||
import android.os.IBinder;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import com.mogo.cloud.live.listener.IYUVDataListener;
|
||||
import com.mogo.cloud.live.manager.CameraFrameManager;
|
||||
import com.mogo.cloud.live.manager.MGLivePushConfig;
|
||||
import com.mogo.cloud.live.manager.ZeGoLiveManager;
|
||||
import com.mogo.cloud.live.utils.ByteUtils;
|
||||
|
||||
|
||||
public class PushService extends Service implements IYUVDataListener {
|
||||
public static final String ACTION_START_RTMP_PUSH = "action_start_rtmp_push";
|
||||
public static final String ACTION_STOP_RTMP_PUSH = "action_stop_rtmp_push";
|
||||
|
||||
private static final String TAG = "PushService";
|
||||
|
||||
private static final String DEVICES_ID = "devices_id";
|
||||
|
||||
private static final int WIDTH = 1280;
|
||||
private static final int HEIGHT = 720;
|
||||
|
||||
// 自研直播SDK
|
||||
private MGLivePushConfig mLivePushConfig;
|
||||
private ZeGoLiveManager mLivePusher;
|
||||
|
||||
/**
|
||||
* 当前设备ID,作为推流端,userID==roomId==streamID
|
||||
*/
|
||||
private volatile String mDevicesId;
|
||||
|
||||
/**
|
||||
* 启动服务
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param action 动作标志
|
||||
* @param devicesId 设备ID
|
||||
*/
|
||||
public static void startService(Context context, String action,
|
||||
String devicesId) {
|
||||
Intent intent = new Intent(context, PushService.class);
|
||||
intent.setAction(action);
|
||||
intent.putExtra(DEVICES_ID, devicesId);
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.d(TAG, "初始化推流服务……");
|
||||
mLivePushConfig = new MGLivePushConfig();
|
||||
mLivePushConfig.setWidth(WIDTH);
|
||||
mLivePushConfig.setHeight(HEIGHT);
|
||||
mLivePushConfig.setVideoBitrate(1500);
|
||||
mLivePushConfig.setVideoFPS(15);
|
||||
mLivePushConfig.setAudioChannels(2);
|
||||
mLivePushConfig.setAudioSampleRate(44100);
|
||||
mLivePushConfig.setAudioFormat(AudioFormat.ENCODING_PCM_16BIT);
|
||||
mLivePushConfig.setMute(true);
|
||||
|
||||
mLivePusher = ZeGoLiveManager.getInstance();
|
||||
mLivePusher.init(getApplication(), mLivePushConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent != null) {
|
||||
if (ACTION_START_RTMP_PUSH.equals(intent.getAction())) {
|
||||
try {
|
||||
String devicesId = intent.getStringExtra(DEVICES_ID);
|
||||
startPush(devicesId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else if (ACTION_STOP_RTMP_PUSH.equals(intent.getAction())) {
|
||||
stopPush();
|
||||
}
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动发布直播视频流
|
||||
*
|
||||
* @param devicesId 设备ID
|
||||
*/
|
||||
private void startPush(String devicesId) {
|
||||
if (mDevicesId != null && mDevicesId.equals(devicesId)
|
||||
&& mLivePusher != null && mLivePusher.isPushing()) {
|
||||
return;
|
||||
}
|
||||
mDevicesId = devicesId;
|
||||
// 注册视频YUV回调监听
|
||||
CameraFrameManager.getInstance().addYuvDataCallback(this);
|
||||
// 登录房间
|
||||
mLivePusher.loginRoom(mDevicesId, mDevicesId);
|
||||
// 开始发布
|
||||
mLivePusher.startPush(mDevicesId);
|
||||
Log.d(TAG, "startPush :mRoomId=" + mDevicesId + " mDevicesId=" + mDevicesId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束发布直播视频流
|
||||
*/
|
||||
private void stopPush() {
|
||||
try {
|
||||
mDevicesId = "";
|
||||
// 是否处于发布状态
|
||||
if (mLivePusher.isPushing()) {
|
||||
// 停止发布
|
||||
mLivePusher.stopPublish();
|
||||
}
|
||||
// 移除视频回碉监听
|
||||
CameraFrameManager.getInstance().rmYuvDataCallback(this);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrame(byte[] data) {
|
||||
if (mLivePusher != null && mLivePusher.isPushing() && data != null) {
|
||||
// 将YUV数据发布到即构
|
||||
ZeGoLiveManager.getInstance().startPublishingStream(
|
||||
ByteUtils.getBuffer(data, data.length), data.length,
|
||||
SystemClock.elapsedRealtime());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.mogo.cloud.live.utils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ByteUtils {
|
||||
|
||||
private static ByteBuffer byteBuffer;
|
||||
|
||||
public static ByteBuffer getBuffer(byte[] bytes, int length) {
|
||||
if (byteBuffer == null) {
|
||||
byteBuffer = ByteBuffer.allocateDirect(length);
|
||||
}
|
||||
byteBuffer.put(bytes);
|
||||
byteBuffer.flip();
|
||||
return byteBuffer;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,8 @@
|
||||
<<<<<<< HEAD
|
||||
include ':modules:mogo-trafficlive'
|
||||
=======
|
||||
include ':foudations:mogo-common'
|
||||
>>>>>>> f325f43171aed36ba51557ef5340f1758bdb1543
|
||||
include ':foudations:mogo-live'
|
||||
include ':foudations:mogo-socket'
|
||||
include ':modules:mogo-realtime'
|
||||
|
||||
Reference in New Issue
Block a user