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 bc1740c..4df4f0b 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
@@ -1,11 +1,10 @@
package com.yinuo.safetywatcher.watcher.ui
import android.content.Intent
-import android.opengl.GLSurfaceView
import android.os.Build
import android.view.View
import androidx.annotation.RequiresApi
-import com.yinuo.library.vlc.RtspSurfaceRender
+import com.yinuo.library.vlc.RtspSurfaceRender2
import com.yinuo.safetywatcher.databinding.ActivityHomeBinding
import com.yinuo.safetywatcher.watcher.base.NoOptionsActivity
import com.yinuo.safetywatcher.watcher.constant.CAMERA_URL
@@ -20,6 +19,8 @@ class HomeActivity : NoOptionsActivity() {
ActivityHomeBinding.inflate(layoutInflater)
}
+ private var mRender: RtspSurfaceRender2? = null
+
override fun getTopBarTitle(): String? {
return null;
}
@@ -46,7 +47,9 @@ class HomeActivity : NoOptionsActivity() {
itemRecovery.setOnClickListener {
mBinding.tvWarn.visibility = View.GONE
}
- cameraSwitch.setOnCheckedChangeListener { buttonView, isChecked -> }
+ cameraSwitch.setOnCheckedChangeListener { buttonView, isChecked ->
+ if (isChecked) mRender?.startRecording() else mRender?.stopRecording()
+ }
itemSetting.post {
itemSetting.requestFocus()
@@ -55,11 +58,14 @@ class HomeActivity : NoOptionsActivity() {
}
private fun setForCamera() {
- mBinding.surface.setEGLContextClientVersion(3);
- val mRender = RtspSurfaceRender(mBinding.surface)
- mRender.setRtspUrl(CAMERA_URL)
- mBinding.surface.setRenderer(mRender)
- mBinding.surface.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
+// mBinding.surface.setEGLContextClientVersion(3);
+// val mRender = RtspSurfaceRender(mBinding.surface)
+// mRender.setRtspUrl(CAMERA_URL)
+// mBinding.surface.setRenderer(mRender)
+// mBinding.surface.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
+
+ mRender = RtspSurfaceRender2(mBinding.surface2)
+ mRender?.setRtspUrl(CAMERA_URL)
}
private fun initTopbarHelper() {
@@ -78,11 +84,11 @@ class HomeActivity : NoOptionsActivity() {
override fun onResume() {
super.onResume()
- mBinding.surface.onResume()
+// mBinding.surface.onResume()
}
override fun onStop() {
super.onStop()
- mBinding.surface.onPause()
+// mBinding.surface.onPause()
}
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml
index 897d0fa..afbba43 100644
--- a/app/src/main/res/layout/activity_home.xml
+++ b/app/src/main/res/layout/activity_home.xml
@@ -6,6 +6,12 @@
+
+
{
+ if (!mVideoEncoder.isRecording()) {
+ File output = CameraHelper.getOutputMediaFile(CameraHelper.MEDIA_TYPE_VIDEO, "");
+ LogUtils.v(String.format("startRecording: %s", output));
+ mVideoEncoder.startRecording(new BaseMovieEncoder.EncoderConfig(output, EGL14.eglGetCurrentContext()));
+ }
+ });
+ }
+
+ public void stopRecording() {
+ mSurfaceView.post(() -> {
+ if (mVideoEncoder.isRecording()) {
+ mVideoEncoder.stopRecording();
+ }
+ });
+ }
+
+ public void onSurfaceDestoryed() {
+ RtspHelper.getInstance().releasePlayer();
+ }
+
+ @Override
+ public void onPreviewFrame(final ByteBuffer buffer, int width, int height) {
+ ByteBuffer newBuffer = null;
+ synchronized (videoBitmap) {
+ overLayBitmap = overlay.javaOverlayBm("1111111@2222222@333333");
+ videoBitmap.copyPixelsFromBuffer(buffer.position(0));
+ videoBitmap = mergeBitmap(videoBitmap, overLayBitmap);
+ newBuffer = ByteBuffer.allocateDirect(videoBitmap.getByteCount()).order(ByteOrder.nativeOrder());
+ videoBitmap.copyPixelsToBuffer(newBuffer);
+ }
+
+ ByteBuffer finalNewBuffer = newBuffer;
+ mSurfaceView.post(() -> {
+ if (finalNewBuffer != null) {
+ mVideoEncoder.frameAvailable(finalNewBuffer.array(), System.nanoTime());
+ } else {
+ mVideoEncoder.frameAvailable(buffer.array(), System.nanoTime());
+ }
+ });
+ }
+
+
+ /**
+ * 把两个位图覆盖合成为一个位图,以底层位图的长宽为基准
+ *
+ * @param backBitmap 在底部的位图
+ * @param frontBitmap 盖在上面的位图
+ * @return
+ */
+ public static Bitmap mergeBitmap(Bitmap backBitmap, Bitmap frontBitmap) {
+
+ if (backBitmap == null || backBitmap.isRecycled()
+ || frontBitmap == null || frontBitmap.isRecycled()) {
+ return backBitmap;
+ }
+ //create the new blank bitmap 创建一个新的和SRC长度宽度一样的位图
+ Bitmap newbmp = Bitmap.createBitmap(backBitmap.getWidth(), backBitmap.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas cv = new Canvas(newbmp);
+ //draw bg into
+ cv.drawBitmap(backBitmap, 0, 0, null);//在 0,0坐标开始画入bg
+ //draw fg into
+ cv.drawBitmap(frontBitmap, 100, 100, null);//在 0,0坐标开始画入fg ,可以从任意位置画入
+ //save all clip
+ cv.save();//保存
+ //store
+ cv.restore();//存储
+ return newbmp;
+ }
+}
diff --git a/library-vlc/src/main/java/com/yinuo/library/vlc/TxtOverlay.java b/library-vlc/src/main/java/com/yinuo/library/vlc/TxtOverlay.java
new file mode 100644
index 0000000..f3135ae
--- /dev/null
+++ b/library-vlc/src/main/java/com/yinuo/library/vlc/TxtOverlay.java
@@ -0,0 +1,44 @@
+package com.yinuo.library.vlc;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.text.TextUtils;
+
+import org.easydarwin.util.YUVUtils;
+
+import java.text.SimpleDateFormat;
+
+/**
+ * Created by John on 2017/2/23.
+ */
+
+public class TxtOverlay {
+
+ String mTip = "";
+ long lastTipUpdateTime = 0;
+ Bitmap bmp;
+
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
+
+ public TxtOverlay() {
+ }
+
+ public Bitmap javaOverlayBm(String txt) {
+ if (TextUtils.isEmpty(txt)) {
+ return null;
+ }
+ long currentTimeMillis = System.currentTimeMillis();
+ // 限制获取bitmap的频率,保证性能
+ if (TextUtils.isEmpty(mTip) || (!txt.equals(mTip) || currentTimeMillis - lastTipUpdateTime > 1000)) {
+ // 记录更新时间和上一次的文字
+ lastTipUpdateTime = currentTimeMillis;
+ mTip = txt;
+ // 文字转bitmap
+ bmp = YUVUtils.generateBitmap(dateFormat.format(lastTipUpdateTime) + "@" + txt, 40, Color.WHITE);
+ // 缩放旋转bitmap
+// bmp = YUVUtils.rotateImage(bmp, 0);
+ }
+ return bmp;
+ }
+
+}
diff --git a/library-vlc/src/main/java/com/yinuo/library/vlc/encoder/CameraHelper.java b/library-vlc/src/main/java/com/yinuo/library/vlc/encoder/CameraHelper.java
new file mode 100644
index 0000000..81f1fd1
--- /dev/null
+++ b/library-vlc/src/main/java/com/yinuo/library/vlc/encoder/CameraHelper.java
@@ -0,0 +1,214 @@
+package com.yinuo.library.vlc.encoder;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.hardware.Camera;
+import android.os.Build;
+import android.os.Environment;
+import android.view.Surface;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Created by liwentian on 2017/8/29.
+ */
+
+public class CameraHelper {
+
+ public static final int MEDIA_TYPE_IMAGE = 1;
+ public static final int MEDIA_TYPE_VIDEO = 2;
+
+ public static int getFrontCameraId() {
+ int frontIdx = 0;
+ Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
+ for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
+ Camera.getCameraInfo(i, cameraInfo);
+
+ if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+ frontIdx = i;
+ break;
+ }
+ }
+ return frontIdx;
+ }
+
+ public static int getDisplayOrientation(Activity activity, int cameraId) {
+ Camera.CameraInfo info = new Camera.CameraInfo();
+ Camera.getCameraInfo(cameraId, info);
+ int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
+
+ int degrees = 0;
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ degrees = 0;
+ break;
+ case Surface.ROTATION_90:
+ degrees = 90;
+ break;
+ case Surface.ROTATION_180:
+ degrees = 180;
+ break;
+ case Surface.ROTATION_270:
+ degrees = 270;
+ break;
+ }
+ int result;
+ if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+ result = (info.orientation + degrees) % 360;
+ result = (360 - result) % 360; // compensate the mirror
+ } else {
+ // back-facing
+ result = (info.orientation - degrees + 360) % 360;
+ }
+
+ return result;
+ }
+
+ /**
+ * Iterate over supported camera video sizes to see which one best fits the
+ * dimensions of the given view while maintaining the aspect ratio. If none can,
+ * be lenient with the aspect ratio.
+ *
+ * @param supportedVideoSizes Supported camera video sizes.
+ * @param previewSizes Supported camera preview sizes.
+ * @param w The width of the view.
+ * @param h The height of the view.
+ * @return Best match camera video size to fit in the view.
+ */
+ public static Camera.Size getOptimalVideoSize(List supportedVideoSizes,
+ List previewSizes, int w, int h) {
+ // Use a very small tolerance because we want an exact match.
+ final double ASPECT_TOLERANCE = 0.1;
+ double targetRatio = (double) w / h;
+
+ // Supported video sizes list might be null, it means that we are allowed to use the preview
+ // sizes
+ List videoSizes;
+ if (supportedVideoSizes != null) {
+ videoSizes = supportedVideoSizes;
+ } else {
+ videoSizes = previewSizes;
+ }
+ Camera.Size optimalSize = null;
+
+ // Start with max value and refine as we iterate over available video sizes. This is the
+ // minimum difference between view and camera height.
+ double minDiff = Double.MAX_VALUE;
+
+ // Target view height
+ int targetHeight = h;
+
+ // Try to find a video size that matches aspect ratio and the target view size.
+ // Iterate over all available sizes and pick the largest size that can fit in the view and
+ // still maintain the aspect ratio.
+ for (Camera.Size size : videoSizes) {
+ double ratio = (double) size.width / size.height;
+ if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
+ continue;
+ if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
+ optimalSize = size;
+ minDiff = Math.abs(size.height - targetHeight);
+ }
+ }
+
+ // Cannot find video size that matches the aspect ratio, ignore the requirement
+ if (optimalSize == null) {
+ minDiff = Double.MAX_VALUE;
+ for (Camera.Size size : videoSizes) {
+ if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
+ optimalSize = size;
+ minDiff = Math.abs(size.height - targetHeight);
+ }
+ }
+ }
+ return optimalSize;
+ }
+
+ /**
+ * @return the default camera on the device. Return null if there is no camera on the device.
+ */
+ public static Camera getDefaultCameraInstance() {
+ int front = getFrontCameraId();
+ return Camera.open(front);
+ }
+
+
+ /**
+ * @return the default rear/back facing camera on the device. Returns null if camera is not
+ * available.
+ */
+ public static Camera getDefaultBackFacingCameraInstance() {
+ return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
+ }
+
+ /**
+ * @return the default front facing camera on the device. Returns null if camera is not
+ * available.
+ */
+ public static Camera getDefaultFrontFacingCameraInstance() {
+ return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_FRONT);
+ }
+
+
+ /**
+ * @param position Physical position of the camera i.e Camera.CameraInfo.CAMERA_FACING_FRONT
+ * or Camera.CameraInfo.CAMERA_FACING_BACK.
+ * @return the default camera on the device. Returns null if camera is not available.
+ */
+ @TargetApi(Build.VERSION_CODES.GINGERBREAD)
+ private static Camera getDefaultCamera(int position) {
+ // Find the total number of cameras available
+ int mNumberOfCameras = Camera.getNumberOfCameras();
+
+ // Find the ID of the back-facing ("default") camera
+ Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
+ for (int i = 0; i < mNumberOfCameras; i++) {
+ Camera.getCameraInfo(i, cameraInfo);
+ if (cameraInfo.facing == position) {
+ return Camera.open(i);
+
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a media file in the {@code Environment.DIRECTORY_PICTURES} directory. The directory
+ * is persistent and available to other applications like gallery.
+ *
+ * @param type Media type. Can be video or image.
+ * @return A file object pointing to the newly created file.
+ */
+ public static File getOutputMediaFile(int type, String name) {
+ // To be safe, you should check that the SDCard is mounted
+ // using Environment.getExternalStorageState() before doing this.
+ if (!Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
+ return null;
+ }
+
+ File mediaStorageDir = Environment.getExternalStoragePublicDirectory("video");
+ if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()) {
+ return null;
+ }
+
+ // Create a media file name
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date());
+ File mediaFile;
+ if (type == MEDIA_TYPE_IMAGE) {
+ mediaFile = new File(mediaStorageDir.getPath() + File.separator +
+ "IMG_" + timeStamp + ".jpg");
+ } else if (type == MEDIA_TYPE_VIDEO) {
+ mediaFile = new File(mediaStorageDir.getPath() + File.separator +
+ "VID_" + timeStamp + "_" + name + ".mp4");
+ } else {
+ return null;
+ }
+
+ return mediaFile;
+ }
+}