From f0a7a69eae86cb25636abd1947add91bd3422aed Mon Sep 17 00:00:00 2001 From: xiaowusky Date: Wed, 27 Sep 2023 16:23:23 +0800 Subject: [PATCH] =?UTF-8?q?desc:record=E3=80=81push=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../safetywatcher/watcher/ui/HomeActivity.kt | 3 +- .../watcher/utils/RecordHelper.kt | 225 ++---------------- .../easydarwin/video/EasyPlayerClient.java | 82 +++---- 3 files changed, 69 insertions(+), 241 deletions(-) diff --git a/app/src/main/java/com/yinuo/safetywatcher/watcher/ui/HomeActivity.kt b/app/src/main/java/com/yinuo/safetywatcher/watcher/ui/HomeActivity.kt index 1499bf6..cf42614 100644 --- a/app/src/main/java/com/yinuo/safetywatcher/watcher/ui/HomeActivity.kt +++ b/app/src/main/java/com/yinuo/safetywatcher/watcher/ui/HomeActivity.kt @@ -33,6 +33,7 @@ import org.easydarwin.TxtOverlay import org.easydarwin.video.EasyPlayerClient import java.io.File + class HomeActivity : NoOptionsActivity() { private var warnDialog: ConfirmDialog? = null @@ -101,7 +102,7 @@ class HomeActivity : NoOptionsActivity() { */ private fun setForCamera() { mClient = EasyPlayerClient(this@HomeActivity, mBinding.surface, null) { -// RecordHelper.onFrameAvailable(it) + RecordHelper.onFrameAvailable(mBinding.surface) if (!AppData.hasCameraData()) { AppData.setCameraData(true) changeViewStatus() diff --git a/app/src/main/java/com/yinuo/safetywatcher/watcher/utils/RecordHelper.kt b/app/src/main/java/com/yinuo/safetywatcher/watcher/utils/RecordHelper.kt index d765eba..3fecc41 100644 --- a/app/src/main/java/com/yinuo/safetywatcher/watcher/utils/RecordHelper.kt +++ b/app/src/main/java/com/yinuo/safetywatcher/watcher/utils/RecordHelper.kt @@ -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 = 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 - } } diff --git a/library-rtsp/src/main/java/org/easydarwin/video/EasyPlayerClient.java b/library-rtsp/src/main/java/org/easydarwin/video/EasyPlayerClient.java index 31bdad3..8199529 100644 --- a/library-rtsp/src/main/java/org/easydarwin/video/EasyPlayerClient.java +++ b/library-rtsp/src/main/java/org/easydarwin/video/EasyPlayerClient.java @@ -926,16 +926,16 @@ public class EasyPlayerClient implements Client.SourceCallBack { Log.i(TAG, String.format("config codec:%s", format)); MediaCodec codec = MediaCodec.createByCodecName(ci.getName()); - codec.configure(format, i420callback != null ? null : mSurface, null, 0); + codec.configure(format, mSurface, null, 0); codec.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT); codec.start(); mCodec = codec; - if (i420callback != null) { - final VideoCodec.VideoDecoderLite decoder = new VideoCodec.VideoDecoderLite(); - decoder.create(mSurface, frameInfo.codec == EASY_SDK_VIDEO_CODEC_H264); - displayer = decoder; - } +// if (i420callback != null) { +// final VideoCodec.VideoDecoderLite decoder = new VideoCodec.VideoDecoderLite(); +// decoder.create(mSurface, frameInfo.codec == EASY_SDK_VIDEO_CODEC_H264); +// displayer = decoder; +// } } catch (Throwable e) { if (mCodec != null) { mCodec.release(); @@ -1129,47 +1129,47 @@ public class EasyPlayerClient implements Client.SourceCallBack { } if (i420callback != null && outputBuffer != null) { - if (sliceHeight != realHeight) { - ByteBuffer tmp = ByteBuffer.allocateDirect(realWidth * realHeight * 3 / 2); - outputBuffer.clear(); - outputBuffer.limit(realWidth * realHeight); - tmp.put(outputBuffer); - - outputBuffer.clear(); - outputBuffer.position(realWidth * sliceHeight); - outputBuffer.limit((realWidth * sliceHeight + realWidth * realHeight / 4)); - tmp.put(outputBuffer); - - outputBuffer.clear(); - outputBuffer.position(realWidth * sliceHeight + realWidth * realHeight / 4); - outputBuffer.limit((realWidth * sliceHeight + realWidth * realHeight / 4 + realWidth * realHeight / 4)); - tmp.put(outputBuffer); - - tmp.clear(); - outputBuffer = tmp; - } +// if (sliceHeight != realHeight) { +// ByteBuffer tmp = ByteBuffer.allocateDirect(realWidth * realHeight * 3 / 2); +// outputBuffer.clear(); +// outputBuffer.limit(realWidth * realHeight); +// tmp.put(outputBuffer); +// +// outputBuffer.clear(); +// outputBuffer.position(realWidth * sliceHeight); +// outputBuffer.limit((realWidth * sliceHeight + realWidth * realHeight / 4)); +// tmp.put(outputBuffer); +// +// outputBuffer.clear(); +// outputBuffer.position(realWidth * sliceHeight + realWidth * realHeight / 4); +// outputBuffer.limit((realWidth * sliceHeight + realWidth * realHeight / 4 + realWidth * realHeight / 4)); +// tmp.put(outputBuffer); +// +// tmp.clear(); +// outputBuffer = tmp; +// } if (mColorFormat == COLOR_FormatYUV420SemiPlanar || mColorFormat == COLOR_FormatYUV420PackedSemiPlanar || mColorFormat == COLOR_TI_FormatYUV420PackedSemiPlanar) { - byte[] in = new byte[realWidth * realHeight * 3 / 2]; - outputBuffer.clear(); - outputBuffer.get(in); - - // yuvuv_to_yuv - JNIUtil.yuvConvert(in, realWidth, realHeight, 4); -// // 旋转90或180或270度 -// yuvRotate(in, 0, realWidth, realHeight, 90); - - ByteBuffer tmp = ByteBuffer.allocateDirect(realWidth * realHeight * 3 / 2); - tmp.clear(); - tmp.put(in); +// byte[] in = new byte[realWidth * realHeight * 3 / 2]; +// outputBuffer.clear(); +// outputBuffer.get(in); +// +// // yuvuv_to_yuv +// JNIUtil.yuvConvert(in, realWidth, realHeight, 4); +//// // 旋转90或180或270度 +//// yuvRotate(in, 0, realWidth, realHeight, 90); +// +// ByteBuffer tmp = ByteBuffer.allocateDirect(realWidth * realHeight * 3 / 2); +// tmp.clear(); +// tmp.put(in); - i420callback.onI420Data(tmp); + i420callback.onI420Data(null); - // 旋转90或270度,则宽高需要互换 - displayer.decoder_decodeBuffer(tmp, realWidth, realHeight); +// // 旋转90或270度,则宽高需要互换 +// displayer.decoder_decodeBuffer(tmp, realWidth, realHeight); } } @@ -1183,7 +1183,7 @@ public class EasyPlayerClient implements Client.SourceCallBack { } // Log.d(TAG,String.format("sleep:%d", newSleepUs/1000)); Thread.sleep(newSleepUs / 1000); - mCodec.releaseOutputBuffer(index, i420callback == null); + mCodec.releaseOutputBuffer(index, true); } if (firstTime) {