路侧、车侧视频流播放优化

Signed-off-by: chenfufeng <chenfufeng@zhidaoauto.com>
This commit is contained in:
chenfufeng
2021-11-12 15:38:53 +08:00
parent a77056f537
commit 01505b4266
15 changed files with 205 additions and 43 deletions

View File

@@ -465,6 +465,10 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
}
override fun startRoadCameraLive(flvUrl: String) {
cameraListView?.startRoadCameraLive(flvUrl)
}
private fun showCameraList(cameraList: List<CameraEntity>?) {
context?.let {
if (cameraViewFloat == null) {
@@ -474,7 +478,11 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
cameraListView!!.setClickListener(object : CameraListView.ClickListener {
override fun onClose(v: View) {
// Builder和cameraListView都置空
dismissFloatView()
// 丢弃开启摄像头推流请求同时monitor回调hmi的startRoadCameraLive()
// 也做了cameraListView的非空判断
CallerMonitorManager.disposeCameraStream()
}
override fun onShowLive(isShow: Boolean) {

View File

@@ -36,7 +36,7 @@ class CameraListAdapter : Adapter<CameraListAdapter.CameraListHolder> {
data?.let {
val cameraEntity = it[position]
holder.cameraInfo.text = with(cameraEntity) {
if (!isCarLive()) "${roadName}${crossingName}${getHeadingStr()}" else sn
if (!isCarLive()) "${roadName}${crossingName}${getHeadingStr()}" else "${street}${township}"
}
holder.bottomLine.visibility =
if (it.size - 1 == position) View.INVISIBLE else View.VISIBLE

View File

@@ -17,8 +17,10 @@ import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.cloud.trafficlive.api.ITrafficCarLiveCallBack
import com.mogo.cloud.trafficlive.api.MoGoAiCloudTrafficLive
import com.mogo.eagle.core.data.camera.CameraEntity
import com.mogo.eagle.core.function.call.monitor.CallerMonitorManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.widget.media.video.SimpleVideoPlayer
import com.mogo.utils.logger.Logger
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.utils.GSYVideoType
@@ -60,6 +62,7 @@ class CameraListView : FrameLayout {
}
private var liveSn: String? = ""
private var isCarLive = false
private var isFirstPage = true
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(
@@ -79,6 +82,7 @@ class CameraListView : FrameLayout {
rvCameraList.adapter = CameraListAdapter(context, cameraInfoList) {
it.tag?.let { cameraEntity ->
showLiveView()
isFirstPage = false
isCloseStatus = false
isPlaySuccess = false
isCarLive = false
@@ -86,7 +90,12 @@ class CameraListView : FrameLayout {
when {
!cameraEntity.isCarLive() -> {
isCarLive = false
if (!cameraEntity.flvUrl.isNullOrEmpty()) gsyVideoPlay(cameraEntity.flvUrl!!)
// 开启摄像头推流
cameraEntity.ip?.let { ip ->
CallerMonitorManager.openCameraStream(ip)
// 测试一直打开的顺义摄像头,不需要调接口让摄像头开启推流
// if (!cameraEntity.flvUrl.isNullOrEmpty()) gsyVideoPlay(cameraEntity.flvUrl!!)
}
}
else -> {
isCarLive = true
@@ -104,6 +113,7 @@ class CameraListView : FrameLayout {
ivCloseIcon.setOnClickListener {
isPlaySuccess = false
isCarLive = false
isFirstPage = true
when {
isCloseStatus -> clickListener?.onClose(it)// 直接退出
else -> {// 返回摄像头列表
@@ -124,6 +134,7 @@ class CameraListView : FrameLayout {
GSYVideoType.setShowType(GSYVideoType.SCREEN_MATCH_FULL)
svpPlayer.setPlayListener(object : SimpleVideoPlayer.PlayListener {
override fun onPlayEvent(event: Int) {
Logger.d(TAG, "onPlayEvent: event is:$event")
when (event) {
SimpleVideoPlayer.PLAY_EVT_PLAY_LOADING -> {
@@ -135,7 +146,9 @@ class CameraListView : FrameLayout {
}
else -> {
showNoSignalView()
valueAnimator.start()
if (!valueAnimator.isStarted && !valueAnimator.isRunning) {
valueAnimator.start()
}
}
}
}
@@ -184,6 +197,10 @@ class CameraListView : FrameLayout {
}
}
fun startRoadCameraLive(flvUrl: String) {
gsyVideoPlay(flvUrl)
}
private fun handleSnLiveStatus(status: Int) {
when (status) {
0 -> {// 正在直播
@@ -197,22 +214,38 @@ class CameraListView : FrameLayout {
private val trafficCarLiveCallBack = object : ITrafficCarLiveCallBack {
override fun onLive(liveSn: String?) {
// 开始直播
progressBar.visibility = View.GONE
surfaceView.visibility = View.VISIBLE
isPlaySuccess = true
Logger.d(TAG, "onLive")
}
override fun onFirstFrame() {
Logger.d(TAG, "onFirstFrame:isFirstPage is:$isFirstPage,isCarLive is:$isCarLive")
if (!isFirstPage && isCarLive) {
// 开始直播
progressBar.visibility = View.GONE
surfaceView.visibility = View.VISIBLE
isPlaySuccess = true
}
}
override fun onDisConnect() {
// 断开连接
showNoSignalView()
valueAnimator.start()
Logger.e(TAG, "onDisConnect")
if (!isFirstPage && isCarLive) {
showNoSignalView()
if (!valueAnimator.isStarted && !valueAnimator.isRunning) {
valueAnimator.start()
}
}
}
override fun onError(errorMsg: String?) {
// 发生错误
showNoSignalView()
valueAnimator.start()
Logger.e(TAG, "onError msg is:${errorMsg}")
if (!isFirstPage && isCarLive) {
// 发生错误
showNoSignalView()
if (!valueAnimator.isStarted && !valueAnimator.isRunning) {
valueAnimator.start()
}
}
}
}
@@ -246,7 +279,6 @@ class CameraListView : FrameLayout {
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
stopLive()
svpPlayer.removeAllViews()
valueAnimator.cancel()
}

View File

@@ -7,9 +7,12 @@ import com.mogo.eagle.core.data.camera.Camera
import com.mogo.eagle.core.data.camera.CameraEntity
import com.mogo.eagle.core.data.camera.LiveCameraInfo
import com.mogo.eagle.core.data.camera.ReqLiveCarBean
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.function.call.map.CallerMapLocationListenerManager
import com.mogo.eagle.core.function.monitoring.net.CameraListServices
import com.mogo.module.common.MogoApisHandler
import com.mogo.module.common.constants.HostConst
import com.mogo.utils.logger.Logger
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
@@ -21,8 +24,12 @@ class CronTaskManager {
private val netWork by lazy {
MogoApisHandler.getInstance().apis.networkApi
}
// 请求路侧摄像头
private var disposable: Disposable? = null
// 请求车侧摄像头
private var carDisposable: Disposable? = null
// 开启路侧摄像头推流
private var streamDisposable: Disposable? = null
private var cameraList: List<CameraEntity>? = null
private var carCameraList: List<CameraEntity>? = null
@@ -51,33 +58,64 @@ class CronTaskManager {
crossing.cameras.filter { camera ->
!camera.flvUrl.isNullOrEmpty()
}.map {
CameraEntity(it.flvUrl, "", it.roadName, it.crossingName, it.getHeadingStr())
CameraEntity(it.flvUrl, "", it.roadName,
it.crossingName, it.getHeadingStr(), it.ip)
}
} ?: ArrayList()
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
cameraList = it
Logger.d(TAG, "requestCameraList返回结果为$it")
}, {
it.printStackTrace()
Logger.e(TAG, "message is:${it.message}, cause is:${it.cause}")
})
}
private fun requestCarCameraList() {
carDisposable = netWork.create(CameraListServices::class.java, HostConst.LAUNCHER_SNAPSHOT_HOST)
.getCarCameraList(ReqLiveCarBean(116.41710185, 39.96944794))
CallerMapLocationListenerManager.getCurrentLocation()?.let { location ->
carDisposable = netWork.create(CameraListServices::class.java, HostConst.LAUNCHER_SNAPSHOT_HOST)
.getCarCameraList(ReqLiveCarBean(location.longitude, location.latitude))
.subscribeOn(Schedulers.io())
.map { liveCarCameraInfo ->
liveCarCameraInfo.result?.liveCamera?.filter { liveCarCamera ->
!liveCarCamera.videoSn.isNullOrEmpty()
}?.map { cameraInfo ->
CameraEntity(sn = cameraInfo.videoSn, street = cameraInfo.street, township = cameraInfo.township)
} ?: ArrayList()
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
carCameraList = it
Logger.d(TAG, "requestCarCameraList返回结果为$it")
}, {
Logger.e(TAG, "message is:${it.message}, cause is:${it.cause}")
it.printStackTrace()
})
} ?: run {
Logger.e(TAG, "CurrentLocation is null!")
}
}
/**
* 开启从摄像头拉流
*/
fun requestOpenCamera(cameraIp: String) {
streamDisposable?.let {
if (!it.isDisposed) it.dispose()
}
streamDisposable = netWork.create(CameraListServices::class.java, HostConst.OPEN_CAMERA_STREAM_HOST)
.openCameraStream(cameraIp)
.subscribeOn(Schedulers.io())
.map { liveCarCameraInfo ->
liveCarCameraInfo.result?.liveCamera?.filter { liveCarCamera ->
!liveCarCamera.videoSn.isNullOrEmpty()
}?.map { cameraInfo ->
CameraEntity(sn = cameraInfo.videoSn)
} ?: ArrayList()
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
carCameraList = it
Logger.d(TAG, "openCameraStream返回结果为$it")
it.result?.let { streamResult ->
if (!streamResult.flvUrl.isNullOrEmpty()) CallerHmiManager.startRoadCameraLive(streamResult.flvUrl!!)
}
}, {
Logger.e(TAG, "openCameraStream&message is:${it.message}, cause is:${it.cause}")
it.printStackTrace()
})
}
@@ -89,11 +127,41 @@ class CronTaskManager {
fun getCameraList() = ArrayList<CameraEntity>().apply {
cameraList?.let { addAll(it) }
carCameraList?.let { addAll(it) }
// // 不需要走华哥打开推流的接口,顺义直接就可以播放的摄像头直播地址
// add(
// CameraEntity("https://video.zhidaohulian.com/live/origin_13_44.flv?txSecret=dc913522389abfc2ab1f2b72f9f4ef41&txTime=6AABBEB2",
// "", "顺义测试道路",
// "路口1", "朝向1", "183.242.46.150")
// )
// add(
// CameraEntity("https://video.zhidaohulian.com/live/origin_13_45.flv?txSecret=7fee9c4ae986169d0e9bd0e1c1b7fab8&txTime=6AABBEB2",
// "", "顺义测试道路",
// "路口2", "朝向2", "183.242.46.150")
// )
// add(
// CameraEntity("https://video.zhidaohulian.com/live/origin_13_46.flv?txSecret=d5ddbd2236743bcf177563bb6680a462&txTime=6AABBEB2",
// "", "顺义测试道路",
// "路口3", "朝向3", "183.242.46.150")
// )
// add(
// CameraEntity("https://video.zhidaohulian.com/live/origin_13_48.flv?txSecret=397b1296eb548c737871fca242ff7ec5&txTime=6AABBEB2",
// "", "顺义测试道路",
// "路口4", "朝向4", "183.242.46.150")
// )
}
fun clear() {
streamDisposable?.let {
if (!it.isDisposed) it.dispose()
}
}
fun onDestroy() {
disposable?.dispose()
carDisposable?.dispose()
streamDisposable?.let {
if (!it.isDisposed) it.dispose()
}
cronHandler.removeMessages(CRON_TASK_TYPE)
}
}

View File

@@ -41,8 +41,18 @@ public class MoGoMonitoringProvider implements IMoGoMonitoringProvider {
}
@Override
public void onDestroy() {
public void openCameraStream(String cameraIp) {
mCronTaskManager.requestOpenCamera(cameraIp);
}
@Override
public void disposeCameraStream() {
mCronTaskManager.clear();
}
@Override
public void onDestroy() {
mCronTaskManager.onDestroy();
}
}

View File

@@ -1,18 +1,19 @@
package com.mogo.eagle.core.function.monitoring.net
import com.mogo.eagle.core.data.camera.CameraListInfo
import com.mogo.eagle.core.data.camera.CameraStreamEntity
import com.mogo.eagle.core.data.camera.LiveCarCameraInfo
import com.mogo.eagle.core.data.camera.ReqLiveCarBean
import io.reactivex.Single
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Query
import retrofit2.http.*
interface CameraListServices {
@GET("/yycp-smartTransportationAiCloud-service/eagle/device/list")
fun getCameraList(@Query("roadId") roadId: String?): Single<CameraListInfo?>
@POST("/yycp-launcherSnapshot/launcherSnapshot/queryLiveCarByLocal")
@POST("/yycp-launcherSnapshot/car/queryLiveCarByLocal")
fun getCarCameraList(@Body reqBody: ReqLiveCarBean): Single<LiveCarCameraInfo?>
@GET("/openStream/{cameraIp}")
fun openCameraStream(@Path("cameraIp") cameraIp: String): Single<CameraStreamEntity>
}

View File

@@ -5,7 +5,10 @@ data class CameraEntity(
var sn: String?="",
var roadName: String?="",
var crossingName: String?="",
var headingDesc: String?=""
var headingDesc: String?="",
var ip: String? = "",
var street: String? = "",
var township: String? = ""
) {
fun isCarLive() = !sn.isNullOrEmpty()
fun getHeadingStr() = if (headingDesc != null) headingDesc else ""

View File

@@ -0,0 +1,16 @@
package com.mogo.eagle.core.data.camera
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import com.mogo.eagle.core.data.BaseData
@Keep
data class CameraStreamEntity (
@SerializedName("result")
var result: StreamResult?
): BaseData()
data class StreamResult(
@SerializedName("flvUrl")
var flvUrl: String?
)

View File

@@ -18,7 +18,10 @@ data class LiveResult(
@Keep
data class LiveCameraInfo(
var videoUrl: String,
var videoChannel: String,
var videoSn: String
var videoUrl: String?,
var videoChannel: String?,
var videoSn: String?,
var street: String?,
var township: String?,
var address: String?
)

View File

@@ -136,4 +136,6 @@ interface IMoGoWaringProvider : IMoGoFunctionProvider {
* 展示VIP标识
*/
fun vipIdentification(visible: Boolean)
fun startRoadCameraLive(flvUrl: String)
}

View File

@@ -12,4 +12,11 @@ import java.util.List;
*/
public interface IMoGoMonitoringProvider extends IMoGoFunctionServerProvider {
List<CameraEntity> getCameraList();
void openCameraStream(String cameraIp);
/**
* 防止回调摄像头列表View的相关调用
*/
void disposeCameraStream();
}

View File

@@ -182,4 +182,7 @@ object CallerHmiManager : CallerBase() {
waringProviderApi.vipIdentification(visible)
}
fun startRoadCameraLive(flvUrl: String) {
waringProviderApi.startRoadCameraLive(flvUrl)
}
}

View File

@@ -14,4 +14,12 @@ object CallerMonitorManager {
fun getCameraList(): List<CameraEntity>? {
return providerApi.cameraList
}
fun openCameraStream(cameraIp: String) {
providerApi.openCameraStream(cameraIp)
}
fun disposeCameraStream() {
providerApi.disposeCameraStream()
}
}

View File

@@ -62,21 +62,21 @@ SERVICE_CHAIN_VERSION=1.0.22
LOGLIB_VERSION=1.0.4
######## MogoAiCloudSDK Version ########
# 网络请求
MOGO_NETWORK_VERSION=1.1.54-live
MOGO_NETWORK_VERSION=1.1.56-live
# 鉴权
MOGO_PASSPORT_VERSION=1.1.54-live
MOGO_PASSPORT_VERSION=1.1.56-live
# 常链接
MOGO_SOCKET_VERSION=1.1.54-live
MOGO_SOCKET_VERSION=1.1.56-live
# 数据采集
MOGO_REALTIME_VERSION=1.1.54-live
MOGO_REALTIME_VERSION=1.1.56-live
# 探路,道路事件发布,获取
MOGO_TANLU_VERSION=1.1.54-live
MOGO_TANLU_VERSION=1.1.56-live
# 直播推流
MOGO_LIVE_VERSION=1.1.54-live
MOGO_LIVE_VERSION=1.1.56-live
# 直播拉流
MOGO_TRAFFICLIVE_VERSION=1.1.54-live
MOGO_TRAFFICLIVE_VERSION=1.1.56-live
# 定位服务
MOGO_LOCATION_VERSION=1.1.54-live
MOGO_LOCATION_VERSION=1.1.56-live
######## MogoAiCloudSDK Version ########
# 自研地图
MAP_SDK_VERSION=V2.0.0.6-test

View File

@@ -21,6 +21,7 @@ public class HostConst {
public static final String IM_SOCKET_DOMAIN = "dzt-im.zhidaozhixing.com";
public static final String WEBSOCKET_DOMAIN = "dzt-Instant.zhidaozhixing.com";
public static final String CAMERA_STREAM_HOST = "http://dzt-smartTransportationAiCloud.zhidaozhixing.com";
public static final String OPEN_CAMERA_STREAM_HOST = "http://10.0.16.6:18080";
public static final String SOCKET_CENTER_DOMAIN = "socketRegion";