2
votes

I am trying to record video using a Vivo X20 (7.1.1) and the camera2 api without using a preview and without recording sound (Strictly recording HD Video only).

I'm currently stuck because I cannot figure out how to successfully call MediaRecorder.setVideoSize() and record a video in HD. Currently when I run the app the log shows the error: Surface with size (w=1920, h=1080) and format 0x1 is not valid, size not in valid set: [1440x1080, 1280x960, 1280x720, 960x540, 800x480, 720x480, 768x432, 640x480, 384x288, 352x288, 320x240, 176x144]

The phone's stock camera app can record video up to 4K so I'm definitely missing something here. There are a total of two camera devices identified by CameraManager. When I use getOutPutFormats() from CameraCharacteristics it shows the same valid set of resolutions for both cameras and it is the same range as the above error message.

The below is the code I am using to initialize MediaRecorder and initiate a capture session:

 public void StartRecordingVideo() {
        Initialize();
        recordingVideo = true;
        cameraManager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);



    try {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
            String[] cameraIDs = cameraManager.getCameraIdList();
            //LogAllCameraInfo();
            if (cameraIDs != null)
            {
                for(int x = 0; x < cameraIDs.length; x++)
                {
                    Log.d(LOG_ID, "ID: " + cameraIDs[x]);
                }
            }

            cameraManager.openCamera(deviceCameraID, cameraStateCallback, handler);

            Log.d(LOG_ID, "Successfully opened camera");
        }
        else
        {
            throw new IllegalAccessException();
        }

    }
    catch (Exception e)
    {
        recordingVideo = false;
        Log.e(LOG_ID, "Error during record video start: " + e.getMessage());
    }
}
 private void Initialize()
    {
        videoRecordThread = new HandlerThread("video_capture");
        videoRecordThread.start();
        handler = new Handler((videoRecordThread.getLooper()));

        try
        {
            vidRecorder = new MediaRecorder();
            vidRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
           vidRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            vidRecorder.setVideoFrameRate(30);
            vidRecorder.setCaptureRate(30);

            vidRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
            vidRecorder.setVideoEncodingBitRate(10000000);

            vidRecorder.setVideoSize(1920, 1080);
            String videoFilename = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+ File.separator + System.currentTimeMillis() + ".mp4";
            vidRecorder.setOutputFile(videoFilename);

            Log.d(LOG_ID, "Starting video: " + videoFilename);
            vidRecorder.prepare();
        }
        catch (Exception e)
        {
            Log.e(LOG_ID, "Error during Initialize: " + e.getMessage());

        }
    }

And the onReady/onSurfacePrepared/Camera onOpened callbacks:

  @Override
        public void onReady(CameraCaptureSession session) {
            Log.d(LOG_ID, "onReady: ");
            super.onReady(session);
            try {
                CaptureRequest.Builder builder = deviceCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);

                builder.addTarget(vidRecorder.getSurface());
                CaptureRequest request = builder.build();
                session.setRepeatingRequest(request, null, handler);
                vidRecorder.start();
            } catch (CameraAccessException e) {
                Log.d(LOG_ID, "Error on Ready: " + e.getMessage());

            }
        }




        @Override
        public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
            Log.d(LOG_ID, "onSurfacePrepared: ");

            super.onSurfacePrepared(session, surface);
        }




    @Override
    public void onOpened(CameraDevice camera) {
        Log.d(LOG_ID, "onOpened: ");
        deviceCamera = camera;

        try {
            camera.createCaptureSession(Arrays.asList(vidRecorder.getSurface()), recordSessionStateCallback, handler);
        } catch (CameraAccessException e) {
            Log.d(LOG_ID, "onOpened: " + e.getMessage());
        }
    }

I've tried messing with the order of calls and the output format/encoder with no luck. I am sure that I have all the required permissions. Thanks in advance for your time!

1
Update: The above code executes perfectly on an LG G5 running 7.0.0 (takes video, shows high resolution options in outputsizes, no issue with setVideoSize) so I'm inclined to believe the issue is actually with the Vivo x20 itself. However as I am very new to camera2 I could definitely be wrong or missing some way to make it work on the x20.Andy
At any rate, you cannot just insist on setVideoSize(1920, 1080) if it is not supported by the device.Alex Cohn
Query your camera2 characteristics for different formats.Alex Cohn
@AlexCohn thanks for your input! I did query the camera2 characteristics and found the exact same list of resolutions as the error message for both cameras. The x20 is listed as fully supporting HD video (and you can record HD video through the native camera app) but for some reason no resolution higher than 1440x1080 is ever accessible to me otherwise.Andy
The specs available on Internet suggest that the front and rare cameras on this device should support same resolution. As for HD video, you can try to check what this device reports to the deprecated Camera API.Alex Cohn

1 Answers

1
votes

This device most likely supports camera2 at the LEGACY level; check what the output of INFO_SUPPORTED_HARDWARE_LEVEL is.

LEGACY devices are effectively running camera2 on top of the legacy android.hardware.Camera API (more complex than that, but roughly true); as a result, their capabilities via camera2 are restricted.

The maximum recording resolution is one significant problem; android.hardware.Camera records videos via a magic path that the LEGACY mapping layer cannot directly use (there's no Surface involved). As a result, camera2 LEGACY can only record at the maximum preview resolution supported by android.hardware.Camera, not at the maximum recording resolution.

Sounds like this device has no support for 1:1 1080p preview, which is pretty unusual for a device launched so recently.

You can verify if the set of supported preview sizes in the deprecated Camera API matches the list you get in your error; if it doesn't then there may be a OS bug in generating the list so it'd be good to know.

But in general, you can't request sizes that aren't enumerated in the CameraCharacteristics StreamConfiguraitonMap for the camera, no matter what the feature list on the side of the box says. Sometimes the OEM camera app has magic hooks to enable features that normal apps can't get to; often because the feature only works in some very very specific set of circumstances, which normal apps wouldn't know how to replicate.