From 70820f31ff9bb2beae2d82c0625ea039cfb1bc32 Mon Sep 17 00:00:00 2001 From: yangyakun Date: Mon, 31 Jul 2023 11:21:18 +0800 Subject: [PATCH] =?UTF-8?q?[3.4.0]=20[=E5=90=8E=E8=A7=86=E9=95=9C=E4=BC=98?= =?UTF-8?q?=E5=8C=96]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/arrived/AroundProviderImpl.kt | 135 ++++++++++++++ .../ui/arrived/RightRearCamSurfaceView.kt | 171 ++++++++++++++++++ .../passenger/ui/arrived/RightRearCamView.kt | 120 +++++++----- .../res/layout/taxi_p_arrived_end_panel.xml | 4 +- 4 files changed, 385 insertions(+), 45 deletions(-) create mode 100644 OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/AroundProviderImpl.kt create mode 100644 OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/RightRearCamSurfaceView.kt diff --git a/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/AroundProviderImpl.kt b/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/AroundProviderImpl.kt new file mode 100644 index 0000000000..834505adc6 --- /dev/null +++ b/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/AroundProviderImpl.kt @@ -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 = Channel(CONFLATED) + get() { + return if (field.isClosedForReceive || field.isClosedForSend) { + Channel(CONFLATED) + } else { + field + } + } + + private val pool by lazy { Pools.SynchronizedPool(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 = 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) + } + } + } +} \ No newline at end of file diff --git a/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/RightRearCamSurfaceView.kt b/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/RightRearCamSurfaceView.kt new file mode 100644 index 0000000000..273dd793ab --- /dev/null +++ b/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/RightRearCamSurfaceView.kt @@ -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() } + + 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 + } +} \ No newline at end of file diff --git a/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/RightRearCamView.kt b/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/RightRearCamView.kt index 96e0cc9214..6449fc6f0c 100644 --- a/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/RightRearCamView.kt +++ b/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/arrived/RightRearCamView.kt @@ -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 = object : CustomTarget() { + private var mBitmapOptions: BitmapFactory.Options? = null //Bitmap管理类,可有效减少Bitmap的OOM问题 - override fun onResourceReady(resource: Bitmap, transition: Transition?) { - 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) + } } \ No newline at end of file diff --git a/OCH/mogo-och-taxi-passenger/src/main/res/layout/taxi_p_arrived_end_panel.xml b/OCH/mogo-och-taxi-passenger/src/main/res/layout/taxi_p_arrived_end_panel.xml index f7e396c76d..54b4bd2d17 100644 --- a/OCH/mogo-och-taxi-passenger/src/main/res/layout/taxi_p_arrived_end_panel.xml +++ b/OCH/mogo-och-taxi-passenger/src/main/res/layout/taxi_p_arrived_end_panel.xml @@ -37,8 +37,8 @@