From b57348ad15a033dd7d4534827d2eeeb0243bc8ec Mon Sep 17 00:00:00 2001 From: xiaowusky Date: Wed, 27 Dec 2023 10:22:30 +0800 Subject: [PATCH] =?UTF-8?q?desc:=E6=96=B0=E6=8E=A8=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../safetywatcher/watcher/ui/HomeActivity.kt | 4 +- .../watcher/utils/RecordHelper.kt | 16 +- .../main/java/org/easydarwin/PushHelper.kt | 2 +- .../java/org/easydarwin/push/HWConsumer.java | 243 ++++++++++++++++++ 4 files changed, 253 insertions(+), 12 deletions(-) create mode 100644 library-rtsp/src/main/java/org/easydarwin/push/HWConsumer.java 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 8216adb..8fa7eba 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 @@ -110,14 +110,14 @@ class HomeActivity : NoOptionsActivity() { } private fun onCameraOpen() { - // 启动录制编码 - RecordHelper.startRecording() // 启动推送 PushHelper.opSwitch(true) PushHelper.startStream(true) // 开启GPIO GPIOUtils.openCamera() setForCamera() + // 启动录制编码 + RecordHelper.startRecording() } private fun onCameraClose() { 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 3385b27..5b127c6 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 @@ -2,20 +2,16 @@ package com.yinuo.safetywatcher.watcher.utils import android.graphics.Bitmap import android.view.TextureView -import com.yinuo.library.vlc.encoder.MediaCodecManager import org.easydarwin.TxtOverlay +import org.easydarwin.push.HWConsumer object RecordHelper { - private val codecManager: MediaCodecManager = MediaCodecManager.getInstance() private const val width = 1920 private const val height = 1080 private const val overlayOffset = 20 private var recording = false; - - init { - codecManager.initCodecManager(width, height, 0) - } + private var consumer: HWConsumer? = null fun onFrameAvailable(view: TextureView, nv12Data: ByteArray) { if (!recording) { @@ -44,20 +40,22 @@ object RecordHelper { } } } - codecManager.addFrameData(nv12Data) + consumer?.onVideo(nv12Data, 0) } fun startRecording() { if (!recording) { recording = true - codecManager.startMediaCodec() + consumer = HWConsumer() + consumer?.onVideoStart(width, height) } } fun stopRecording() { if (recording) { recording = false - codecManager.stopMediaCodec() + consumer?.onVideoStop() + consumer = null } } } diff --git a/library-push/src/main/java/org/easydarwin/PushHelper.kt b/library-push/src/main/java/org/easydarwin/PushHelper.kt index 66b06f9..e0302c7 100644 --- a/library-push/src/main/java/org/easydarwin/PushHelper.kt +++ b/library-push/src/main/java/org/easydarwin/PushHelper.kt @@ -98,7 +98,7 @@ object PushHelper { mPusher?.initPush(mApplicationContext, callback) mPusher?.setMediaInfo( if (hevc) Pusher.Codec.EASY_SDK_VIDEO_CODEC_H265 else Pusher.Codec.EASY_SDK_VIDEO_CODEC_H264, - 24, + 17, Pusher.Codec.EASY_SDK_AUDIO_CODEC_AAC, 1, 8000, diff --git a/library-rtsp/src/main/java/org/easydarwin/push/HWConsumer.java b/library-rtsp/src/main/java/org/easydarwin/push/HWConsumer.java new file mode 100644 index 0000000..951c5ec --- /dev/null +++ b/library-rtsp/src/main/java/org/easydarwin/push/HWConsumer.java @@ -0,0 +1,243 @@ +package org.easydarwin.push; + +import android.content.Context; +import android.media.MediaCodec; +import android.media.MediaCodecInfo; +import android.media.MediaFormat; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; + +import com.yinuo.library.vlc.encoder.AndroidMuxer; + +import org.easydarwin.PushHelper; +import org.easydarwin.muxer.EasyMuxer; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * Created by apple on 2017/5/13. + */ +public class HWConsumer extends Thread implements VideoConsumer { + private static final String TAG = "HWConsumer"; + private static final String MIME_TYPE = "video/hevc"; + private int mHeight; + private int mWidth; + private MediaCodec mMediaCodec; + private ByteBuffer[] inputBuffers; + private ByteBuffer[] outputBuffers; + private volatile boolean mVideoStarted; + private MediaFormat newFormat; + + private AndroidMuxer androidMuxer; + private int mTrackIndex = -1; + + public HWConsumer() { + } + + @Override + public void onVideoStart(int width, int height) { + newFormat = null; + this.mWidth = width; + this.mHeight = height; + startMediaCodec(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + 1) { + inputBuffers = outputBuffers = null; + } else { + inputBuffers = mMediaCodec.getInputBuffers(); + outputBuffers = mMediaCodec.getOutputBuffers(); + } + androidMuxer = new AndroidMuxer(); + start(); + mVideoStarted = true; + } + +// final int millisPerframe = 1000 / 17; +// long lastPush = 0; + + @Override + public int onVideo(byte[] nv12, int format) { + if (!mVideoStarted) return 0; + try { + byte[] data = nv12; +// if (lastPush == 0) { +// lastPush = System.currentTimeMillis(); +// } +// long time = System.currentTimeMillis() - lastPush; +// if (time >= 0) { +// time = millisPerframe - time; +// if (time > 0) Thread.sleep(time / 2); +// } + + + int bufferIndex = mMediaCodec.dequeueInputBuffer(0); + if (bufferIndex >= 0) { + Log.d(TAG, "onVideo: " + bufferIndex); + ByteBuffer buffer = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + buffer = mMediaCodec.getInputBuffer(bufferIndex); + } else { + buffer = inputBuffers[bufferIndex]; + } + buffer.clear(); + buffer.put(data); + buffer.clear(); + mMediaCodec.queueInputBuffer(bufferIndex, 0, data.length, System.nanoTime() / 1000, MediaCodec.BUFFER_FLAG_KEY_FRAME); + } +// if (time > 0) Thread.sleep(time / 2); +// lastPush = System.currentTimeMillis(); + } catch (Exception ex) { + ex.printStackTrace(); + } + return 0; + } + + + @Override + public void run() { + MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); + int outputBufferIndex = 0; + int NAL_I = 19; + int NAL_VPS = 32; + byte[] vps_sps_pps_buf = null; + do { + outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 10000); + if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { + // no output available yet + } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { + // not expected for an encoder + outputBuffers = mMediaCodec.getOutputBuffers(); + } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { + synchronized (HWConsumer.this) { + newFormat = mMediaCodec.getOutputFormat(); + if (androidMuxer != null) { + // should happen before receiving buffers, and should only happen once + mTrackIndex = androidMuxer.addTrack(newFormat); + } + } + } else if (outputBufferIndex < 0) { + // let's ignore it + } else { + ByteBuffer outputBuffer; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex); + } else { + outputBuffer = outputBuffers[outputBufferIndex]; + } + outputBuffer.position(bufferInfo.offset); + outputBuffer.limit(bufferInfo.offset + bufferInfo.size); + if (androidMuxer != null && mTrackIndex != -1) { + androidMuxer.writeSampleData(mTrackIndex, outputBuffer, bufferInfo); + } + byte[] outData = new byte[bufferInfo.size]; + outputBuffer.get(outData); + int offset = 4; + if (outData[2] == 0x01) { + offset = 3; + } + int type = (outData[offset] & 0x7E) >> 1; + Log.i("cyy", "type = " + type); + if (type == NAL_VPS) { + if (vps_sps_pps_buf == null) { + vps_sps_pps_buf = outData; + } + } else if (type == NAL_I) { + byte[] newBuf = new byte[vps_sps_pps_buf.length + outData.length]; + System.arraycopy(vps_sps_pps_buf, 0, newBuf, 0, vps_sps_pps_buf.length); + System.arraycopy(outData, 0, newBuf, vps_sps_pps_buf.length, outData.length); + outData = newBuf; + } + PushHelper.INSTANCE.pushData(outData, outData.length, bufferInfo.presentationTimeUs / 1000, 1); + mMediaCodec.releaseOutputBuffer(outputBufferIndex, false); + } + } + while (mVideoStarted); + } + + @Override + public void onVideoStop() { + do { + newFormat = null; + mVideoStarted = false; + try { + join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } while (isAlive()); + if (androidMuxer != null) { + androidMuxer.release(); + } + if (mMediaCodec != null) { + stopMediaCodec(); + mMediaCodec = null; + } + } + + @Override + public synchronized void setMuxer(EasyMuxer muxer) { + } + + + /** + * 初始化编码器 + */ + private void startMediaCodec() { + /* + SD (Low quality) SD (High quality) HD 720p +1 HD 1080p +1 +Video resolution 320 x 240 px 720 x 480 px 1280 x 720 px 1920 x 1080 px +Video frame rate 20 fps 30 fps 30 fps 30 fps +Video bitrate 384 Kbps 2 Mbps 4 Mbps 10 Mbps + */ + int framerate = 17; +// if (width == 640 || height == 640) { +// bitrate = 2000000; +// } else if (width == 1280 || height == 1280) { +// bitrate = 4000000; +// } else { +// bitrate = 2 * width * height; +// } + + int bitrate = (int) (mWidth * mHeight * 20 * 2 * 0.05f); + + if (mWidth >= 1920 || mHeight >= 1920) + bitrate *= 0.3; + else if (mWidth >= 1280 || mHeight >= 1280) + bitrate *= 0.4; + else if (mWidth >= 720 || mHeight >= 720) + bitrate *= 0.6; + + try { +// mMediaCodec = MediaCodec.createByCodecName("0MX.rk.video_encoder.avc"); + mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE); + } catch (IOException e) { + e.printStackTrace(); + throw new IllegalStateException(e); + } + + MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight); + mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate + 3000); + mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate); + mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar); + mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); + mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); + mMediaCodec.start(); + + Bundle params = new Bundle(); + params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + mMediaCodec.setParameters(params); + } + } + + /** + * 停止编码并释放编码资源占用 + */ + private void stopMediaCodec() { + mMediaCodec.stop(); + mMediaCodec.release(); + } +}