|  |  |  | @ -1,81 +1,55 @@ | 
		
	
		
			
				|  |  |  |  | package com.yinuo.safetywatcher.watcher.utils | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | import android.graphics.Bitmap | 
		
	
		
			
				|  |  |  |  | import android.graphics.BitmapFactory | 
		
	
		
			
				|  |  |  |  | import android.graphics.ImageFormat | 
		
	
		
			
				|  |  |  |  | import android.graphics.Rect | 
		
	
		
			
				|  |  |  |  | import android.graphics.YuvImage | 
		
	
		
			
				|  |  |  |  | import android.opengl.EGL14 | 
		
	
		
			
				|  |  |  |  | import android.os.Handler | 
		
	
		
			
				|  |  |  |  | import android.os.HandlerThread | 
		
	
		
			
				|  |  |  |  | import android.view.TextureView | 
		
	
		
			
				|  |  |  |  | import com.common.commonlib.CommonApplication | 
		
	
		
			
				|  |  |  |  | import com.common.commonlib.utils.BitmapUtils | 
		
	
		
			
				|  |  |  |  | import com.yinuo.library.vlc.encoder.BaseMovieEncoder.EncoderConfig | 
		
	
		
			
				|  |  |  |  | import com.yinuo.library.vlc.encoder.MovieEncoder1 | 
		
	
		
			
				|  |  |  |  | import org.easydarwin.TxtOverlay | 
		
	
		
			
				|  |  |  |  | import org.easydarwin.sw.JNIUtil | 
		
	
		
			
				|  |  |  |  | import java.io.ByteArrayOutputStream | 
		
	
		
			
				|  |  |  |  | import java.io.IOException | 
		
	
		
			
				|  |  |  |  | import java.nio.ByteBuffer | 
		
	
		
			
				|  |  |  |  | import java.util.concurrent.BlockingQueue | 
		
	
		
			
				|  |  |  |  | import java.util.concurrent.LinkedBlockingQueue | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | object RecordHelper { | 
		
	
		
			
				|  |  |  |  |     private val mVideoEncoder: MovieEncoder1 | 
		
	
		
			
				|  |  |  |  |     val width = 1920 | 
		
	
		
			
				|  |  |  |  |     val height = 1080 | 
		
	
		
			
				|  |  |  |  |     private const val width = 1920 | 
		
	
		
			
				|  |  |  |  |     private const val height = 1080 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     val utils by lazy { | 
		
	
		
			
				|  |  |  |  |         NV21ToBitmap(CommonApplication.getContext()) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     private val mTaskQueue: BlockingQueue<ByteArray> = LinkedBlockingQueue() | 
		
	
		
			
				|  |  |  |  |     private val workHandler by lazy { | 
		
	
		
			
				|  |  |  |  |         val mHandlerThread = HandlerThread("recordAndEncode") | 
		
	
		
			
				|  |  |  |  |         mHandlerThread.start() | 
		
	
		
			
				|  |  |  |  |         Handler(mHandlerThread.looper) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     init { | 
		
	
		
			
				|  |  |  |  |         mVideoEncoder = MovieEncoder1(CommonApplication.getContext(), width, height, true) | 
		
	
		
			
				|  |  |  |  |         Thread { | 
		
	
		
			
				|  |  |  |  |             while (true) { | 
		
	
		
			
				|  |  |  |  |                 val take = mTaskQueue.take() | 
		
	
		
			
				|  |  |  |  |                 if (take != null) { | 
		
	
		
			
				|  |  |  |  |                     val overLayBitmap: Bitmap? = TxtOverlay.getOverlayBitmap() | 
		
	
		
			
				|  |  |  |  |                     val dst = ByteArray(take.size) | 
		
	
		
			
				|  |  |  |  |                     JNIUtil.ConvertFromI420(take, dst, width, height, 2) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |                     var mVideoBitmap = utils.nv21ToBitmap(dst, width, height) | 
		
	
		
			
				|  |  |  |  |                     if (overLayBitmap != null && mVideoBitmap != null) { | 
		
	
		
			
				|  |  |  |  |                         mVideoBitmap = BitmapUtils.mergeBitmap(mVideoBitmap!!, overLayBitmap) | 
		
	
		
			
				|  |  |  |  |                     } | 
		
	
		
			
				|  |  |  |  |                     val baos2 = ByteArrayOutputStream() | 
		
	
		
			
				|  |  |  |  |                     mVideoBitmap?.compress(Bitmap.CompressFormat.PNG, 100, baos2) | 
		
	
		
			
				|  |  |  |  |                     val data = baos2.toByteArray() | 
		
	
		
			
				|  |  |  |  |                     mVideoEncoder.frameAvailable(data, System.nanoTime()) | 
		
	
		
			
				|  |  |  |  |     fun onFrameAvailable(view: TextureView) { | 
		
	
		
			
				|  |  |  |  |         if (!mVideoEncoder.isRecording) { | 
		
	
		
			
				|  |  |  |  |             return | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         workHandler.post { | 
		
	
		
			
				|  |  |  |  |             val nanoTime = System.nanoTime() | 
		
	
		
			
				|  |  |  |  |             var bitmap = view.bitmap | 
		
	
		
			
				|  |  |  |  |             bitmap?.let { | 
		
	
		
			
				|  |  |  |  |                 val overLayBitmap: Bitmap? = TxtOverlay.getOverlayBitmap() | 
		
	
		
			
				|  |  |  |  |                 overLayBitmap?.let { | 
		
	
		
			
				|  |  |  |  |                     bitmap = BitmapUtils.mergeBitmap(bitmap!!, overLayBitmap) | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         }.start() | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     fun onFrameAvailable(buffer: ByteBuffer) { | 
		
	
		
			
				|  |  |  |  |         val input = ByteArray(buffer.capacity()) | 
		
	
		
			
				|  |  |  |  |         buffer.clear() | 
		
	
		
			
				|  |  |  |  |         buffer[input] | 
		
	
		
			
				|  |  |  |  |         TxtOverlay.setShowTip("111@2222") | 
		
	
		
			
				|  |  |  |  |         val overLayBitmap: Bitmap? = TxtOverlay.getOverlayBitmap() | 
		
	
		
			
				|  |  |  |  |         val dst = ByteArray(buffer.capacity()) | 
		
	
		
			
				|  |  |  |  |         JNIUtil.ConvertFromI420(input, dst, width, height, 2) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         val yuvimage = YuvImage(dst, ImageFormat.NV21, width, height, null) | 
		
	
		
			
				|  |  |  |  |         val baos = ByteArrayOutputStream() | 
		
	
		
			
				|  |  |  |  |         yuvimage.compressToJpeg(Rect(0, 0, width, height), 80, baos) | 
		
	
		
			
				|  |  |  |  |         val toByteArray = baos.toByteArray() | 
		
	
		
			
				|  |  |  |  |         var mVideoBitmap = BitmapFactory.decodeByteArray(toByteArray, 0, toByteArray.size) | 
		
	
		
			
				|  |  |  |  |         baos.close() | 
		
	
		
			
				|  |  |  |  |         if (overLayBitmap != null && mVideoBitmap != null) { | 
		
	
		
			
				|  |  |  |  |             mVideoBitmap = BitmapUtils.mergeBitmap(mVideoBitmap!!, overLayBitmap) | 
		
	
		
			
				|  |  |  |  |             val buffer = ByteBuffer.allocate(bitmap!!.getByteCount()) | 
		
	
		
			
				|  |  |  |  |             bitmap!!.copyPixelsToBuffer(buffer) | 
		
	
		
			
				|  |  |  |  |             bitmap!!.recycle() | 
		
	
		
			
				|  |  |  |  |             mVideoEncoder.frameAvailable(buffer.array(), nanoTime) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         val yuvByBitmap = getYUVByBitmap(mVideoBitmap) | 
		
	
		
			
				|  |  |  |  |         buffer.clear() | 
		
	
		
			
				|  |  |  |  |         buffer.limit(yuvByBitmap!!.size) | 
		
	
		
			
				|  |  |  |  |         buffer.put(yuvByBitmap) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     fun startRecording() { | 
		
	
	
		
			
				
					|  |  |  | @ -89,151 +63,4 @@ object RecordHelper { | 
		
	
		
			
				|  |  |  |  |             mVideoEncoder.stopRecording() | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     fun rgb2YCbCr420(pixels: IntArray, width: Int, height: Int): ByteArray { | 
		
	
		
			
				|  |  |  |  |         val len = width * height | 
		
	
		
			
				|  |  |  |  |         // yuv格式数组大小,y亮度占len长度,u,v各占len/4长度。 | 
		
	
		
			
				|  |  |  |  |         val yuv = ByteArray(len * 3 / 2) | 
		
	
		
			
				|  |  |  |  |         var y: Int | 
		
	
		
			
				|  |  |  |  |         var u: Int | 
		
	
		
			
				|  |  |  |  |         var v: Int | 
		
	
		
			
				|  |  |  |  |         for (i in 0 until height) { | 
		
	
		
			
				|  |  |  |  |             for (j in 0 until width) { | 
		
	
		
			
				|  |  |  |  |                 // 屏蔽ARGB的透明度值 | 
		
	
		
			
				|  |  |  |  |                 val rgb = pixels[i * width + j] and 0x00FFFFFF | 
		
	
		
			
				|  |  |  |  |                 // 像素的颜色顺序为bgr,移位运算。 | 
		
	
		
			
				|  |  |  |  |                 val r = rgb and 0xFF | 
		
	
		
			
				|  |  |  |  |                 val g = rgb shr 8 and 0xFF | 
		
	
		
			
				|  |  |  |  |                 val b = rgb shr 16 and 0xFF | 
		
	
		
			
				|  |  |  |  |                 // 套用公式 | 
		
	
		
			
				|  |  |  |  |                 y = (66 * r + 129 * g + 25 * b + 128 shr 8) + 16 | 
		
	
		
			
				|  |  |  |  |                 u = (-38 * r - 74 * g + 112 * b + 128 shr 8) + 128 | 
		
	
		
			
				|  |  |  |  |                 v = (112 * r - 94 * g - 18 * b + 128 shr 8) + 128 | 
		
	
		
			
				|  |  |  |  |                 // rgb2yuv | 
		
	
		
			
				|  |  |  |  |                 // y = (int) (0.299 * r + 0.587 * g + 0.114 * b); | 
		
	
		
			
				|  |  |  |  |                 // u = (int) (-0.147 * r - 0.289 * g + 0.437 * b); | 
		
	
		
			
				|  |  |  |  |                 // v = (int) (0.615 * r - 0.515 * g - 0.1 * b); | 
		
	
		
			
				|  |  |  |  |                 // RGB转换YCbCr | 
		
	
		
			
				|  |  |  |  |                 // y = (int) (0.299 * r + 0.587 * g + 0.114 * b); | 
		
	
		
			
				|  |  |  |  |                 // u = (int) (-0.1687 * r - 0.3313 * g + 0.5 * b + 128); | 
		
	
		
			
				|  |  |  |  |                 // if (u > 255) | 
		
	
		
			
				|  |  |  |  |                 // u = 255; | 
		
	
		
			
				|  |  |  |  |                 // v = (int) (0.5 * r - 0.4187 * g - 0.0813 * b + 128); | 
		
	
		
			
				|  |  |  |  |                 // if (v > 255) | 
		
	
		
			
				|  |  |  |  |                 // v = 255; | 
		
	
		
			
				|  |  |  |  |                 // 调整 | 
		
	
		
			
				|  |  |  |  |                 y = if (y < 16) 16 else if (y > 255) 255 else y | 
		
	
		
			
				|  |  |  |  |                 u = if (u < 0) 0 else if (u > 255) 255 else u | 
		
	
		
			
				|  |  |  |  |                 v = if (v < 0) 0 else if (v > 255) 255 else v | 
		
	
		
			
				|  |  |  |  |                 // 赋值 | 
		
	
		
			
				|  |  |  |  |                 yuv[i * width + j] = y.toByte() | 
		
	
		
			
				|  |  |  |  |                 yuv[len + ((i shr 1) * width) + (j and 1.inv()) + 0] = u.toByte() | 
		
	
		
			
				|  |  |  |  |                 yuv[len + (+(i shr 1) * width) + (j and 1.inv()) + 1] = v.toByte() | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         return yuv | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     fun decodeYUV420SP( | 
		
	
		
			
				|  |  |  |  |         rgbBuf: ByteArray?, yuv420sp: ByteArray?, | 
		
	
		
			
				|  |  |  |  |         width: Int, height: Int | 
		
	
		
			
				|  |  |  |  |     ) { | 
		
	
		
			
				|  |  |  |  |         val frameSize = width * height | 
		
	
		
			
				|  |  |  |  |         if (rgbBuf == null) throw NullPointerException("buffer 'rgbBuf' is null") | 
		
	
		
			
				|  |  |  |  |         if (rgbBuf.size < frameSize * 3) throw IllegalArgumentException( | 
		
	
		
			
				|  |  |  |  |             ("buffer 'rgbBuf' size " | 
		
	
		
			
				|  |  |  |  |                     + rgbBuf.size + " < minimum ") + frameSize * 3 | 
		
	
		
			
				|  |  |  |  |         ) | 
		
	
		
			
				|  |  |  |  |         if (yuv420sp == null) throw NullPointerException("buffer 'yuv420sp' is null") | 
		
	
		
			
				|  |  |  |  |         if (yuv420sp.size < frameSize * 3 / 2) throw IllegalArgumentException( | 
		
	
		
			
				|  |  |  |  |             ("buffer 'yuv420sp' size " | 
		
	
		
			
				|  |  |  |  |                     + yuv420sp.size + " < minimum " + (frameSize * 3 / 2)) | 
		
	
		
			
				|  |  |  |  |         ) | 
		
	
		
			
				|  |  |  |  |         var i = 0 | 
		
	
		
			
				|  |  |  |  |         var y = 0 | 
		
	
		
			
				|  |  |  |  |         var uvp = 0 | 
		
	
		
			
				|  |  |  |  |         var u = 0 | 
		
	
		
			
				|  |  |  |  |         var v = 0 | 
		
	
		
			
				|  |  |  |  |         var y1192 = 0 | 
		
	
		
			
				|  |  |  |  |         var r = 0 | 
		
	
		
			
				|  |  |  |  |         var g = 0 | 
		
	
		
			
				|  |  |  |  |         var b = 0 | 
		
	
		
			
				|  |  |  |  |         var j = 0 | 
		
	
		
			
				|  |  |  |  |         var yp = 0 | 
		
	
		
			
				|  |  |  |  |         while (j < height) { | 
		
	
		
			
				|  |  |  |  |             uvp = frameSize + (j shr 1) * width | 
		
	
		
			
				|  |  |  |  |             u = 0 | 
		
	
		
			
				|  |  |  |  |             v = 0 | 
		
	
		
			
				|  |  |  |  |             i = 0 | 
		
	
		
			
				|  |  |  |  |             while (i < width) { | 
		
	
		
			
				|  |  |  |  |                 y = (0xff and (yuv420sp[yp].toInt())) - 16 | 
		
	
		
			
				|  |  |  |  |                 if (y < 0) y = 0 | 
		
	
		
			
				|  |  |  |  |                 if ((i and 1) == 0) { | 
		
	
		
			
				|  |  |  |  |                     v = (0xff and yuv420sp[uvp++].toInt()) - 128 | 
		
	
		
			
				|  |  |  |  |                     u = (0xff and yuv420sp[uvp++].toInt()) - 128 | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |                 y1192 = 1192 * y | 
		
	
		
			
				|  |  |  |  |                 r = (y1192 + 1634 * v) | 
		
	
		
			
				|  |  |  |  |                 g = (y1192 - (833 * v) - (400 * u)) | 
		
	
		
			
				|  |  |  |  |                 b = (y1192 + 2066 * u) | 
		
	
		
			
				|  |  |  |  |                 if (r < 0) r = 0 else if (r > 262143) r = 262143 | 
		
	
		
			
				|  |  |  |  |                 if (g < 0) g = 0 else if (g > 262143) g = 262143 | 
		
	
		
			
				|  |  |  |  |                 if (b < 0) b = 0 else if (b > 262143) b = 262143 | 
		
	
		
			
				|  |  |  |  |                 rgbBuf[yp * 3] = (r shr 10).toByte() | 
		
	
		
			
				|  |  |  |  |                 rgbBuf[yp * 3 + 1] = (g shr 10).toByte() | 
		
	
		
			
				|  |  |  |  |                 rgbBuf[yp * 3 + 2] = (b shr 10).toByte() | 
		
	
		
			
				|  |  |  |  |                 i++ | 
		
	
		
			
				|  |  |  |  |                 yp++ | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             j++ | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     /* | 
		
	
		
			
				|  |  |  |  | 	 * 获取位图的RGB数据 | 
		
	
		
			
				|  |  |  |  | 	 */ | 
		
	
		
			
				|  |  |  |  |     fun getRGBByBitmap(bitmap: Bitmap?): ByteArray? { | 
		
	
		
			
				|  |  |  |  |         if (bitmap == null) { | 
		
	
		
			
				|  |  |  |  |             return null | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         val width = bitmap.width | 
		
	
		
			
				|  |  |  |  |         val height = bitmap.height | 
		
	
		
			
				|  |  |  |  |         val size = width * height | 
		
	
		
			
				|  |  |  |  |         val pixels = IntArray(size) | 
		
	
		
			
				|  |  |  |  |         bitmap.getPixels(pixels, 0, width, 0, 0, width, height) | 
		
	
		
			
				|  |  |  |  |         return convertColorToByte(pixels) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     /* | 
		
	
		
			
				|  |  |  |  | 	 * 获取位图的YUV数据 | 
		
	
		
			
				|  |  |  |  | 	 */ | 
		
	
		
			
				|  |  |  |  |     fun getYUVByBitmap(bitmap: Bitmap?): ByteArray? { | 
		
	
		
			
				|  |  |  |  |         if (bitmap == null) { | 
		
	
		
			
				|  |  |  |  |             return null | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         val width = bitmap.width | 
		
	
		
			
				|  |  |  |  |         val height = bitmap.height | 
		
	
		
			
				|  |  |  |  |         val size = width * height | 
		
	
		
			
				|  |  |  |  |         val pixels = IntArray(size) | 
		
	
		
			
				|  |  |  |  |         bitmap.getPixels(pixels, 0, width, 0, 0, width, height) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         // byte[] data = convertColorToByte(pixels); | 
		
	
		
			
				|  |  |  |  |         return rgb2YCbCr420(pixels, width, height) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     /* | 
		
	
		
			
				|  |  |  |  | 	 * 像素数组转化为RGB数组 | 
		
	
		
			
				|  |  |  |  | 	 */ | 
		
	
		
			
				|  |  |  |  |     fun convertColorToByte(color: IntArray?): ByteArray? { | 
		
	
		
			
				|  |  |  |  |         if (color == null) { | 
		
	
		
			
				|  |  |  |  |             return null | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         val data = ByteArray(color.size * 3) | 
		
	
		
			
				|  |  |  |  |         for (i in color.indices) { | 
		
	
		
			
				|  |  |  |  |             data[i * 3] = ((color[i] shr 16) and 0xff).toByte() | 
		
	
		
			
				|  |  |  |  |             data[i * 3 + 1] = ((color[i] shr 8) and 0xff).toByte() | 
		
	
		
			
				|  |  |  |  |             data[i * 3 + 2] = (color[i] and 0xff).toByte() | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         return data | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
	
		
			
				
					|  |  |  | 
 |