权限:
<!-- 相机权限 -->
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- 录音权限(包括麦克风权限) -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
layout.xml:
XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextureView
android:id="@+id/textureView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/switchCameraButton"/>
<Button
android:id="@+id/switchCameraButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="翻转摄像头"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginBottom="16dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Activity:
java
package com.xmkjsoft.video_call;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.view.Surface;
import android.view.TextureView;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.webrtc.MediaStream;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
public class MainActivity extends AppCompatActivity {
private MyWebSocketClient webSocketClient;
private static final int PERMISSION_REQUEST_CAMERA = 1;
private static final int PERMISSION_REQUEST_RECORD_AUDIO = 2;
private static final int REQUEST_CAMERA_PERMISSION = 200;
private TextureView textureView;
private CameraDevice cameraDevice;
private CaptureRequest.Builder captureRequestBuilder;
private CameraCaptureSession cameraCaptureSession;
private String cameraId;
private MediaRecorder mediaRecorder;
// 储存视频的文件路径
private static final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath() + "/video.mp4";
// 默认无参数构造函数
public MainActivity() {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 检查并请求相机权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
PERMISSION_REQUEST_CAMERA);
}
// 检查并请求录音权限(包括麦克风权限)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.RECORD_AUDIO},
PERMISSION_REQUEST_RECORD_AUDIO);
}
// 设置按钮点击事件监听器
Button switchCameraButton = findViewById(R.id.switchCameraButton);
switchCameraButton.setOnClickListener(v -> {
if (cameraDevice != null) {
closeCamera();
// 切换摄像头
cameraId = (cameraId.equals("0")) ? "1" : "0"; // 更新 cameraId
openCamera(); // 重新打开摄像头
}
});
// 获取本地视频流
textureView = findViewById(R.id.textureView);
textureView.setSurfaceTextureListener(textureListener);
// 初始化 MediaRecorder
mediaRecorder = new MediaRecorder();
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mediaRecorder.setOutputFile(VIDEO_FILE_PATH);
connectWebSocket();
}
private void connectWebSocket() {
try {
webSocketClient = new MyWebSocketClient("ws://192.168.28.218/ws/1233");
webSocketClient.connect();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
// 内部类,用于处理 WebSocket 连接状态和消息
private class MyWebSocketClient extends WebSocketClient {
public MyWebSocketClient(String serverUri) throws URISyntaxException {
super(new java.net.URI(serverUri));
}
@Override
public void onOpen(ServerHandshake handshakedata) {
// WebSocket 连接已打开
System.out.println("WebSocket 连接已打开");
}
@Override
public void onMessage(String message) {
// 收到文本消息
System.out.println("收到文本消息:" + message);
}
@Override
public void onClose(int code, String reason, boolean remote) {
// WebSocket 连接已关闭
System.out.println("WebSocket 连接已关闭,code:" + code + ", reason:" + reason + ", remote:" + remote);
}
@Override
public void onError(Exception ex) {
// WebSocket 连接出错
System.out.println("WebSocket 连接出错:" + ex.getMessage());
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CAMERA) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 相机权限已授予
System.out.println("相机权限已授予");
} else {
// 相机权限被拒绝
System.out.println("相机权限被拒绝");
}
} else if (requestCode == PERMISSION_REQUEST_RECORD_AUDIO) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 录音权限已授予
System.out.println("录音权限已授予");
} else {
// 录音权限被拒绝
System.out.println("录音权限被拒绝");
}
}
}
private TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int i, int i1) {
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surfaceTexture, int i, int i1) {
}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
return false;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) {
}
};
// 打开相机
private void openCamera() {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
// 检查 cameraId 是否为空
if (cameraId == null) {
// 如果为空,选择默认摄像头
cameraId = manager.getCameraIdList()[0];
}
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
return;
}
manager.openCamera(cameraId, stateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
cameraDevice = camera;
createCameraPreview();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
cameraDevice.close();
}
@Override
public void onError(@NonNull CameraDevice camera, int i) {
cameraDevice.close();
cameraDevice = null;
}
};
private void createCameraPreview() {
try {
SurfaceTexture texture = textureView.getSurfaceTexture();
texture.setDefaultBufferSize(1920, 1080);
Surface surface = new Surface(texture);
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(surface);
cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
if (cameraDevice == null) {
return;
}
MainActivity.this.cameraCaptureSession = cameraCaptureSession;
try {
// 开始预览
MainActivity.this.cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
// 添加 MediaRecorder 的 Surface
if (mediaRecorder != null) {
captureRequestBuilder.addTarget(mediaRecorder.getSurface());
// 开始录制视频
mediaRecorder.start();
}
} catch (CameraAccessException | IllegalStateException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
Toast.makeText(MainActivity.this, "Failed", Toast.LENGTH_SHORT).show();
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
// 在 onStop() 方法中停止录制视频
@Override
protected void onStop() {
super.onStop();
stopRecording();
}
private void stopRecording() {
if (mediaRecorder != null) {
try {
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
} catch (RuntimeException e) {
e.printStackTrace();
}
}
}
// 关闭相机
private void closeCamera() {
if (cameraCaptureSession != null) {
cameraCaptureSession.close();
cameraCaptureSession = null;
}
if (cameraDevice != null) {
cameraDevice.close();
cameraDevice = null;
}
}
}
运行效果: