安卓实现视频录制与显示和翻转摄像头

权限:

复制代码
<!-- 相机权限 -->
<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;
        }
    }
}

运行效果:

相关推荐
无极程序员1 小时前
PHP常量
android·ide·android studio
萌面小侠Plus2 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机
慢慢成长的码农2 小时前
Android Profiler 内存分析
android
大风起兮云飞扬丶2 小时前
Android——多线程、线程通信、handler机制
android
L72562 小时前
Android的Handler
android
清风徐来辽2 小时前
Android HandlerThread 基础
android
HerayChen3 小时前
HbuildderX运行到手机或模拟器的Android App基座识别不到设备 mac
android·macos·智能手机
顾北川_野3 小时前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
hairenjing11233 小时前
在 Android 手机上从SD 卡恢复数据的 6 个有效应用程序
android·人工智能·windows·macos·智能手机
小黄人软件4 小时前
android浏览器源码 可输入地址或关键词搜索 android studio 2024 可开发可改地址
android·ide·android studio