[3.4.0]
[后视镜优化]
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
package com.mogo.och.taxi.passenger.ui.arrived
|
||||
|
||||
import android.content.*
|
||||
import android.graphics.BitmapFactory
|
||||
import android.util.*
|
||||
import androidx.core.util.Pools
|
||||
import com.mogo.eagle.core.data.config.*
|
||||
import com.mogo.eagle.core.function.api.autopilot.*
|
||||
import com.mogo.eagle.core.function.api.lookaround.*
|
||||
import com.mogo.eagle.core.function.api.lookaround.data.*
|
||||
import com.mogo.eagle.core.function.call.autopilot.*
|
||||
import com.mogo.eagle.core.utilcode.mogo.*
|
||||
import com.zhjt.mogo.adas.data.*
|
||||
import com.zhjt.mogo.adas.data.bean.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.*
|
||||
import kotlinx.coroutines.channels.Channel.Factory.CONFLATED
|
||||
import kotlinx.coroutines.flow.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.concurrent.*
|
||||
|
||||
internal class AroundProviderImpl: IMoGoBackCameraVideoListener, IMoGoRoboBusJinlvM1StitchedVideoListener {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "AroundProviderImpl"
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private var channel: Channel<LookAroundData> = Channel(CONFLATED)
|
||||
get() {
|
||||
return if (field.isClosedForReceive || field.isClosedForSend) {
|
||||
Channel(CONFLATED)
|
||||
} else {
|
||||
field
|
||||
}
|
||||
}
|
||||
|
||||
private val pool by lazy { Pools.SynchronizedPool<LookAroundData>(10) }
|
||||
|
||||
private val bitmapWidth by lazy { AtomicInteger(0) }
|
||||
|
||||
private val bitmapHeight by lazy { AtomicInteger(0) }
|
||||
|
||||
//276,319,147,366
|
||||
private val targetX by lazy { AtomicInteger(0) }
|
||||
|
||||
private val targetY by lazy { AtomicInteger(0) }
|
||||
|
||||
private val targetWidth by lazy { AtomicInteger(0) }
|
||||
|
||||
private val targetHeight by lazy { AtomicInteger(0) }
|
||||
|
||||
private val scope by lazy { CoroutineScope(Dispatchers.IO + SupervisorJob()) }
|
||||
|
||||
@Volatile
|
||||
private var job: Job? = null
|
||||
|
||||
fun init() {
|
||||
CallerBackCameraVideoListenerManager.addListener(TAG, this)
|
||||
CallerRoboBusJinlvM1StitchedVideoListenerManager.addListener(TAG, this)
|
||||
}
|
||||
fun removeListener() {
|
||||
CallerBackCameraVideoListenerManager.removeListener(TAG)
|
||||
CallerRoboBusJinlvM1StitchedVideoListenerManager.removeListener(TAG)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun flow(): Flow<LookAroundData> = channelFlow {
|
||||
val iterator = this@AroundProviderImpl.channel.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
send(iterator.next())
|
||||
}
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
override fun onRoboBusJinlvM1StitchedVideo(data: ByteArray) {
|
||||
if (bitmapWidth.get() == 0 || bitmapHeight.get() == 0) {
|
||||
val options = BitmapFactory.Options()
|
||||
options.inJustDecodeBounds = true
|
||||
BitmapFactory.decodeByteArray(data,0 , data.size, options)
|
||||
bitmapWidth.set(options.outWidth)
|
||||
bitmapHeight.set(options.outHeight)
|
||||
}
|
||||
Log.d(TAG, "-- onRoboBusJinlvM1StitchedVideo -- bitmap:[${bitmapWidth.get()}, ${bitmapHeight.get()}] -- data: ${data.size}")
|
||||
var old = pool.acquire()
|
||||
try {
|
||||
if (old == null) {
|
||||
old = LookAroundData(data, bitmapWidth.get(), bitmapHeight.get(), targetX.get(), targetY.get(), targetWidth.get(), targetHeight.get())
|
||||
} else {
|
||||
old.data = data
|
||||
old.bitmapWidth = bitmapWidth.get()
|
||||
old.bitmapHeight = bitmapHeight.get()
|
||||
old.targetX = targetX.get()
|
||||
old.targetY = targetY.get()
|
||||
old.targetWidth = targetWidth.get()
|
||||
old.targetHeight = targetHeight.get()
|
||||
}
|
||||
channel.trySend(old)
|
||||
} finally {
|
||||
if (old != null) {
|
||||
pool.release(old)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onBackCameraVideo(data: ByteArray) {
|
||||
if (bitmapWidth.get() == 0 || bitmapHeight.get() == 0) {
|
||||
val options = BitmapFactory.Options()
|
||||
options.inJustDecodeBounds = true
|
||||
BitmapFactory.decodeByteArray(data,0 , data.size, options)
|
||||
bitmapWidth.set(options.outWidth)
|
||||
bitmapHeight.set(options.outHeight)
|
||||
}
|
||||
Log.d(TAG, "-- onSweeperFutianBackCameraVideo -- bitmap:[${bitmapWidth.get()}, ${bitmapHeight.get()}] -- data: ${data.size}")
|
||||
var old = pool.acquire()
|
||||
try {
|
||||
if (old == null) {
|
||||
old = LookAroundData(data, bitmapWidth.get(), bitmapHeight.get(), targetX.get(), targetY.get(), targetWidth.get(), targetHeight.get())
|
||||
} else {
|
||||
old.data = data
|
||||
old.bitmapWidth = bitmapWidth.get()
|
||||
old.bitmapHeight = bitmapHeight.get()
|
||||
old.targetX = targetX.get()
|
||||
old.targetY = targetY.get()
|
||||
old.targetWidth = targetWidth.get()
|
||||
old.targetHeight = targetHeight.get()
|
||||
}
|
||||
channel.trySend(old)
|
||||
} finally {
|
||||
if (old != null) {
|
||||
pool.release(old)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.mogo.och.taxi.passenger.ui.arrived
|
||||
|
||||
import android.content.*
|
||||
import android.graphics.*
|
||||
import android.graphics.Paint.Style.STROKE
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.os.SystemClock
|
||||
import android.util.*
|
||||
import android.view.SurfaceHolder
|
||||
import android.view.SurfaceView
|
||||
import android.widget.Toast
|
||||
import com.mogo.eagle.core.function.api.autopilot.*
|
||||
import com.mogo.eagle.core.function.api.lookaround.data.*
|
||||
import com.mogo.eagle.core.function.call.autopilot.*
|
||||
import com.mogo.eagle.core.function.call.devatools.*
|
||||
import com.mogo.eagle.core.utilcode.kotlin.*
|
||||
import com.mogo.eagle.core.utilcode.util.ThreadUtils
|
||||
import com.mogo.eagle.core.utilcode.util.Utils
|
||||
import com.mogo.eagle.core.widget.media.video.TextureVideoViewOutlineProvider
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Runnable
|
||||
import kotlinx.coroutines.flow.*
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.math.*
|
||||
|
||||
class RightRearCamSurfaceView: SurfaceView, SurfaceHolder.Callback, Runnable {
|
||||
|
||||
constructor(context: Context?) : super(context)
|
||||
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
companion object {
|
||||
private const val TAG = "M1LookAroundView"
|
||||
}
|
||||
|
||||
init {
|
||||
holder.addCallback(this)
|
||||
}
|
||||
|
||||
private val handler by lazy { AtomicReference<Handler>() }
|
||||
|
||||
private val lookAroundDataProvider by lazy { AroundProviderImpl() }
|
||||
|
||||
private val bitmapPaint by lazy { Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG).also { it.xfermode = null } }
|
||||
|
||||
|
||||
@Volatile
|
||||
private var surfaceWidth = 0
|
||||
|
||||
@Volatile
|
||||
private var surfaceHeight = 0
|
||||
|
||||
@Volatile
|
||||
private var isSurfaceValid = false
|
||||
|
||||
@Volatile
|
||||
private var data: LookAroundData? = null
|
||||
|
||||
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||
val old = handler.get()
|
||||
if (old == null) {
|
||||
handler.set(HandlerThread("look-around-drawer").let { it.start(); Handler(it.looper) })
|
||||
} else {
|
||||
old.looper.quitSafely()
|
||||
handler.set(HandlerThread("look-around-drawer").let { it.start(); Handler(it.looper) })
|
||||
}
|
||||
handler.get()?.removeCallbacks(this)
|
||||
handler.get()?.post(this)
|
||||
isSurfaceValid = true
|
||||
}
|
||||
|
||||
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
|
||||
this.surfaceWidth = width
|
||||
this.surfaceHeight = height
|
||||
}
|
||||
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||
isSurfaceValid = false
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
var isTimedBlock = false
|
||||
try {
|
||||
if (!isSurfaceValid) {
|
||||
isTimedBlock = true
|
||||
return
|
||||
}
|
||||
if (this.surfaceWidth <= 0 || this.surfaceHeight <= 0) {
|
||||
isTimedBlock = true
|
||||
return
|
||||
}
|
||||
val data = this.data ?: return
|
||||
val bitmapWidth = data.bitmapWidth
|
||||
val bitmapHeight = data.bitmapHeight
|
||||
val scaleX = this.surfaceWidth * 1.0f / bitmapWidth
|
||||
val scaleY = this.surfaceHeight * 1.0f / bitmapHeight
|
||||
val bytes = data.data
|
||||
if (bytes == null) {
|
||||
isTimedBlock = true
|
||||
return
|
||||
}
|
||||
val canvas = holder.lockCanvas()
|
||||
try {
|
||||
if (canvas == null) {
|
||||
isTimedBlock = true
|
||||
return
|
||||
}
|
||||
//1. 绘制图片
|
||||
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
|
||||
if (bitmap == null) {
|
||||
isTimedBlock = true
|
||||
return
|
||||
}
|
||||
try {
|
||||
canvas.save()
|
||||
canvas.scale(scaleX, scaleY)
|
||||
canvas.drawBitmap(bitmap, 0f, 0f, bitmapPaint)
|
||||
} finally {
|
||||
canvas.restore()
|
||||
if (!bitmap.isRecycled) {
|
||||
bitmap.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (canvas != null) {
|
||||
holder.unlockCanvasAndPost(canvas)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (isTimedBlock) {
|
||||
try {
|
||||
Thread.sleep(2000)
|
||||
} catch (ignore: Exception) { }
|
||||
}
|
||||
handler.get().post(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
Log.d(TAG, "--- 发起订阅 ---")
|
||||
CallerAutoPilotControlManager.setIsSubscribeBackCameraVideoVideo(1, true)
|
||||
lookAroundDataProvider.init()
|
||||
scope.launch(ThreadUtils.getCpuPool().asCoroutineDispatcher()) {
|
||||
lookAroundDataProvider.flow().also { flow ->
|
||||
flow.onEach {
|
||||
Log.d(TAG, "-- onEach ---:$it")
|
||||
//if (it.isValid()) {
|
||||
data = it
|
||||
//}
|
||||
}.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
Log.d(TAG, "--- 取消订阅 ---")
|
||||
CallerAutoPilotControlManager.setIsSubscribeBackCameraVideoVideo(1, false)
|
||||
lookAroundDataProvider.removeListener()
|
||||
handler.get()?.looper?.quitSafely()
|
||||
}
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
outlineProvider = TextureVideoViewOutlineProvider(36f)
|
||||
clipToOutline = true
|
||||
}
|
||||
}
|
||||
@@ -2,37 +2,32 @@ package com.mogo.och.taxi.passenger.ui.arrived
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoBackCameraVideoListener
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoRoboBusJinlvM1StitchedVideoListener
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager.setIsSubscribeBackCameraVideoVideo
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerBackCameraVideoListenerManager
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerRoboBusJinlvM1StitchedVideoListenerManager
|
||||
import com.mogo.eagle.core.utilcode.mogo.glide.GlideApp
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
|
||||
import com.mogo.eagle.core.utilcode.util.ThreadUtils
|
||||
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
|
||||
import com.mogo.eagle.core.widget.media.video.TextureVideoViewOutlineProvider
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import kotlinx.android.synthetic.main.taxi_p_right_rear_cam.view.actv_cam_position_group
|
||||
import kotlinx.android.synthetic.main.taxi_p_right_rear_cam.view.v_video_right_rear
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 评价View
|
||||
* Created on 2022/5/16
|
||||
*/
|
||||
class RightRearCamView : ConstraintLayout , IMoGoBackCameraVideoListener,
|
||||
IMoGoRoboBusJinlvM1StitchedVideoListener {
|
||||
IMoGoRoboBusJinlvM1StitchedVideoListener,Runnable {
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
@@ -42,24 +37,10 @@ class RightRearCamView : ConstraintLayout , IMoGoBackCameraVideoListener,
|
||||
|
||||
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attributeSet, defStyleAttr, defStyleRes)
|
||||
|
||||
private val requestOptions = RequestOptions()
|
||||
.priority(Priority.HIGH)
|
||||
.skipMemoryCache(true)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.dontAnimate()
|
||||
private var mBitmap: Bitmap? = null
|
||||
|
||||
private val target: CustomTarget<Bitmap?> = object : CustomTarget<Bitmap?>() {
|
||||
private var mBitmapOptions: BitmapFactory.Options? = null //Bitmap管理类,可有效减少Bitmap的OOM问题
|
||||
|
||||
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap?>?) {
|
||||
if (!resource.isRecycled&&v_video_right_rear!=null) {
|
||||
v_video_right_rear.setImageBitmap(resource)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
//这个方法在target被回收时调用,如果在除了imageView以外的地方引用了imageView中的bitmap,在这里清除引用以避免崩溃
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
d(SceneConstant.M_TAXI_P + TAG, "initView")
|
||||
@@ -97,32 +78,85 @@ class RightRearCamView : ConstraintLayout , IMoGoBackCameraVideoListener,
|
||||
v_video_right_rear.setImageResource(R.drawable.taxi_p_right_rear_cam)
|
||||
}
|
||||
|
||||
private fun draw(data: ByteArray) {
|
||||
ThreadUtils.runOnUiThread {
|
||||
if(actv_cam_position_group.visibility == GONE) {
|
||||
actv_cam_position_group.visibility = VISIBLE
|
||||
}
|
||||
GlideApp.with(v_video_right_rear)
|
||||
.asBitmap()
|
||||
.load(data)
|
||||
.placeholder(R.drawable.taxi_p_right_rear_cam)
|
||||
.apply(requestOptions)
|
||||
.into(target)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onBackCameraVideo(data: ByteArray) {
|
||||
draw(data)
|
||||
decodeData(data)
|
||||
}
|
||||
|
||||
override fun onRoboBusJinlvM1StitchedVideo(data: ByteArray) {
|
||||
draw(data)
|
||||
decodeData(data)
|
||||
}
|
||||
|
||||
var preTime :Long=System.currentTimeMillis()
|
||||
|
||||
@Synchronized
|
||||
private fun decodeData(data: ByteArray){
|
||||
val currentTimeMillis = System.currentTimeMillis()
|
||||
val dexTime = currentTimeMillis - preTime
|
||||
preTime = currentTimeMillis
|
||||
if(dexTime<20){
|
||||
return
|
||||
}
|
||||
d(SceneConstant.M_TAXI_P + TAG, "图片频率:$dexTime")
|
||||
if (mBitmapOptions == null) {
|
||||
val bmp = (v_video_right_rear.drawable as BitmapDrawable).bitmap
|
||||
val width = bmp.width
|
||||
val height = bmp.height
|
||||
val config = bmp.config
|
||||
mBitmap = Bitmap.createBitmap(width, height, config)
|
||||
mBitmapOptions = BitmapFactory.Options()
|
||||
//设置Bitmap内存复用
|
||||
mBitmapOptions!!.inBitmap = mBitmap //Bitmap复用内存块,类似对象池,避免不必要的内存分配和回收
|
||||
mBitmapOptions!!.inMutable = true //解码时返回可变Bitmap
|
||||
|
||||
val options = BitmapFactory.Options()
|
||||
options.inJustDecodeBounds = true
|
||||
BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
||||
mBitmapOptions!!.inSampleSize = calculateInSampleSize(options, width, height)
|
||||
}
|
||||
mBitmapOptions?.let {
|
||||
try {
|
||||
val preTime = System.currentTimeMillis()
|
||||
BitmapFactory.decodeByteArray(data, 0, data.size, mBitmapOptions)
|
||||
d(SceneConstant.M_TAXI_P + TAG, "decode时间:${System.currentTimeMillis()-preTime}")
|
||||
UiThreadHandler.post(this)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
|
||||
val width = options.outWidth
|
||||
val height = options.outHeight
|
||||
Log.i(TAG, "calculateInSampleSize: out width and height is $width height $height")
|
||||
var inSampleWidth = 1
|
||||
if (height > reqHeight || width > reqWidth) {
|
||||
val halfHeight = height / 2
|
||||
val halfWidth = width / 2
|
||||
// 采样率设置为2的指数
|
||||
while (halfHeight / inSampleWidth >= reqHeight && halfWidth / inSampleWidth >= reqWidth) {
|
||||
inSampleWidth *= 2
|
||||
}
|
||||
}
|
||||
|
||||
while (width/inSampleWidth>reqWidth||height/inSampleWidth>reqHeight){
|
||||
inSampleWidth++
|
||||
}
|
||||
|
||||
return inSampleWidth
|
||||
}
|
||||
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
outlineProvider = TextureVideoViewOutlineProvider(36f)
|
||||
clipToOutline = true
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
if(actv_cam_position_group?.visibility == GONE) {
|
||||
actv_cam_position_group?.visibility = VISIBLE
|
||||
}
|
||||
v_video_right_rear?.setImageBitmap(mBitmap)
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,8 @@
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/actv_endstation"
|
||||
app:layout_constraintStart_toStartOf="@+id/v_video_right_rear"
|
||||
app:layout_constraintBottom_toTopOf="@+id/v_video_right_rear"
|
||||
app:layout_constraintStart_toStartOf="@+id/v_video_right_rear_view"
|
||||
app:layout_constraintBottom_toTopOf="@+id/v_video_right_rear_view"
|
||||
android:layout_marginBottom="@dimen/dp_26"
|
||||
android:textColor="@android:color/white"
|
||||
android:maxLines="2"
|
||||
|
||||
Reference in New Issue
Block a user