+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+ android:name=".PushActivity"
+ android:label="推送直播" />
-
-
-
+ android:label="网络测试" />
-
-
-
+ android:label="实时数据测试" />
-
+ android:label="路况服务" />
diff --git a/app/src/main/java/com/mogo/cloud/BaseLiveActivity.java b/app/src/main/java/com/mogo/cloud/BaseLiveActivity.java
new file mode 100644
index 0000000..de3620e
--- /dev/null
+++ b/app/src/main/java/com/mogo/cloud/BaseLiveActivity.java
@@ -0,0 +1,148 @@
+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_push_video);
+
+ 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 车机从相机获取的视频数据回调
+ *
+ * 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);
+
+ /**
+ * 是否是静音模式
+ *
+ * @param isMute true-静音,false-非静音
+ */
+ public abstract void toggleMute(boolean isMute);
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/mogo/cloud/MainActivity.java b/app/src/main/java/com/mogo/cloud/MainActivity.java
index 29a3de1..53dbb26 100644
--- a/app/src/main/java/com/mogo/cloud/MainActivity.java
+++ b/app/src/main/java/com/mogo/cloud/MainActivity.java
@@ -18,6 +18,7 @@ public class MainActivity extends AppCompatActivity {
private Button btnJumpNetWorkPort;
private Button btnJumpRealTime;
private Button btnJumpRoadCondition;
+ private Button btnJumpPushLive;
private TextView tvSn;
private TextView tvToken;
@@ -57,6 +58,12 @@ public class MainActivity extends AppCompatActivity {
startActivity(intent);
});
+ btnJumpPushLive = findViewById(R.id.btnJumpPushLive);
+ btnJumpPushLive.setOnClickListener(v -> {
+ Intent intent = new Intent(MainActivity.this, PushActivity.class);
+ startActivity(intent);
+ });
+
MoGoAiCloudClient.getInstance().addTokenCallbacks(new IMoGoTokenCallback() {
@Override
public void onTokenGot(String token, String sn) {
diff --git a/app/src/main/java/com/mogo/cloud/PushActivity.java b/app/src/main/java/com/mogo/cloud/PushActivity.java
new file mode 100644
index 0000000..00f2946
--- /dev/null
+++ b/app/src/main/java/com/mogo/cloud/PushActivity.java
@@ -0,0 +1,120 @@
+package com.mogo.cloud;
+
+import android.media.AudioFormat;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.mogo.cloud.live.listener.ILiveProgressListener;
+import com.mogo.cloud.live.manager.MGLivePushConfig;
+import com.mogo.cloud.live.manager.ZeGoLiveManager;
+import com.mogo.cloud.live.utils.ByteUtils;
+import com.mogo.cloud.util.Devices;
+
+
+/**
+ * 推流页面
+ */
+public class PushActivity extends BaseLiveActivity {
+ public static final String TAG = "PushActivity";
+
+ private boolean isLoginSuccess = false;
+ private boolean isLive = false;
+ private boolean isOnStart = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ MGLivePushConfig mLivePushConfig = new MGLivePushConfig();
+ mLivePushConfig.setWidth(1280);
+ mLivePushConfig.setHeight(720);
+ mLivePushConfig.setVideoBitrate(1500);
+ mLivePushConfig.setVideoFPS(15);
+ mLivePushConfig.setAudioChannels(2);
+ mLivePushConfig.setAudioSampleRate(44100);
+ mLivePushConfig.setAudioFormat(AudioFormat.ENCODING_PCM_16BIT);
+ mLivePushConfig.setMute(true);
+
+ ZeGoLiveManager.getInstance().init(this.getApplication(), mLivePushConfig);
+ ZeGoLiveManager.getInstance().setLiveProgressListener(new ILiveProgressListener() {
+ @Override
+ public void onStart() {
+ Log.i(TAG, "onStart");
+ isOnStart = true;
+ }
+
+ @Override
+ public void onStop() {
+ Log.i(TAG, "onStop");
+ isOnStart = false;
+ }
+
+ @Override
+ public void onConnecting() {
+ Log.i(TAG, "onConnecting");
+ }
+
+ @Override
+ public void onConnected(String roomId) {
+ Log.i(TAG, "onConnected");
+ isLoginSuccess = true;
+ }
+
+ @Override
+ public void onDisConnect() {
+ Log.i(TAG, "onDisConnect");
+ isLoginSuccess = false;
+ }
+
+ @Override
+ public void onDebugError(int errorCode, String funcName, String errorInfo) {
+ Log.i(TAG, "errorCode : " + errorCode + " , funcName : " + funcName + " , errorInfo : " + errorInfo);
+ }
+ });
+ }
+
+ @Override
+ public void onVideoFrame(byte[] bytes, int bytesLength) {
+ if (!isLoginSuccess) {
+// Log.i(TAG, "还未进房成功");
+ return;
+ }
+ if (!isOnStart) {
+// Log.i(TAG, "还未执行onStart回调");
+ return;
+ }
+ if (!isLive) {
+ return;
+ }
+ Log.i(TAG, "onVideoFrame byte length: " + bytesLength);
+ ZeGoLiveManager.getInstance().startPublishingStream(ByteUtils.getBuffer(bytes, bytesLength), bytesLength, SystemClock.elapsedRealtime());
+ }
+
+ @Override
+ public void toggleLive(boolean isLive) {
+ this.isLive = isLive;
+ if (!isLoginSuccess) {
+ Log.i(TAG, "toggleLive isLive : " + isLive);
+ btnLive.setChecked(!isLive);
+ return;
+ }
+ Log.i(TAG, "toggleLive : " + isLive);
+ if (isLive) {
+ ZeGoLiveManager.getInstance().startPush(Devices.getSn());
+ } else {
+ ZeGoLiveManager.getInstance().stopPublish();
+ }
+ }
+
+ @Override
+ public void toggleMute(boolean isMute) {
+
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ ZeGoLiveManager.getInstance().onDestroyPublish();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/mogo/cloud/util/Devices.java b/app/src/main/java/com/mogo/cloud/util/Devices.java
new file mode 100644
index 0000000..aea09c5
--- /dev/null
+++ b/app/src/main/java/com/mogo/cloud/util/Devices.java
@@ -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;
+ }
+}
diff --git a/app/src/main/java/com/mogo/cloud/util/YuvToolUtils.java b/app/src/main/java/com/mogo/cloud/util/YuvToolUtils.java
new file mode 100644
index 0000000..ee21ada
--- /dev/null
+++ b/app/src/main/java/com/mogo/cloud/util/YuvToolUtils.java
@@ -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 splitYUVI420(byte[] i420bytes, int width, int height) {
+
+ ArrayList 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 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;
+ }
+
+
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index b61d77c..b32e84a 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -65,6 +65,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="路况服务测试" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_push_video.xml b/app/src/main/res/layout/activity_push_video.xml
new file mode 100644
index 0000000..f6a3f9b
--- /dev/null
+++ b/app/src/main/res/layout/activity_push_video.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c1072ae..bb5e69c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,8 @@
MoGoAiCloudSdk
+
+
+ 开始直播
+ 停止直播
+
\ No newline at end of file
diff --git a/foudations/mogo-live/build.gradle b/foudations/mogo-live/build.gradle
index 22869f9..341b8be 100644
--- a/foudations/mogo-live/build.gradle
+++ b/foudations/mogo-live/build.gradle
@@ -35,8 +35,9 @@ dependencies {
implementation rootProject.ext.dependencies.androidxappcompat
implementation rootProject.ext.dependencies.live_sdk_zego
- implementation 'com.zhidao.ptech:connsvr-protoco:0.1.37'
- implementation 'com.google.protobuf:protobuf-java:3.10.0'
+ 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}"
diff --git a/foudations/mogo-live/src/main/AndroidManifest.xml b/foudations/mogo-live/src/main/AndroidManifest.xml
index 7639097..46d64db 100644
--- a/foudations/mogo-live/src/main/AndroidManifest.xml
+++ b/foudations/mogo-live/src/main/AndroidManifest.xml
@@ -1,5 +1,9 @@
- /
+
+
+
+
+
\ No newline at end of file
diff --git a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/IYUVDataCallback.java b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/IYUVDataCallback.java
deleted file mode 100644
index 00f8f48..0000000
--- a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/IYUVDataCallback.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.mogo.cloud.live;
-
-/**
- * YUV数据回调
- */
-public interface IYUVDataCallback {
- void onFrame(byte[] data);
-}
diff --git a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/ILiveStatusCallback.java b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/listener/ILiveStatusListener.java
similarity index 51%
rename from foudations/mogo-live/src/main/java/com/mogo/cloud/live/ILiveStatusCallback.java
rename to foudations/mogo-live/src/main/java/com/mogo/cloud/live/listener/ILiveStatusListener.java
index 45a1d12..4c7a124 100644
--- a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/ILiveStatusCallback.java
+++ b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/listener/ILiveStatusListener.java
@@ -1,8 +1,8 @@
-package com.mogo.cloud.live;
+package com.mogo.cloud.live.listener;
/**
* 直播状态回调用
*/
-public interface ILiveStatusCallback {
+public interface ILiveStatusListener {
void onChange(String camId, int status);
}
diff --git a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/listener/IYUVDataListener.java b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/listener/IYUVDataListener.java
new file mode 100644
index 0000000..41a8272
--- /dev/null
+++ b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/listener/IYUVDataListener.java
@@ -0,0 +1,8 @@
+package com.mogo.cloud.live.listener;
+
+/**
+ * YUV数据回调
+ */
+public interface IYUVDataListener {
+ void onFrame(byte[] data);
+}
diff --git a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/utils/FrameManagerUtils.java b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/manager/CameraFrameManager.java
similarity index 81%
rename from foudations/mogo-live/src/main/java/com/mogo/cloud/live/utils/FrameManagerUtils.java
rename to foudations/mogo-live/src/main/java/com/mogo/cloud/live/manager/CameraFrameManager.java
index 8d4788f..f232803 100644
--- a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/utils/FrameManagerUtils.java
+++ b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/manager/CameraFrameManager.java
@@ -1,37 +1,42 @@
-package com.mogo.cloud.live.utils;
+package com.mogo.cloud.live.manager;
-import com.mogo.cloud.live.IYUVDataCallback;
+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 FrameManagerUtils {
+/**
+ * 视频帧管理
+ */
+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 globalDataListener = new HashSet<>();
+ private final Set globalDataListener = new HashSet<>();
- public static FrameManagerUtils getInstance() {
+ public static CameraFrameManager getInstance() {
return SingletonHolder.INSTANCE;
}
private static final class SingletonHolder {
- private static final FrameManagerUtils INSTANCE = new FrameManagerUtils();
+ 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 data origin yuv data
+ * @param width data width
* @param height data height
- * @param type 1:YV12 2:NV21 3:I420 4 nv12
+ * @param type 1:YV12 2:NV21 3:I420 4 nv12
*/
private byte[] handleData(byte[] data, int width, int height, int type) {
if (dst == null) {
@@ -80,13 +85,13 @@ public class FrameManagerUtils {
return new byte[0];
}
- public synchronized void addYuvDataCallback(IYUVDataCallback callback) {
+ public synchronized void addYuvDataCallback(IYUVDataListener callback) {
if (callback != null) {
globalDataListener.add(callback);
}
}
- public synchronized void rmYuvDataCallback(IYUVDataCallback callback) {
+ public synchronized void rmYuvDataCallback(IYUVDataListener callback) {
if (callback != null) {
globalDataListener.remove(callback);
}
@@ -99,7 +104,7 @@ public class FrameManagerUtils {
private void dispatchData(byte[] data) {
if (globalDataListener != null && globalDataListener.size() > 0) {
- for (IYUVDataCallback callback : globalDataListener) {
+ for (IYUVDataListener callback : globalDataListener) {
if (callback != null) {
callback.onFrame(data);
}
diff --git a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/manager/ZeGoLiveManager.java b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/manager/ZeGoLiveManager.java
index 4041ac5..36609bc 100644
--- a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/manager/ZeGoLiveManager.java
+++ b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/manager/ZeGoLiveManager.java
@@ -14,7 +14,6 @@ import java.util.ArrayList;
import im.zego.zegoexpress.ZegoExpressEngine;
import im.zego.zegoexpress.callback.IZegoCustomVideoCaptureHandler;
-import im.zego.zegoexpress.callback.IZegoDestroyCompletionCallback;
import im.zego.zegoexpress.callback.IZegoEventHandler;
import im.zego.zegoexpress.constants.ZegoEngineState;
import im.zego.zegoexpress.constants.ZegoNetworkMode;
@@ -63,15 +62,15 @@ public class ZeGoLiveManager {
/**
* 直播会议ID,使用推流端的车机信息建立房间ID,这里采用格式为:ROOM_ID_车机SN编号
*/
- private static final String ROOM_ID_PREFIX = "ROOM_ID_";
+ public static final String ROOM_ID_PREFIX = "ROOM_ID_";
/**
* 直播用户名称的
*/
- private static final String NAME_PREFIX = "MoGoCar_";
+ public static final String NAME_PREFIX = "MoGoCar_";
/**
* 直播流ID,发起推送的直播流ID
*/
- private static final String STREAM_ID_PREFIX = "STREAM_ID_"; //+ "_" + System.currentTimeMillis()
+ public static final String STREAM_ID_PREFIX = "STREAM_ID_"; //+ "_" + System.currentTimeMillis()
/**
* 定义 即构SDK 引擎对象
*/
@@ -160,6 +159,8 @@ public class ZeGoLiveManager {
customVideoCaptureConfig = new ZegoCustomVideoCaptureConfig();
// 设置自定义视频采集视频帧数据类型
customVideoCaptureConfig.bufferType = ZegoVideoBufferType.RAW_DATA;
+ // true 表示静音(关闭)
+ mExpressEngine.muteMicrophone(true);
// 开始或停止自定义视频采集,支持设置其他通道的推流
mExpressEngine.enableCustomVideoCapture(true, customVideoCaptureConfig, ZegoPublishChannel.MAIN);
// 设置自定义视频采集回调
@@ -327,7 +328,7 @@ public class ZeGoLiveManager {
* @param userId 当前用户ID
* @param roomId 要进入的房间ID
*/
- private void loginRoom(String userId, String roomId) {
+ public void loginRoom(String userId, String roomId) {
currentRoomId = ROOM_ID_PREFIX + roomId;
ZegoUser zegoUser = new ZegoUser(userId, NAME_PREFIX + userId);
mExpressEngine.loginRoom(currentRoomId, zegoUser);
@@ -368,6 +369,7 @@ public class ZeGoLiveManager {
*/
public void stopLive(String streamId) {
mExpressEngine.stopPlayingStream(streamId);
+ stopPreview();
}
/**
@@ -418,7 +420,6 @@ public class ZeGoLiveManager {
* 停止推送
*/
public void stopPublish() {
- stopPreview();
stopPublishingStream();
}
@@ -434,6 +435,7 @@ public class ZeGoLiveManager {
* 停止观看直播
*/
public void onDestroyLive() {
+ stopPreview();
logOutRoom();
destroyEngine();
}
diff --git a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/push/PushService.java b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/server/PushService.java
similarity index 51%
rename from foudations/mogo-live/src/main/java/com/mogo/cloud/live/push/PushService.java
rename to foudations/mogo-live/src/main/java/com/mogo/cloud/live/server/PushService.java
index 2be9c78..45d7642 100644
--- a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/push/PushService.java
+++ b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/server/PushService.java
@@ -1,25 +1,27 @@
-package com.mogo.cloud.live.push;
+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.text.TextUtils;
+import android.os.SystemClock;
import android.util.Log;
-import com.mogo.cloud.live.IYUVDataCallback;
+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 IYUVDataCallback {
+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 RTMP_URL = "rtmp_url";
+ private static final String DEVICES_ID = "devices_id";
private static final String CAM_ID = "cam_id";
private static final int WIDTH = 1280;
@@ -29,12 +31,24 @@ public class PushService extends Service implements IYUVDataCallback {
private MGLivePushConfig mLivePushConfig;
private ZeGoLiveManager mLivePusher;
- private static volatile String mRtmpUrl;
+ /**
+ * 当前设备ID,作为推流端,userID==roomId==streamID
+ */
+ private volatile String mDevicesId;
- public static void startService(Context context, String action, String rtmpurl, String camId) {
+ /**
+ * 启动服务
+ *
+ * @param context 上下文
+ * @param action 动作标志
+ * @param devicesId 设备ID
+ * @param camId 摄像头
+ */
+ public static void startService(Context context, String action,
+ String devicesId, String camId) {
Intent intent = new Intent(context, PushService.class);
intent.setAction(action);
- intent.putExtra(RTMP_URL, rtmpurl);
+ intent.putExtra(DEVICES_ID, devicesId);
intent.putExtra(CAM_ID, camId);
context.startService(intent);
}
@@ -44,8 +58,8 @@ public class PushService extends Service implements IYUVDataCallback {
super.onCreate();
Log.d(TAG, "初始化推流服务……");
mLivePushConfig = new MGLivePushConfig();
- mLivePushConfig.setWidth(1280);
- mLivePushConfig.setHeight(720);
+ mLivePushConfig.setWidth(WIDTH);
+ mLivePushConfig.setHeight(HEIGHT);
mLivePushConfig.setVideoBitrate(1500);
mLivePushConfig.setVideoFPS(15);
mLivePushConfig.setAudioChannels(2);
@@ -54,7 +68,7 @@ public class PushService extends Service implements IYUVDataCallback {
mLivePushConfig.setMute(true);
mLivePusher = ZeGoLiveManager.getInstance();
- mLivePusher.init(getApplication(),mLivePushConfig);
+ mLivePusher.init(getApplication(), mLivePushConfig);
}
@Override
@@ -62,13 +76,13 @@ public class PushService extends Service implements IYUVDataCallback {
if (intent != null) {
if (ACTION_START_RTMP_PUSH.equals(intent.getAction())) {
try {
- String url = intent.getStringExtra(RTMP_URL);
- startRtmpPush(url);
+ String devicesId = intent.getStringExtra(DEVICES_ID);
+ startPush(devicesId);
} catch (Exception e) {
e.printStackTrace();
}
} else if (ACTION_STOP_RTMP_PUSH.equals(intent.getAction())) {
- stopRtmpPush();
+ stopPush();
}
}
return super.onStartCommand(intent, flags, startId);
@@ -79,54 +93,39 @@ public class PushService extends Service implements IYUVDataCallback {
return null;
}
- private void startRtmpPush(String url) {
- if (mRtmpUrl != null && mRtmpUrl.equals(url)
+ /**
+ * 启动发布直播视频流
+ *
+ * @param devicesId 设备ID
+ */
+ private void startPush(String devicesId) {
+ if (mDevicesId != null && mDevicesId.equals(devicesId)
&& mLivePusher != null && mLivePusher.isPushing()) {
return;
}
-
- mRtmpUrl = url;
-
- String rtmpUrl = null;
- if (!TextUtils.isEmpty(mRtmpUrl)) {
- String url1[] = mRtmpUrl.split("###");
- if (url1.length > 0) {
- rtmpUrl = url1[0];
- }
- }
-
- if (TextUtils.isEmpty(rtmpUrl) || (!rtmpUrl.trim().toLowerCase().startsWith("rtmp://"))) {
- //TODO rtmp url 地址有误
- Log.e(TAG, "rtmp url 地址有误");
- return;
- }
-
- //int customModeType = 0;
- //customModeType |= TXLiveConstants.CUSTOM_MODE_VIDEO_CAPTURE;
- //mLivePusher.setVideoQuality(TXLiveConstants.VIDEO_QUALITY_HIGH_DEFINITION, true, true);
- //mLivePushConfig.setVideoResolution(TXLiveConstants.VIDEO_RESOLUTION_TYPE_1280_720);
- //mLivePushConfig.setAutoAdjustBitrate(false);
- //mLivePushConfig.setHomeOrientation(TXLiveConstants.VIDEO_ANGLE_HOME_LEFT);
- //mLivePushConfig.setVideoBitrate(900);
- //mLivePushConfig.setVideoEncodeGop(1);
- //mLivePushConfig.setVideoFPS(15);
- //mLivePushConfig.setCustomModeType(customModeType);
-
-
- FrameManagerUtils.getInstance().addYuvDataCallback(this);
-
- mLivePusher.startPusher(rtmpUrl.trim());
-
- Log.d(TAG, "startRtmpPush :" + rtmpUrl);
+ mDevicesId = devicesId;
+ // 注册视频YUV回调监听
+ CameraFrameManager.getInstance().addYuvDataCallback(this);
+ // 登录房间
+ mLivePusher.loginRoom(mDevicesId, mDevicesId);
+ // 开始发布
+ mLivePusher.startPush(mDevicesId);
+ Log.d(TAG, "startPush :mRoomId=" + mDevicesId + " mDevicesId=" + mDevicesId);
}
- private void stopRtmpPush() {
+ /**
+ * 结束发布直播视频流
+ */
+ private void stopPush() {
try {
- mRtmpUrl = "";
+ mDevicesId = "";
+ // 是否处于发布状态
if (mLivePusher.isPushing()) {
- mLivePusher.stopPusher();
+ // 停止发布
+ mLivePusher.stopPublish();
}
- FrameManagerUtils.getInstance().rmYuvDataCallback(this);
+ // 移除视频回碉监听
+ CameraFrameManager.getInstance().rmYuvDataCallback(this);
} catch (Exception e) {
e.printStackTrace();
}
@@ -135,8 +134,10 @@ public class PushService extends Service implements IYUVDataCallback {
@Override
public void onFrame(byte[] data) {
if (mLivePusher != null && mLivePusher.isPushing() && data != null) {
- int ret = mLivePusher.sendCustomVideoData(data, 3, WIDTH, HEIGHT);
- if (BuildConfig.DEBUG) Log.d(TAG, "onFrame push ret is:" + ret);
+ // 将YUV数据发布到即构
+ ZeGoLiveManager.getInstance().startPublishingStream(
+ ByteUtils.getBuffer(data, data.length), data.length,
+ SystemClock.elapsedRealtime());
}
}
}
diff --git a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/utils/ByteUtils.java b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/utils/ByteUtils.java
new file mode 100644
index 0000000..5df727e
--- /dev/null
+++ b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/utils/ByteUtils.java
@@ -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;
+ }
+}