SCameraCaptureSessionクラスを使用してビデオをキャプチャしようとしています。このクラスの関数-setRepeatingRequest(これは here について説明しています)を使用しているときに、次のエラーが発生します。
Java.lang.IllegalArgumentException:CaptureRequestに未構成の入力/出力サーフェスが含まれています!
私が気づいたように、問題はMediaRecorderのSurfaceオブジェクトに何かがあるために発生しています。ただし、Android 8より古いバージョンを使用している場合は正常に動作し、クラッシュはAndroid 8.を実行しているSamsungデバイスでのみ発生します。そのクラッシュ、それで私はそれがかなり新しいと思います...
誰かが何か情報を持っていますか?私が言及したように、MediaRecorderの表面をデバイスで正常に機能させるにはどうすればよいですか?
重要な注意:ビデオのキャプチャは機能します素晴らしいAndroid 8より前のバージョン!!!
MediaRecorderからのサーフェス構成に問題があるようです。カスタムの永続的なサーフェスを渡すと、機能するはずです。
MediaCodec.createPersistentInputSurface()を呼び出して、サーフェスをインスタンス化します
MediaRecorder.setInputSurface(yourSurface);を使用して渡します。
このサーフェスの使用を停止した後、yourSurface.release()を呼び出します。
注:この方法を使用する場合は、mediaRecorder.getSurface()を使用しないでください。
参照:
MediaRecorder: MediaRecorder-Android Docs
MediaCodec: MediaCodec-Android Docs
同じ例外があり、私は私のケースを解決しました。私のケースの根本的な原因は、TextureViewのSurfaceを再作成したことです。サーフェスを再作成しないように変更すると、例外がなくなりました。
私のコードもAndroid 8.0以前はうまく機能しています。
私の初期化カメラは次のようなものです。
CameraDevice mCameraDevice;
CameraCaptureSession mCameraCaptureSession;
CaptureRequest mCaptureRequest;
Surface mTextureViewSurface;
public void updateCameraState(boolean run) {
if (run) {
if (mTextureView == null || !mTextureView.isAvailable()) {
// wait until mTextureView is available
// then call updateCameraState() again via SurfaceTextureListener
return;
}
if (mCameraDevice == null) {
// open camera and wait until mCameraDevice is obtained.
// then call updateCameraState() again via CameraDevice.StateCallback
mCameraManager.openCamera(...);
return;
}
if (mCameraCaptureSession == null) {
// createCaptureSession and wait until mCameraCaptureSession is obtained.
// then call updateCameraState() again via CameraCaptureSession.StateCallback
mTextureViewSurface = new Surface(texture);
List<Surface> surfaces = Arrays.asList(mTextureViewSurface, mImageReader.getSurface());
mCameraDevice.createCaptureSession(surfaces, mSessionStateCallback, sHandler);
return;
}
if (mCaptureRequest == null) {
CaptureRequest.Builder builder = mCameraCaptureSession.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
/* Put some values into builder */
// *************************************************************************
// POINT: In my old code, It re-create Surface
// *************************************************************************
// Surface surface = new Surface(texture);
// builder.addTarget(surface);
builder.addTarget(mTextureViewSurface);
mCameraCaptureSession.setRepeatingRequest(builder.build(), mCaptureCallback, sHandler);
}
// fin
} else {
if (mCaptureRequest != null) {
mCaptureRequest = null;
}
// *************************************************************************
// POINT: I do not know release() is needed. But I add it here.
// *************************************************************************
if (mTextureViewSurface != null) {
mTextureViewSurface.release();
mTextureViewSurface = null;
}
if (mCameraCaptureSession != null) {
mCameraCaptureSession.close();
mCameraCaptureSession = null;
}
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
}
}
同じ症状でした。 'SurfaceView'クラスを使用して解決しましたが、使用しませんでした。Android.hardware.camera2Android.hardware.cameraを使用しています
コードの解決された部分。
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
// encoding data
encoding(data) ;
}
/**
* byte data encoding
* @param data
*/
private void encoding (byte[] data) {
// api 21 미만에 대해서 필요
ByteBuffer[] inputBuffers = this.mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = this.mediaCodec.getOutputBuffers();
int inputBufferIndex = this.mediaCodec.dequeueInputBuffer(TIMEOUT_USEC/* wait time, nagative value is infinite */);
// data write 가능 할 경우
if (inputBufferIndex >= 0) {
// data null (마지막 데이터)
int length = 0, flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
if (data != null) {
ByteBuffer inputBuffer = null;
if (CameraUtils.isCamera2()) inputBuffer = this.mediaCodec.getInputBuffer(inputBufferIndex);
else inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(data);
length = data.length;
flags = 0;
}
/*
- index : dequeueInputBuffer 에서 return 받은 index 번호를 넣습니다.
- offset : 항상 0이겠지만 Buffer에 채워넣은 데이터의 시작 점을 지정할 수 있습니다.
- size : Buffer에 채워넣은 데이터 사이즈 정보
- presentationTimeUs : 디코딩의 경우 Play 할 데이터의 시간(마이크로 초)
- flags : 읽은 버퍼의 정보가 설정값인지 BUFFER_FLAG_CODEC_CONFIG, 마지막 데이터인지BUFFER_FLAG_END_OF_STREAM에 대한 정보를 초기화 할 수 있습니다.
대부분은 0을 채워넣고 마지막 데이터를 알리기 위해서는 BUFFER_FLAGS_END_OF_STREAM을 넣습니다.
*/
this.mediaCodec.queueInputBuffer(inputBufferIndex, 0, length, computePresentationTimeNsec(), flags);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = this.mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC/* wait time, nagative value is infinite */);
switch (outputBufferIndex) {
/*
MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED
- Buffer 정보가 1번 변경되게 됩니다.
- API 21인 Lollipop 부터는 이 @deprecated 되었기에 불필요하지만 이전 API에서는 꼭 필요한 정보입니다. 이게 호출되면 처음에 생성한 ByteBuffer[] 배열의 변화가 일어나게 됩니다.
*/
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.i(TAG, "INFO_OUTPUT_BUFFERS_CHANGED");
outputBuffers = this.mediaCodec.getOutputBuffers();
break;
/*MediaCodec.INFO_OUTPUT_FORMAT_CHANGED
- 처음에 생성하였든 MediaFormat을 기억하시는지요. 그 MediaFormat이 변경된 정보를 알려주게됩니다.
- 이 경우는 Encoder에서만 주로 사용하고, 디코더에서는 사용할 일은 없습니다.
*/
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
if (this.isMuxerStart) throw new RuntimeException("Format changed twice");
Log.d(TAG, "INFO_OUTPUT_FORMAT_CHANGED format : " + this.mediaCodec.getOutputFormat());
this.trackId = this.mediaMuxer.addTrack(this.mediaCodec.getOutputFormat());
this.mediaMuxer.start();
this.isMuxerStart = true;
break;
/*MediaCodec.INFO_TRY_AGAIN_LATER
- 이 함수가 호출되는 경우라면 사실 무시하여도 됩니다.
*/
case MediaCodec.INFO_TRY_AGAIN_LATER:
break;
/*outputBufferIndex >= 0
- 이 경우에 실제 디코딩 된 데이터가 들어오는 경우에 해당됩니다.
*/
default:
while (outputBufferIndex >= 0 && this.mediaCodec != null && this.mediaMuxer != null) {
ByteBuffer outputBuffer = null;
if (CameraUtils.isCamera2()) outputBuffer = this.mediaCodec.getOutputBuffer(outputBufferIndex);
else outputBuffer = outputBuffers[outputBufferIndex];
// null exception
if (outputBuffer == null)
throw new RuntimeException("EncoderOutputBuffer " + outputBuffer + " was NULL");
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
// The codec config data was pulled out and fed to the muxer when we got
// the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
bufferInfo.size = 0;
}
if (bufferInfo.size >= 0) {
if (!this.isMuxerStart) throw new RuntimeException("MediaMuxer hasn't started");
// 프레임의 타임스탬프 작성
bufferInfo.presentationTimeUs = computePresentationTimeNsec();
this.prevTime = bufferInfo.presentationTimeUs;
this.mediaMuxer.writeSampleData(this.trackId, outputBuffer, bufferInfo);
}
this.mediaCodec.releaseOutputBuffer(outputBufferIndex, false/* true is surface init */);
outputBufferIndex = this.mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC/* wait time, nagative value is infinite */);
// end of frame
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
// release
releaseRecorder();
// 저장 완료
onCompleteEncoding(recordPath);
stopEncodingThread();
return;
}
}
break;
}
}
私はあなたと同じ問題を抱えていました。サムスンのSCamera SDKからSCameraProcessor
を使用した後にのみ、機能しました。
それらが提供するサンプルAPKをダウンロードしてよく見ることをお勧めしますが、ここでは、より注意を払う必要がある主要な部分を含むスニペットを示します。
セットアップ:
SCamera sCamera = new SCamera();
sCamera.initialize(this);
...
SCameraProcessorManager processorManager = sCamera.getSCameraProcessorManager();
SCameraEffectProcessor processor = processorManager
.createProcessor(SCameraProcessorManager.PROCESSOR_TYPE_EFFECT);
...
processor.initialize();
...
// Carry out the opening process of the camera device here.
...
processor.setOutputSurface(outputSurface);
Surface cameraSurface = processor.getInputSurface();
// 'cameraSurface' above must then be added as a target to your
// SCaptureRequest.Builder and given as part of the surfaces list
// to have configured when calling SCameraDevice.createCaptureRequest().
録音を開始:
// After setting up your MediaRecorder object...
processor.setRecordingSurface(mediaRecorder.getSurface());
mediaRecorder.start();
記録を停止:
processor.setRecordingSurface(null);
mediaRecorder.stop();
mediaRecorder.reset();
参照:
これが役に立てば幸いです!
Androidxライブラリを使用している場合は、そのライブラリの最新バージョン(1.0.0-alpha03)に更新することをお勧めします。変更ログに記載されているように、この問題を解決する必要があります。
https://developer.Android.com/jetpack/androidx/releases/camera#1.0.0-alpha