如何通过Camera2 API实现稳定恒定的25fps视频录制
Hey there, let's break down your issue step by step and figure out how to get that steady 25fps recording working reliably, plus clear up why your devices show different support.
First: Why Do P30 Pro and Pixel 3 XL Have Different Frame Rate Support?
The root cause is hardware and driver implementation differences across Android devices, tied to Camera2 API capability levels:
- P30 Pro likely falls into the
FULLorLEVEL_3Camera2 category. These devices support advanced control over capture parameters, including fixed frame rates like 25fps for specific resolutions. - Pixel 3 XL might only support
LIMITEDcapabilities for your target resolution, or its camera sensor/ISP (Image Signal Processor) doesn't natively output 25fps. Some hardware is optimized for common rates like 30/60fps and doesn't include 25fps in its supported ranges.
To confirm this, you can check the device's supported frame rates programmatically (more on that later).
Fix 1: Validate Device Support for 25fps First
Before setting any parameters, confirm your target resolution actually supports a fixed 25fps range. Use this code snippet to check:
private boolean is25FpsSupported(String cameraId, Size videoSize) throws CameraAccessException { CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (map == null) return false; // Check standard video frame rate ranges Range<Integer>[] fpsRanges = map.getOutputFpsRanges(videoSize); for (Range<Integer> range : fpsRanges) { if (range.getLower() == 25 && range.getUpper() == 25) { return true; } } // Check high-speed video ranges (if applicable) Range<Integer>[] highSpeedRanges = map.getHighSpeedVideoFpsRangesFor(videoSize); for (Range<Integer> range : highSpeedRanges) { if (range.getLower() == 25 && range.getUpper() == 25) { return true; } } return false; }
If this returns false for a device (like Pixel 3 XL), hardware-level 25fps isn't possible—you'll either need to adjust your target rate or use software encoding (which impacts performance).
Fix 2: Correct MediaRecorder Parameter Configuration
Your current setup has a few gaps that might be causing frame rate drift:
Parameter order matters: Ensure you set frame rate-related values in this sequence:
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mMediaRecorder.setOutputFile(mNextVideoAbsolutePath); mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight()); mMediaRecorder.setVideoEncodingBitRate(10000000); mMediaRecorder.setVideoFrameRate(25); // Target encoder frame rate mMediaRecorder.setCaptureRate(25.0); // Critical: Matches camera capture rate to encoder mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); mMediaRecorder.prepare();Use the
doubleoverload ofsetCaptureRate()for more precision than the integer version.Ensure resolution matches 25fps support: Some devices support 25fps at 1080p but not at 4K. Double-check that your
mVideoSizeis a resolution the device supports for 25fps.
Fix 3: Proper Camera2 CaptureRequest & Session Setup
Your existing Camera2 code is missing a key step: adding the MediaRecorder's surface to the capture session. Here's the corrected flow:
// After setting up MediaRecorder Surface recorderSurface = mMediaRecorder.getSurface(); SurfaceTexture texture = mTextureView.getSurfaceTexture(); assert texture != null; texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); Surface previewSurface = new Surface(texture); // Build capture request mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); if (is25FpsSupported(mCameraId, mVideoSize)) { mPreviewBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<>(25, 25)); } mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); mPreviewBuilder.addTarget(previewSurface); mPreviewBuilder.addTarget(recorderSurface); // Critical: Send camera frames to MediaRecorder // Create capture session mCameraDevice.createCaptureSession(Arrays.asList(previewSurface, recorderSurface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { mCaptureSession = session; try { // Start repeating preview first session.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler); // Wait a moment for preview to stabilize, then start recording mBackgroundHandler.postDelayed(() -> mMediaRecorder.start(), 500); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { // Handle configuration failure } }, mBackgroundHandler);
- Adding the MediaRecorder surface to the capture session ensures the camera sends frames directly to the recorder.
- Adding a short delay before starting
mMediaRecorderhelps avoid initial frame rate fluctuations as the camera's AE/AF stabilizes.
Debugging Tips
- Use
CameraCaptureSession.CaptureCallbackto log frame timestamps and calculate actual capture rate, to confirm if the issue is with the camera output or MediaRecorder encoding. - On debug builds, run this adb command to view all supported resolutions and frame rates:
adb shell dumpsys camera | grep -A 15 "Stream configurations"
内容的提问来源于stack exchange,提问作者0stack

