OSDN Git Service

Thetaの制御機能を搭載した。
[gokigen/mangle.git] / app / src / main / java / jp / osdn / gokigen / gokigenassets / liveview / image / CameraLiveViewListenerImpl.kt
1 package jp.osdn.gokigen.gokigenassets.liveview.image
2
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
19 import java.util.*
20
21
22 data class MyImageByteArray(val imageData : ByteArray, val rotationDegrees: Int)
23
24 class CameraLiveViewListenerImpl(private val context: Context) : IImageDataReceiver, IImageProvider, ImageAnalysis.Analyzer
25 {
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>()
31
32     companion object
33     {
34         private val TAG = CameraLiveViewListenerImpl::class.java.simpleName
35     }
36
37     init
38     {
39         refresher.clear()
40     }
41
42     fun setRefresher(refresher: ILiveViewRefresher)
43     {
44         this.refresher.add(refresher)
45         setupLiveviewCache()
46     }
47
48     override fun onUpdateLiveView(data: ByteArray, metadata: Map<String, Any>?)
49     {
50         try
51         {
52             //Log.v(TAG, "onUpdateLiveView()")
53             insertCache(data, 0)
54             isImageReceived = true
55             refresh()
56         }
57         catch (t : Throwable)
58         {
59             t.printStackTrace()
60         }
61     }
62
63     @SuppressLint("UnsafeExperimentalUsageError")
64     override fun analyze(imageProxy: ImageProxy)
65     {
66         try
67         {
68             val rotationDegrees = imageProxy.imageInfo.rotationDegrees
69             isImageReceived = true
70
71             if (imageProxy.image?.planes?.get(1)?.pixelStride == 1)
72             {
73                 // from I420 format
74                 convertToBitmapI420(imageProxy, rotationDegrees)
75                 //Log.v(TAG, " convertToBitmapI420 $rotationDegrees ")
76                 return
77             }
78             if (imageProxy.format == ImageFormat.YUV_420_888)
79             {
80                 if (imageProxy.image?.planes?.get(1)?.rowStride != imageProxy.image?.width)
81                 {
82                     //  Format : NV12, YU12, YV12  YUV_420_888
83                     convertToBitmapYUV420888(imageProxy, rotationDegrees)
84                     //Log.v(TAG, " convertToBitmapYUV420888 $rotationDegrees ")
85                     return
86                 }
87             }
88             convertToBitmapYUV420888NV21(imageProxy, rotationDegrees)
89             //convertToBitmapYUV420888(imageProxy, rotationDegrees)
90             //Log.v(TAG, " convertToBitmapYUV420888NV21 $rotationDegrees ")
91         }
92         catch (e: Throwable)
93         {
94             e.printStackTrace()
95         }
96     }
97
98     private fun convertToBitmapI420(imageProxy: ImageProxy, rotationDegrees: Int)
99     {
100         //Log.v(TAG, " convertToBitmap(I420) $rotationDegrees ")
101
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
106
107         val ySize = yBuffer.remaining()
108         val uSize = uBuffer.remaining()
109         val vSize = vBuffer.remaining()
110
111         val nv21 = ByteArray(ySize + uSize + vSize)
112         yBuffer.get(nv21, 0, ySize)
113         try
114         {
115             var orgIndex = 0
116             var index = ySize
117             while (index < (ySize + uSize + vSize))
118             {
119                 nv21[index++] = imageProxy.planes[2].buffer.get(orgIndex)
120                 nv21[index++] = imageProxy.planes[1].buffer.get(orgIndex)
121                 orgIndex++
122             }
123         }
124         catch (t : Throwable)
125         {
126             t.printStackTrace()
127         }
128
129         val width = imageProxy.width
130         val height = imageProxy.height
131         val yuvImage = YuvImage(nv21, NV21, width, height, null)
132
133         val out = ByteArrayOutputStream()
134         yuvImage.compressToJpeg(Rect(0, 0, width, height), 100, out)
135
136         insertCache(out.toByteArray(), rotationDegrees)
137         imageProxy.close()
138         refresh()
139     }
140
141
142     private fun convertToBitmapYUV420888(imageProxy: ImageProxy, rotationDegrees: Int)
143     {
144         //Log.v(TAG, " convertToBitmap(YUV420-888) $rotationDegrees ")
145
146         val yBuffer = imageProxy.planes[0].buffer
147         val uBuffer = imageProxy.planes[1].buffer
148         val vBuffer = imageProxy.planes[2].buffer
149
150         val ySize = yBuffer.remaining()
151         val uSize = uBuffer.remaining()
152         val vSize = vBuffer.remaining()
153
154         val nv21 = ByteArray(ySize + uSize + vSize)
155         var outputOffset = 0
156
157         /////////  Y BUFFER  /////////
158         if (imageProxy.planes[0].pixelStride != 1)
159         {
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))
163             {
164                 yBuffer.position(row * imageProxy.planes[0].rowStride)
165                 yBuffer.get(rowBuffer1, 0, imageProxy.planes[0].pixelStride)
166                 if (outputOffset > 0)
167                 {
168                     for (col in 0 until imageProxy.width)
169                     {
170                         nv21[outputOffset] = rowBuffer1[col * imageProxy.planes[0].pixelStride]
171                         outputOffset += imageProxy.planes[0].pixelStride
172                     }
173                 }
174             }
175         }
176         else
177         {
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)
181             {
182                 yBuffer.position(row * imageProxy.planes[0].rowStride)
183                 yBuffer.get(nv21, outputOffset, imageProxy.width)
184                 outputOffset += imageProxy.width
185             }
186         }
187
188         /////////  V BUFFER  /////////
189         try
190         {
191             for (row in 0 until ((imageProxy.height / imageProxy.planes[2].pixelStride) - 1))
192             {
193                 vBuffer.position(row * imageProxy.planes[2].rowStride)
194                 vBuffer.get(nv21, outputOffset, imageProxy.width)
195                 outputOffset += imageProxy.width
196             }
197         }
198         catch (e : Exception)
199         {
200             e.printStackTrace()
201         }
202
203         /////////  U BUFFER  /////////
204         try
205         {
206             for (row in 0 until ((imageProxy.height / imageProxy.planes[1].pixelStride) - 1))
207             {
208                 uBuffer.position(row * imageProxy.planes[1].rowStride)
209                 uBuffer.get(nv21, outputOffset, imageProxy.width)
210                 outputOffset += imageProxy.width
211             }
212         }
213         catch (e : Exception)
214         {
215             e.printStackTrace()
216         }
217
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)
223
224         insertCache(out.toByteArray(), rotationDegrees)
225         imageProxy.close()
226         refresh()
227     }
228
229
230     private fun convertToBitmapYUV420888NV21(imageProxy: ImageProxy, rotationDegrees: Int)
231     {
232         //Log.v(TAG, " convertToBitmap(YUV420-NV21) $rotationDegrees ")
233
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
238
239         val ySize = yBuffer.remaining()
240         val uSize = uBuffer.remaining()
241         val vSize = vBuffer.remaining()
242
243         val nv21 = ByteArray(ySize + uSize + vSize)
244
245         yBuffer.get(nv21, 0, ySize)
246         vBuffer.get(nv21, ySize, vSize)
247         uBuffer.get(nv21, ySize + vSize, uSize)
248
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)
254
255         insertCache(out.toByteArray(), rotationDegrees)
256         imageProxy.close()
257         refresh()
258     }
259
260     private fun getImageBitmap(image: MyImageByteArray) : Bitmap?
261     {
262         var imageBitmap : Bitmap? = null
263         try
264         {
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)
269             System.gc()
270         }
271         catch (t: Throwable)
272         {
273             t.printStackTrace()
274         }
275         return (imageBitmap)
276     }
277
278     private fun insertCache(byteArray : ByteArray, rotationDegrees: Int)
279     {
280         try
281         {
282             if ((cachePics.size != 0)&&(cachePics.size >= maxCachePics))
283             {
284                 cachePics.removeAt(0)
285             }
286             cachePics.add(MyImageByteArray(byteArray, rotationDegrees))
287             if ((maxCachePics > 0)&&(cachePics.size != maxCachePics))
288             {
289                 Log.v(TAG, " -=-=- image cache : ${cachePics.size} / $maxCachePics")
290             }
291         }
292         catch (e : Exception)
293         {
294             e.printStackTrace()
295         }
296     }
297
298     override fun getImage(position: Float) : Bitmap
299     {
300         try
301         {
302             if (cachePics.size == 0)
303             {
304                 // 画像が入っていない...
305                 return (BitmapFactory.decodeResource(context.resources, ID_DRAWABLE_SPLASH_IMAGE))
306             }
307
308             val pos = (position * maxCachePics.toFloat()).toInt()
309             //Log.v(TAG, " getImage (pos: $position : $pos)")
310             val image : MyImageByteArray =
311             if (pos >= cachePics.size)
312             {
313                 cachePics[cachePics.size - 1]
314             }
315             else
316             {
317                 cachePics[pos]
318             }
319             val imageBitmap = getImageBitmap(image)
320             if (imageBitmap != null)
321             {
322                 return (imageBitmap)
323             }
324         }
325         catch (e : Throwable)
326         {
327             e.printStackTrace()
328         }
329         return (BitmapFactory.decodeResource(context.resources, ID_DRAWABLE_SPLASH_IMAGE))
330     }
331
332     fun isImageReceived() : Boolean
333     {
334         return (isImageReceived)
335     }
336
337     private fun refresh()
338     {
339         try
340         {
341             for (p in refresher)
342             {
343                 p.refresh()
344             }
345         }
346         catch (e : Exception)
347         {
348             e.printStackTrace()
349         }
350     }
351
352     private fun setupLiveviewCache()
353     {
354         val preference = PreferenceAccessWrapper(context)
355         if (!preference.getBoolean(ID_PREFERENCE_CACHE_LIVE_VIEW_PICTURES, false))
356         {
357             return
358         }
359
360         cachePics.clear()
361         val nofCachePics = preference.getString(ID_PREFERENCE_NUMBER_OF_CACHE_PICTURES, ID_PREFERENCE_NUMBER_OF_CACHE_PICTURES_DEFAULT_VALUE)
362         maxCachePics = try {
363             nofCachePics.toInt()
364         } catch (e: Exception) {
365             e.printStackTrace()
366             500
367         }
368     }
369 }