我正在尝试将Android Camera2的输出发送到预览Surface和从MediaCodec.createInputSurface()获得的Surface。但是,当我将这些表面传递给对CameraDevice.createCaptureSession的调用,然后尝试构建CaptureRequest时,我得到:
android.hardware.camera2.CameraAccessException:CAMERA_ERROR(3):submitRequestList-无法使用未配置的表面。
CaptureRequest构建逻辑(请参见下文)来自Flutter相机官方插件,当您使用MediaRecorder.getSurface()而不是MediaCodec.createInputSurface()时,可以正常工作。这表明尚未配置MediaCodec表面。我正在使用经过良好测试的开源RTMP代码(https://github.com/pedroSG94/rtmp-rtsp-stream-client-java)中的VideoEncoder类,该类可与旧的Camera API(即不是Camera2)一起使用。该类将因此初始化编解码器:
String type = CodecUtil.H264_MIME;
MediaCodecInfo encoder = chooseEncoder(type);
try {
if (encoder != null) {
codec = MediaCodec.createByCodecName(encoder.getName());
} else {
Log.e(TAG, "Valid encoder not found");
return false;
}
MediaFormat videoFormat;
//if you dont use mediacodec rotation you need swap width and height in rotation 90 or 270
// for correct encoding resolution
String resolution;
if (!hardwareRotation && (rotation == 90 || rotation == 270)) {
resolution = height + "x" + width;
videoFormat = MediaFormat.createVideoFormat(type, height, width);
} else {
resolution = width + "x" + height;
videoFormat = MediaFormat.createVideoFormat(type, width, height);
}
Log.i(TAG, "Prepare video info: " + this.formatVideoEncoder.name() + ", " + resolution);
videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
this.formatVideoEncoder.getFormatCodec());
videoFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);
videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, fps);
videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);
if (hardwareRotation) {
videoFormat.setInteger("rotation-degrees", rotation);
}
if (this.avcProfile > 0 && this.avcProfileLevel > 0) {
// MediaFormat.KEY_PROFILE, API > 21
videoFormat.setInteger("profile", this.avcProfile);
// MediaFormat.KEY_LEVEL, API > 23
videoFormat.setInteger("level", this.avcProfileLevel);
}
codec.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
inputSurface = codec.createInputSurface();
当我尝试构建捕获请求时失败的代码在CameraCaptureSession.StateCallback.onConfigured中,对build()的调用引发了异常:
createCaptureSession( CameraDevice.TEMPLATE_RECORD, successCallback, surfaceFromMediaCodec );
private void createCaptureSession(
int templateType, Runnable onSuccessCallback, Surface... surfaces)
throws CameraAccessException {
// Close any existing capture session.
closeCaptureSession();
// Create a new capture builder.
captureRequestBuilder = cameraDevice.createCaptureRequest(templateType);
// Build Flutter surface to render to
SurfaceTexture surfaceTexture = flutterTexture.surfaceTexture();
surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
Surface flutterSurface = new Surface(surfaceTexture);
captureRequestBuilder.addTarget(flutterSurface);
List<Surface> remainingSurfaces = Arrays.asList(surfaces);
if (templateType != CameraDevice.TEMPLATE_PREVIEW) {
// If it is not preview mode, add all surfaces as targets.
for (Surface surface : remainingSurfaces) {
captureRequestBuilder.addTarget(surface);
}
}
// Prepare the callback
CameraCaptureSession.StateCallback callback =
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
if (cameraDevice == null) {
dartMessenger.send(
DartMessenger.EventType.ERROR, "The camera was closed during configuration.");
return;
}
cameraCaptureSession = session;
captureRequestBuilder.set(
CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
if (onSuccessCallback != null) {
onSuccessCallback.run();
}
} catch (CameraAccessException | IllegalStateException | IllegalArgumentException e) {
Log.i( TAG, "exception building capture session " + e );
dartMessenger.send(DartMessenger.EventType.ERROR, e.getMessage());
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
dartMessenger.send(
DartMessenger.EventType.ERROR, "Failed to configure camera session.");
}
};
// Collect all surfaces we want to render to.
List<Surface> surfaceList = new ArrayList<>();
surfaceList.add(flutterSurface);
surfaceList.addAll(remainingSurfaces);
// Start the session
cameraDevice.createCaptureSession(surfaceList, callback, null);
}
如果删除MediaCodec inputSurface作为构建目标,它将起作用(但我不会将任何东西捕获到MediaCodec中)。我想念什么?顺便说一句,第二个代码摘录中有一些Flutter代码,但是没有证据表明在Flutter中嵌入是相关的。
回答我自己的问题。误导性的Exception消息“无法使用未配置的表面”使我无法接受这种气味。表面已配置。我以为我检查过尺寸,但是一个是720x480,另一个是480x720。交换后,它起作用了。