要在Android Camera2中减少视频块/片段之间的延迟,可以使用预览回调和视频录制回调之间的双缓冲机制。以下是一个示例代码,演示如何实现此功能:
import android.Manifest;
import android.content.pm.PackageManager;
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.TotalCaptureResult;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Camera2VideoDelay";
private static final int REQUEST_CAMERA_PERMISSION_RESULT = 0;
private static final int REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION_RESULT = 1;
private Button mRecordButton;
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private CameraManager mCameraManager;
private CameraDevice mCameraDevice;
private String mCameraId;
private Size mPreviewSize;
private CaptureRequest.Builder mCaptureRequestBuilder;
private CameraCaptureSession mPreviewCaptureSession;
private HandlerThread mBackgroundHandlerThread;
private Handler mBackgroundHandler;
private File mVideoFolder;
private String mVideoFileName;
private ImageReader mImageReader;
private boolean mIsRecording = false;
private final CameraDevice.StateCallback mCameraDeviceStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
mCameraDevice = cameraDevice;
startPreview();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
cameraDevice.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int i) {
cameraDevice.close();
mCameraDevice = null;
}
};
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader imageReader) {
mBackgroundHandler.post(new ImageSaver(imageReader.acquireLatestImage()));
}
};
private final CameraCaptureSession.StateCallback mPreviewCaptureSessionStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
mPreviewCaptureSession = cameraCaptureSession;
try {
mPreviewCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(), null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
Toast.makeText(MainActivity.this, "Unable to setup camera preview", Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSurfaceView = findViewById(R.id.surfaceView);
mRecordButton = findViewById(R.id.recordButton);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
setupCamera();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
closeCamera();
}
});
mRecordButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mIsRecording) {
mIsRecording = false;
mRecordButton.setText("Record");
} else {
checkWriteStoragePermission();
}
}
});
createVideoFolder();
}
@Override
protected void onResume() {
super.onResume();
startBackgroundThread();
if (mSurfaceHolder.getSurface() != null) {