开始编写直播模块

This commit is contained in:
董宏宇
2021-02-03 14:08:12 +08:00
parent ca397a4400
commit 36a6785ec2
26 changed files with 4183 additions and 10 deletions

1
.idea/gradle.xml generated
View File

@@ -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" />

View File

@@ -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
View File

@@ -0,0 +1 @@
/build

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

View File

@@ -0,0 +1,4 @@
GROUP=com.mogo.cloud
POM_ARTIFACT_ID=common
VERSION_CODE=1
VERSION_NAME=1.0.2-SNAPSHOT

View 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

View File

@@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.cloud.common">
/
</manifest>

View File

@@ -0,0 +1,7 @@
package com.mogo.cloud.common.utils;
/**
* 设备信息
*/
public class DevicesUtils {
}

View File

@@ -20,11 +20,31 @@ 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
implementation 'com.zhidao.ptech:connsvr-protoco:0.1.37'
implementation 'com.google.protobuf:protobuf-java:3.10.0'
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")
}
}

View File

@@ -0,0 +1,8 @@
package com.mogo.cloud.live;
/**
* 直播状态回调用
*/
public interface ILiveStatusCallback {
void onChange(String camId, int status);
}

View File

@@ -0,0 +1,8 @@
package com.mogo.cloud.live;
/**
* YUV数据回调
*/
public interface IYUVDataCallback {
void onFrame(byte[] data);
}

View File

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

View File

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

View File

@@ -0,0 +1,441 @@
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.IZegoDestroyCompletionCallback;
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编号
*/
private static final String ROOM_ID_PREFIX = "ROOM_ID_";
/**
* 直播用户名称的
*/
private static final String NAME_PREFIX = "MoGoCar_";
/**
* 直播流ID发起推送的直播流ID
*/
private 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;
// 开始或停止自定义视频采集,支持设置其他通道的推流
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
*/
private 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);
}
/**
* 开始发布视频流
*
* @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() {
stopPreview();
stopPublishingStream();
}
/**
* 停止推送数据
*/
private void stopPublishingStream() {
// 停止推送
ZegoExpressEngine.getEngine().stopPublishingStream();
}
/**
* 停止观看直播
*/
public void onDestroyLive() {
logOutRoom();
destroyEngine();
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
package com.mogo.cloud.live.model;
public class StartCarAlarmInfo extends BaseData{
public Result result;
public class Result {
public String eventId;
}
}

View File

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

View File

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

View File

@@ -0,0 +1,142 @@
package com.mogo.cloud.live.push;
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.util.Log;
import com.mogo.cloud.live.IYUVDataCallback;
import com.mogo.cloud.live.manager.MGLivePushConfig;
import com.mogo.cloud.live.manager.ZeGoLiveManager;
public class PushService extends Service implements IYUVDataCallback {
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 CAM_ID = "cam_id";
private static final int WIDTH = 1280;
private static final int HEIGHT = 720;
// 自研直播SDK
private MGLivePushConfig mLivePushConfig;
private ZeGoLiveManager mLivePusher;
private static volatile String mRtmpUrl;
public static void startService(Context context, String action, String rtmpurl, String camId) {
Intent intent = new Intent(context, PushService.class);
intent.setAction(action);
intent.putExtra(RTMP_URL, rtmpurl);
intent.putExtra(CAM_ID, camId);
context.startService(intent);
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "初始化推流服务……");
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);
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 url = intent.getStringExtra(RTMP_URL);
startRtmpPush(url);
} catch (Exception e) {
e.printStackTrace();
}
} else if (ACTION_STOP_RTMP_PUSH.equals(intent.getAction())) {
stopRtmpPush();
}
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void startRtmpPush(String url) {
if (mRtmpUrl != null && mRtmpUrl.equals(url)
&& 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);
}
private void stopRtmpPush() {
try {
mRtmpUrl = "";
if (mLivePusher.isPushing()) {
mLivePusher.stopPusher();
}
FrameManagerUtils.getInstance().rmYuvDataCallback(this);
} catch (Exception e) {
e.printStackTrace();
}
}
@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);
}
}
}

View File

@@ -0,0 +1,115 @@
package com.mogo.cloud.live.utils;
import com.mogo.cloud.live.IYUVDataCallback;
import java.util.HashSet;
import java.util.Set;
public class FrameManagerUtils {
private static final int DST_WIDTH = 1280;
private static final int DST_HEIGHT = 720;
byte[] dst = null;
byte[] tmp = null;
private final Set<IYUVDataCallback> globalDataListener = new HashSet<>();
public static FrameManagerUtils getInstance() {
return SingletonHolder.INSTANCE;
}
private static final class SingletonHolder {
private static final FrameManagerUtils INSTANCE = new FrameManagerUtils();
}
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(IYUVDataCallback callback) {
if (callback != null) {
globalDataListener.add(callback);
}
}
public synchronized void rmYuvDataCallback(IYUVDataCallback 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 (IYUVDataCallback 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);
}
}

View File

@@ -1,3 +1,4 @@
include ':foudations:mogo-common'
include ':foudations:mogo-live'
include ':foudations:mogo-socket'
include ':modules:mogo-realtime'