提问记录
调试代码
从回答的问题中可以看到,核心的代码已基本都给出来。这里我们可以根据经验补全XML代码或继续问细节代码都行(当然也可以直接贴报错代码进行提问)。
调整后代码
xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextureView
android:id="@+id/textureview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/start"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="startRtmp"
android:text="开始推流"/>
<Button
android:id="@+id/stop"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="stopRtmp"
android:text="关闭推流"/>
<Button
android:id="@+id/take_photo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="takePhoto"
android:text="拍照"/>
</LinearLayout>
</RelativeLayout>
Activity代码
package runde.mark.demo;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaCodec;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* Created by 玉念聿辉.
* User: 吴明辉
* Date: 2023/5/4
* Time: 10:02
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Size videoSize;
private Size previewSize;
private CameraDevice cameraDevice;
private CameraCaptureSession cameraCaptureSession;
private CaptureRequest.Builder captureRequestBuilder;
private MediaRecorder mediaRecorder;
private TextureView textureView;
private Surface previewSurface;
private Surface recordSurface;
private ImageReader imageReader;
private Handler mHandler;
private boolean isTakePhoto = false;
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.camera);
Button start = findViewById(R.id.start);
start.setText("开始录制");
Button stop = findViewById(R.id.stop);
stop.setText("关闭录制");
HandlerThread mThreadHandler = new HandlerThread("CAMERA2");
mThreadHandler.start();
mHandler = new Handler(mThreadHandler.getLooper());
// 初始化 MediaRecorder
startRecordingVideo();
// 创建 ImageReader,并设置监听器
imageReader = ImageReader.newInstance(1920, 1080, ImageFormat.JPEG, 1);
imageReader.setOnImageAvailableListener(imageReader -> {
// 获取最新的图片
Image image = imageReader.acquireLatestImage();
// 将图片保存为文件
if (isTakePhoto) {
FileOutputStream output = null;
try {
// 创建保存图片的文件
File pictureFile = new File(getExternalFilesDir(null), System.currentTimeMillis() + ".jpg");
Log.e(TAG, "onImageAvailable 图片地址: " + pictureFile.getAbsolutePath());
output = new FileOutputStream(pictureFile);
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
output.write(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
isTakePhoto = false;
}
}
if (image != null) {
image.close();
}
}, mHandler);
// 设置监听器,并打开相机并启动预览
textureView = findViewById(R.id.textureview);
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int i, int i1) {
// 打开相机并启动预览
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
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) {
}
});
}
// 打开相机
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void openCamera() {
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
// 获取可用的摄像头列表
String[] cameraIds = cameraManager.getCameraIdList();
// 选择后置摄像头作为默认摄像头
String cameraId = null;
for (String id : cameraIds) {
CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(id);
if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING)
== CameraCharacteristics.LENS_FACING_BACK) {
cameraId = id;
break;
}
}
// 获取相机支持的预览尺寸和视频尺寸
CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);
StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
previewSize = streamConfigurationMap.getOutputSizes(SurfaceTexture.class)[0];
videoSize = chooseVideoSize(streamConfigurationMap.getOutputSizes(MediaRecorder.class));
// 打开相机并启动预览
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
cameraManager.openCamera(cameraId, new CameraDevice.StateCallback() {
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onOpened(CameraDevice cameraDevice) {
MainActivity.this.cameraDevice = cameraDevice;
try {
// 创建预览请求,以及对应的 Surface
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
previewSurface = new Surface(surfaceTexture);
captureRequestBuilder.addTarget(previewSurface);
// 创建录制请求,以及对应的 Surface
captureRequestBuilder.addTarget(recordSurface);
// 添加拍照 Surface
captureRequestBuilder.addTarget(imageReader.getSurface());
// 创建 CameraCaptureSession
cameraDevice.createCaptureSession(Arrays.asList(previewSurface, recordSurface, imageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
MainActivity.this.cameraCaptureSession = cameraCaptureSession;
try {
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(),
null, mHandler);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to start preview", e);
}
}
@Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Log.e(TAG, "Failed to create capture session");
}
}, mHandler);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to create capture request", e);
}
}
@Override
public void onDisconnected(CameraDevice cameraDevice) {
cameraDevice.close();
MainActivity.this.cameraDevice = null;
}
@Override
public void onError(CameraDevice cameraDevice, int error) {
cameraDevice.close();
MainActivity.this.cameraDevice = null;
}
}, mHandler);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to get camera id list", e);
}
}
// 选择视频尺寸
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private Size chooseVideoSize(Size[] sizes) {
for (Size size : sizes) {
if (size.getWidth() ==
size.getHeight() * 16 / 9 &&
size.getWidth() <= 1920) {
return size;
}
}
return sizes[0];
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void updateView() {
try {
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
captureRequestBuilder.addTarget(previewSurface);
// 创建录制请求,以及对应的 Surface
captureRequestBuilder.addTarget(recordSurface);
// 添加拍照 Surface
captureRequestBuilder.addTarget(imageReader.getSurface());
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, mHandler);
Log.e(TAG, "updateView");
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to start preview", e);
}
}
// 启动录制视频
private void startRecordingVideo() {
try {
// 配置 MediaRecorder
if (mediaRecorder == null) {
mediaRecorder = new MediaRecorder();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
recordSurface = MediaCodec.createPersistentInputSurface();
}
} else {
mediaRecorder.reset();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mediaRecorder.setInputSurface(recordSurface);
}
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
mediaRecorder.setOutputFile(getOutputFile());
mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);
mediaRecorder.setVideoFrameRate(30);
mediaRecorder.setVideoSize(1920, 1080);
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mediaRecorder.prepare();
mediaRecorder.start();
//再次录制时刷新预览
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && cameraCaptureSession != null) {
updateView();
}
} catch (IOException e) {
Log.e(TAG, "Failed to start recording video", e);
}
}
// 停止录制视频
private void stopRecordingVideo() {
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.reset();
}
}
// 获取输出文件路径
private String getOutputFile() {
File file = new File(this.getExternalFilesDir(null), System.currentTimeMillis() + ".ts");
Log.e(TAG, "getOutputFile 视频路径: " + file.getAbsolutePath());
return file.getAbsolutePath();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void startRtmp(View view) {
Log.e(TAG, "startRtmp: 开始录制");
startRecordingVideo();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void stopRtmp(View view) {
Log.e(TAG, "stopRtmp: 关闭录制");
stopRecordingVideo();
}
public void takePhoto(View view) {
Log.e(TAG, "takePhoto: 拍照");
isTakePhoto = true;
}
}
记得添加权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />