add new module trafficLive and rename package of class

This commit is contained in:
zhongchao
2021-02-03 17:49:16 +08:00
40 changed files with 4878 additions and 28 deletions

View 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);
}

View 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();
}
}

View 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);
}
}

View File

@@ -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) {

View 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;
}
}

View 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;
}
}