[后视镜优化]
This commit is contained in:
yangyakun
2023-07-31 11:21:18 +08:00
parent 37a2710dd9
commit 70820f31ff
4 changed files with 385 additions and 45 deletions

View File

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

View File

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

View File

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

View File

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