1 package jp.osdn.gokigen.gokigenassets.liveview.image
3 import android.annotation.SuppressLint
4 import android.content.Context
5 import android.graphics.*
6 import android.graphics.ImageFormat.NV21
7 import android.util.Log
8 import androidx.camera.core.ImageAnalysis
9 import androidx.camera.core.ImageProxy
10 import jp.osdn.gokigen.gokigenassets.constants.IApplicationConstantConvert.Companion.ID_DRAWABLE_SPLASH_IMAGE
11 import jp.osdn.gokigen.gokigenassets.constants.IApplicationConstantConvert.Companion.ID_PREFERENCE_CACHE_LIVE_VIEW_PICTURES
12 import jp.osdn.gokigen.gokigenassets.constants.IApplicationConstantConvert.Companion.ID_PREFERENCE_NUMBER_OF_CACHE_PICTURES
13 import jp.osdn.gokigen.gokigenassets.constants.IApplicationConstantConvert.Companion.ID_PREFERENCE_NUMBER_OF_CACHE_PICTURES_DEFAULT_VALUE
14 import jp.osdn.gokigen.gokigenassets.liveview.ILiveViewRefresher
15 import jp.osdn.gokigen.gokigenassets.liveview.bitmapconvert.IPreviewImageConverter
16 import jp.osdn.gokigen.gokigenassets.liveview.bitmapconvert.ImageConvertFactory
17 import jp.osdn.gokigen.gokigenassets.preference.PreferenceAccessWrapper
18 import java.io.ByteArrayOutputStream
22 data class MyImageByteArray(val imageData : ByteArray, val rotationDegrees: Int)
24 class CameraLiveViewListenerImpl(private val context: Context) : IImageDataReceiver, IImageProvider, ImageAnalysis.Analyzer
26 private var cachePics = ArrayList<MyImageByteArray>()
27 private var isImageReceived = false
28 private var maxCachePics : Int = 0
29 private var bitmapConverter : IPreviewImageConverter = ImageConvertFactory().getImageConverter(0)
30 private val refresher = ArrayList<ILiveViewRefresher>()
34 private val TAG = CameraLiveViewListenerImpl::class.java.simpleName
42 fun setRefresher(refresher: ILiveViewRefresher)
44 this.refresher.add(refresher)
48 override fun onUpdateLiveView(data: ByteArray, metadata: Map<String, Any>?)
52 //Log.v(TAG, "onUpdateLiveView()")
54 isImageReceived = true
63 @SuppressLint("UnsafeExperimentalUsageError")
64 override fun analyze(imageProxy: ImageProxy)
68 val rotationDegrees = imageProxy.imageInfo.rotationDegrees
69 isImageReceived = true
71 if (imageProxy.image?.planes?.get(1)?.pixelStride == 1)
74 convertToBitmapI420(imageProxy, rotationDegrees)
75 //Log.v(TAG, " convertToBitmapI420 $rotationDegrees ")
78 if (imageProxy.format == ImageFormat.YUV_420_888)
80 if (imageProxy.image?.planes?.get(1)?.rowStride != imageProxy.image?.width)
82 // Format : NV12, YU12, YV12 YUV_420_888
83 convertToBitmapYUV420888(imageProxy, rotationDegrees)
84 //Log.v(TAG, " convertToBitmapYUV420888 $rotationDegrees ")
88 convertToBitmapYUV420888NV21(imageProxy, rotationDegrees)
89 //convertToBitmapYUV420888(imageProxy, rotationDegrees)
90 //Log.v(TAG, " convertToBitmapYUV420888NV21 $rotationDegrees ")
98 private fun convertToBitmapI420(imageProxy: ImageProxy, rotationDegrees: Int)
100 //Log.v(TAG, " convertToBitmap(I420) $rotationDegrees ")
102 // ImageFormat.YUV_420_888 : 35
103 val yBuffer = imageProxy.planes[0].buffer
104 val uBuffer = imageProxy.planes[1].buffer
105 val vBuffer = imageProxy.planes[2].buffer
107 val ySize = yBuffer.remaining()
108 val uSize = uBuffer.remaining()
109 val vSize = vBuffer.remaining()
111 val nv21 = ByteArray(ySize + uSize + vSize)
112 yBuffer.get(nv21, 0, ySize)
117 while (index < (ySize + uSize + vSize))
119 nv21[index++] = imageProxy.planes[2].buffer.get(orgIndex)
120 nv21[index++] = imageProxy.planes[1].buffer.get(orgIndex)
124 catch (t : Throwable)
129 val width = imageProxy.width
130 val height = imageProxy.height
131 val yuvImage = YuvImage(nv21, NV21, width, height, null)
133 val out = ByteArrayOutputStream()
134 yuvImage.compressToJpeg(Rect(0, 0, width, height), 100, out)
136 insertCache(out.toByteArray(), rotationDegrees)
142 private fun convertToBitmapYUV420888(imageProxy: ImageProxy, rotationDegrees: Int)
144 //Log.v(TAG, " convertToBitmap(YUV420-888) $rotationDegrees ")
146 val yBuffer = imageProxy.planes[0].buffer
147 val uBuffer = imageProxy.planes[1].buffer
148 val vBuffer = imageProxy.planes[2].buffer
150 val ySize = yBuffer.remaining()
151 val uSize = uBuffer.remaining()
152 val vSize = vBuffer.remaining()
154 val nv21 = ByteArray(ySize + uSize + vSize)
157 ///////// Y BUFFER /////////
158 if (imageProxy.planes[0].pixelStride != 1)
160 //Log.v(TAG, " [0] pixelStride = ${imageProxy.planes[0].pixelStride}, rowStride = ${imageProxy.planes[0].rowStride}, width = ${imageProxy.width}")
161 val rowBuffer1 = ByteArray(imageProxy.planes[0].rowStride)
162 for (row in 0 until (imageProxy.height / imageProxy.planes[0].pixelStride))
164 yBuffer.position(row * imageProxy.planes[0].rowStride)
165 yBuffer.get(rowBuffer1, 0, imageProxy.planes[0].pixelStride)
166 if (outputOffset > 0)
168 for (col in 0 until imageProxy.width)
170 nv21[outputOffset] = rowBuffer1[col * imageProxy.planes[0].pixelStride]
171 outputOffset += imageProxy.planes[0].pixelStride
178 // imageProxy.planes[0].pixelStride == 1
179 //Log.v(TAG, " [0] pixelStride = ${imageProxy.planes[0].pixelStride}, rowStride = ${imageProxy.planes[0].rowStride}, width = ${imageProxy.width}, height = ${imageProxy.height}")
180 for (row in 0 until imageProxy.height)
182 yBuffer.position(row * imageProxy.planes[0].rowStride)
183 yBuffer.get(nv21, outputOffset, imageProxy.width)
184 outputOffset += imageProxy.width
188 ///////// V BUFFER /////////
191 for (row in 0 until ((imageProxy.height / imageProxy.planes[2].pixelStride) - 1))
193 vBuffer.position(row * imageProxy.planes[2].rowStride)
194 vBuffer.get(nv21, outputOffset, imageProxy.width)
195 outputOffset += imageProxy.width
198 catch (e : Exception)
203 ///////// U BUFFER /////////
206 for (row in 0 until ((imageProxy.height / imageProxy.planes[1].pixelStride) - 1))
208 uBuffer.position(row * imageProxy.planes[1].rowStride)
209 uBuffer.get(nv21, outputOffset, imageProxy.width)
210 outputOffset += imageProxy.width
213 catch (e : Exception)
218 val out = ByteArrayOutputStream()
219 val width = imageProxy.width
220 val height = imageProxy.height
221 val yuvImage = YuvImage(nv21, NV21, width, height, null)
222 yuvImage.compressToJpeg(Rect(0, 0, width, height), 100, out)
224 insertCache(out.toByteArray(), rotationDegrees)
230 private fun convertToBitmapYUV420888NV21(imageProxy: ImageProxy, rotationDegrees: Int)
232 //Log.v(TAG, " convertToBitmap(YUV420-NV21) $rotationDegrees ")
234 // ImageFormat.YUV_420_888 : 35
235 val yBuffer = imageProxy.planes[0].buffer
236 val uBuffer = imageProxy.planes[1].buffer
237 val vBuffer = imageProxy.planes[2].buffer
239 val ySize = yBuffer.remaining()
240 val uSize = uBuffer.remaining()
241 val vSize = vBuffer.remaining()
243 val nv21 = ByteArray(ySize + uSize + vSize)
245 yBuffer.get(nv21, 0, ySize)
246 vBuffer.get(nv21, ySize, vSize)
247 uBuffer.get(nv21, ySize + vSize, uSize)
249 val out = ByteArrayOutputStream()
250 val width = imageProxy.width
251 val height = imageProxy.height
252 val yuvImage = YuvImage(nv21, NV21, width, height, null)
253 yuvImage.compressToJpeg(Rect(0, 0, width, height), 100, out)
255 insertCache(out.toByteArray(), rotationDegrees)
260 private fun getImageBitmap(image: MyImageByteArray) : Bitmap?
262 var imageBitmap : Bitmap? = null
265 val rotationMatrix = Matrix()
266 rotationMatrix.postRotate(image.rotationDegrees.toFloat())
267 imageBitmap = BitmapFactory.decodeByteArray(image.imageData, 0, image.imageData.size)
268 imageBitmap = Bitmap.createBitmap(imageBitmap, 0, 0, imageBitmap.width, imageBitmap.height, rotationMatrix, true)
278 private fun insertCache(byteArray : ByteArray, rotationDegrees: Int)
282 if ((cachePics.size != 0)&&(cachePics.size >= maxCachePics))
284 cachePics.removeAt(0)
286 cachePics.add(MyImageByteArray(byteArray, rotationDegrees))
287 if ((maxCachePics > 0)&&(cachePics.size != maxCachePics))
289 Log.v(TAG, " -=-=- image cache : ${cachePics.size} / $maxCachePics")
292 catch (e : Exception)
298 override fun getImage(position: Float) : Bitmap
302 if (cachePics.size == 0)
305 return (BitmapFactory.decodeResource(context.resources, ID_DRAWABLE_SPLASH_IMAGE))
308 val pos = (position * maxCachePics.toFloat()).toInt()
309 //Log.v(TAG, " getImage (pos: $position : $pos)")
310 val image : MyImageByteArray =
311 if (pos >= cachePics.size)
313 cachePics[cachePics.size - 1]
319 val imageBitmap = getImageBitmap(image)
320 if (imageBitmap != null)
325 catch (e : Throwable)
329 return (BitmapFactory.decodeResource(context.resources, ID_DRAWABLE_SPLASH_IMAGE))
332 fun isImageReceived() : Boolean
334 return (isImageReceived)
337 private fun refresh()
346 catch (e : Exception)
352 private fun setupLiveviewCache()
354 val preference = PreferenceAccessWrapper(context)
355 if (!preference.getBoolean(ID_PREFERENCE_CACHE_LIVE_VIEW_PICTURES, false))
361 val nofCachePics = preference.getString(ID_PREFERENCE_NUMBER_OF_CACHE_PICTURES, ID_PREFERENCE_NUMBER_OF_CACHE_PICTURES_DEFAULT_VALUE)
364 } catch (e: Exception) {