From 03648a40668bf12f7681c3d196d6effb1fe5f168 Mon Sep 17 00:00:00 2001
From: xufuji456 <839789740@qq.com>
Date: Wed, 13 Apr 2022 01:14:12 +0800
Subject: [PATCH] Feature: fit landscape and portrait preview
---
.../com/frank/live/camera/Camera2Helper.java | 28 +++++++++++++--
.../com/frank/live/stream/VideoStreamNew.java | 33 ++++++++++++++---
.../java/com/frank/live/util/YUVUtil.java | 36 +++++++++++++++----
app/src/main/AndroidManifest.xml | 11 +++---
4 files changed, 86 insertions(+), 22 deletions(-)
diff --git a/Live/src/main/java/com/frank/live/camera/Camera2Helper.java b/Live/src/main/java/com/frank/live/camera/Camera2Helper.java
index 77c7354..65b9a1f 100644
--- a/Live/src/main/java/com/frank/live/camera/Camera2Helper.java
+++ b/Live/src/main/java/com/frank/live/camera/Camera2Helper.java
@@ -36,6 +36,8 @@ import java.util.concurrent.locks.ReentrantLock;
import androidx.annotation.NonNull;
+import com.frank.live.util.YUVUtil;
+
/**
* Camera2: open, preview and close
* Created by frank on 2019/12/18.
@@ -67,11 +69,14 @@ public class Camera2Helper {
private Size mPreviewSize;
+ private int rotateDegree = 0;
+
private Camera2Helper(Builder builder) {
mTextureView = builder.previewDisplayView;
specificCameraId = builder.specificCameraId;
camera2Listener = builder.camera2Listener;
rotation = builder.rotation;
+ rotateDegree = builder.rotateDegree;
previewViewSize = builder.previewViewSize;
context = builder.context;
}
@@ -479,12 +484,13 @@ public class Camera2Helper {
private int rotation;
+ private int rotateDegree;
+
private Context context;
public Builder() {
}
-
public Builder previewOn(TextureView val) {
previewDisplayView = val;
return this;
@@ -500,6 +506,10 @@ public class Camera2Helper {
return this;
}
+ public Builder rotateDegree(int val) {
+ rotateDegree = val;
+ return this;
+ }
public Builder specificCameraId(String val) {
specificCameraId = val;
@@ -527,6 +537,7 @@ public class Camera2Helper {
private class OnImageAvailableListenerImpl implements ImageReader.OnImageAvailableListener {
private byte[] temp = null;
private byte[] yuvData = null;
+ private byte[] dstData = null;
private final ReentrantLock lock = new ReentrantLock();
@Override
@@ -569,9 +580,20 @@ public class Camera2Helper {
offset += len / 4;
}
- if (camera2Listener != null) {
- camera2Listener.onPreviewFrame(yuvData);
+ if (rotateDegree == 90) {
+ if (dstData == null) {
+ dstData = new byte[len * 3 / 2];
+ }
+ YUVUtil.YUV420pRotate90(dstData, yuvData, width, height);
+ if (camera2Listener != null) {
+ camera2Listener.onPreviewFrame(dstData);
+ }
+ } else {
+ if (camera2Listener != null) {
+ camera2Listener.onPreviewFrame(yuvData);
+ }
}
+
lock.unlock();
}
image.close();
diff --git a/Live/src/main/java/com/frank/live/stream/VideoStreamNew.java b/Live/src/main/java/com/frank/live/stream/VideoStreamNew.java
index d61ab3d..3ad6ba1 100644
--- a/Live/src/main/java/com/frank/live/stream/VideoStreamNew.java
+++ b/Live/src/main/java/com/frank/live/stream/VideoStreamNew.java
@@ -6,6 +6,7 @@ import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.util.Log;
import android.util.Size;
+import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.TextureView;
import android.view.View;
@@ -24,6 +25,7 @@ public class VideoStreamNew extends VideoStreamBase
private static final String TAG = VideoStreamNew.class.getSimpleName();
+ private int rotation = 0;
private boolean isLiving;
private final Context mContext;
private Camera2Helper camera2Helper;
@@ -47,9 +49,8 @@ public class VideoStreamNew extends VideoStreamBase
* start previewing
*/
private void startPreview() {
- int rotateDegree = 0;
if (mContext instanceof Activity) {
- rotateDegree = ((Activity) mContext).getWindowManager().getDefaultDisplay().getRotation();
+ rotation = ((Activity) mContext).getWindowManager().getDefaultDisplay().getRotation();
}
camera2Helper = new Camera2Helper.Builder()
.cameraListener(this)
@@ -57,7 +58,8 @@ public class VideoStreamNew extends VideoStreamBase
.context(mContext.getApplicationContext())
.previewOn(mTextureView)
.previewViewSize(new Point(mVideoParam.getWidth(), mVideoParam.getHeight()))
- .rotation(rotateDegree)
+ .rotation(rotation)
+ .rotateDegree(getPreviewDegree(rotation))
.build();
camera2Helper.start();
}
@@ -137,12 +139,33 @@ public class VideoStreamNew extends VideoStreamBase
}
}
+ private int getPreviewDegree(int rotation) {
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ return 90;
+ case Surface.ROTATION_90:
+ return 0;
+ case Surface.ROTATION_180:
+ return 270;
+ case Surface.ROTATION_270:
+ return 180;
+ default:
+ return -1;
+ }
+ }
+
@Override
public void onCameraOpened(Size previewSize, int displayOrientation) {
Log.i(TAG, "onCameraOpened previewSize=" + previewSize.toString());
if (mCallback != null && mVideoParam != null) {
- mCallback.onVideoCodecInfo(previewSize.getWidth(), previewSize.getHeight(),
- mVideoParam.getFrameRate(), mVideoParam.getBitRate());
+ int width = previewSize.getWidth();
+ int height = previewSize.getHeight();
+ if (getPreviewDegree(rotation) == 90 || getPreviewDegree(rotation) == 270) {
+ int temp = width;
+ width = height;
+ height = temp;
+ }
+ mCallback.onVideoCodecInfo(width, height, mVideoParam.getFrameRate(), mVideoParam.getBitRate());
}
}
diff --git a/Live/src/main/java/com/frank/live/util/YUVUtil.java b/Live/src/main/java/com/frank/live/util/YUVUtil.java
index eb30837..4786703 100644
--- a/Live/src/main/java/com/frank/live/util/YUVUtil.java
+++ b/Live/src/main/java/com/frank/live/util/YUVUtil.java
@@ -19,23 +19,22 @@ public class YUVUtil {
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
- a = (input[index] & 0xff000000) >> 24; // a is not used obviously
+ a = (input[index] & 0xff000000) >> 24;
R = (input[index] & 0xff0000) >> 16;
G = (input[index] & 0xff00) >> 8;
B = (input[index] & 0xff);
- // well known RGB to YUV algorithm
+ // RGB to YUV algorithm
Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
// NV21 has a plane of Y and interleaved planes of VU each sampled by a factor of 2
- // meaning for every 4 Y pixels there are 1 V and 1 U. Note the sampling is every other
- // pixel AND every other scanLine.
- yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
+ // meaning for every 4 Y pixels there are 1 V and 1 U.
+ yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : (Math.min(Y, 255)));
if (j % 2 == 0 && index % 2 == 0) {
- yuv420sp[uvIndex++] = (byte) ((V < 0) ? 0 : ((V > 255) ? 255 : V));
- yuv420sp[uvIndex++] = (byte) ((U < 0) ? 0 : ((U > 255) ? 255 : U));
+ yuv420sp[uvIndex++] = (byte) ((V < 0) ? 0 : (Math.min(V, 255)));
+ yuv420sp[uvIndex++] = (byte) ((U < 0) ? 0 : (Math.min(U, 255)));
}
index++;
@@ -44,4 +43,27 @@ public class YUVUtil {
return yuv420sp;
}
+ public static void YUV420pRotate90(byte[] dstData, byte[] data, int width, int height) {
+ int n = 0;
+ int wh = width * height;
+ //y
+ for (int j = 0; j < width; j++) {
+ for(int i = height - 1; i >= 0; i--) {
+ dstData[n++] = data[width * i + j];
+ }
+ }
+ //u
+ for (int i = 0; i < width / 2; i++) {
+ for (int j = 1; j <= height / 2; j++) {
+ dstData[n++] = data[wh + ((height/2 - j) * (width / 2) + i)];
+ }
+ }
+ //v
+ for(int i = 0; i < width / 2; i++) {
+ for(int j = 1; j <= height / 2; j++) {
+ dstData[n++] = data[wh + wh / 4 + ((height / 2 - j) * (width / 2) + i)];
+ }
+ }
+ }
+
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 172c5a6..44d5f8b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -38,16 +38,13 @@
+ android:configChanges="orientation|screenSize" />
-
-
-
+
+
+