Getting error on Android 8 on Samsung device using SCameraCaptureSession

I am trying to capture a video using the SCameraCaptureSession class. When using the function of this class - setRepeatingRequest (which is described here ), I get the following error:

java.lang.IllegalArgumentException: CaptureRequest contains an unconfigured I / O surface!

As I noticed, the problem is due to something in the MediaRecorder Surface object. However, it works great when using an Android version older than 8 years old, and the crash only occurs on Samsung devices running Android 8. No Google search showed anything useful in this crash, so I think it's completely new ...

Does anyone have any info? How can I make the MediaRecorder surface work on the device, as I mentioned?

Important Note: Video capture works fine on any version of Android up to 8 !!!

+5
source share
2 answers

I had the same symptoms. I decided to use the SurfaceView class but not using android.hardware.camera2 Using android.hardware.camera

Solved parts of codes.

@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; } } 
0
source

I had the same exception, and I decided my case. The root cause of my business I created a recreated surface TextureView. When I change it so as not to recreate Surface, the exception has not passed.

My code also works well until Android 8.0

My initialization camera is similar to the following.

 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; } } } 
0
source

Source: https://habr.com/ru/post/1276128/


All Articles