3

I'm trying to record video using MediaRecorder and get raw frames (byte arrays) from onPreviewFrame callback method

Seems it's not that easy, mb it's not even possible, I don't know...

But I found some answers (for similar questions) and people say that you should reconnect camera instance (Camera.reconnect()) after calling MediaRecorder.start() and set preview callback again

I tried something like this but it doesn't work (recording works but onPreviewFrame is never called)

I also tried to call Camera's stopPreview and startPreview methods after MediaRecorder.start() but seems we should not do it otherwise when I try to stop recording (MediaRecorder.stop()) after such actions app will stop responding (it becomes frozen)

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.sample_main);
    mPreview = findViewById(R.id.surface_view);
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            testVideoRecording();
        }
    }, 1000);
}

@Override
public void onPreviewFrame(byte[] bytes, Camera camera) {
    Log.i(TAG, "onPreviewFrame");
}

private void testVideoRecording() {
    mCamera = Camera.open();

    Camera.Parameters parameters = mCamera.getParameters();
    List<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes();
    List<Camera.Size> mSupportedVideoSizes = parameters.getSupportedVideoSizes();
    Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(mSupportedVideoSizes,
            mSupportedPreviewSizes, mPreview.getWidth(), mPreview.getHeight());

    CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
    profile.videoFrameWidth = optimalSize.width;
    profile.videoFrameHeight = optimalSize.height;

    parameters.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight);
    mCamera.setParameters(parameters);
    try {
        mCamera.setPreviewTexture(mPreview.getSurfaceTexture());
    } catch (IOException e) {
        e.printStackTrace();
    }

    mCamera.setPreviewCallback(this);

    mMediaRecorder = new MediaRecorder();

    // Step 1: Unlock and set camera to MediaRecorder
    mCamera.unlock();
    mMediaRecorder.setCamera(mCamera);

    // Step 2: Set sources
    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

    // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
    mMediaRecorder.setProfile(profile);

    // Step 4: Set output file
    mOutputFile = new File(Environment.getExternalStorageDirectory()
            + File.separator + "test.mp4");
    if (mOutputFile.exists()) mOutputFile.delete();

    mMediaRecorder.setOutputFile(mOutputFile.getPath());

    // Step 5: Prepare configured MediaRecorder
    try {
        mMediaRecorder.prepare();
    } catch (IOException e) {
        e.printStackTrace();
    }
    mMediaRecorder.start();

    // Step 6: try to set preview frame callback

    //mCamera.stopPreview();

    try {
        mCamera.reconnect();
    } catch (IOException e) {
        e.printStackTrace();
    }

    /*try {
        mCamera.setPreviewTexture(mPreview.getSurfaceTexture());
    } catch (IOException e) {
        e.printStackTrace();
    }*/

    mCamera.setPreviewCallback(this);

    //mCamera.startPreview();
}

So I want to know if it's even possible to use MediaRecorder and preview frame callback at the same time. If yes then how to do it properly?

user155
  • 775
  • 1
  • 7
  • 25

2 Answers2

1

(edited based on Alex's comment below)

Yes, it is possible in general. The camera API can be tricky, precision and persistence are needed.

First, start with example code that you know works. For example, the official tutorial on capturing video with original API is: https://developer.android.com/guide/topics/media/camera#capture-video . Great, now that you have example code working on your device, you are most of the way there. If you can't get it to work on your device, try on at least one other brand of device.

Next, make you own app code match the threading and API calls of the example. When you get an error or a freeze, look at logs to understand what happened. It is often something trivial like missing permissions in manifest.

Finally, logs, logs, logs. In this question, if you had posted errors from logs along with your code, you might have found a specific problem with a specific answer.

Good luck!

leorleor
  • 554
  • 5
  • 14
  • 1
    You answer a different question. There is a big difference between displaying preview and processing onPreveiwfFrame callbacks – Alex Cohn Nov 26 '18 at 19:30
1

First of all, I strongly recommend to switch from the deprecated Camera API (Camera.open(), …) to the new camera2 API, unless your target devices are all below Android API 21. The new API is much more powerful and flexible. For example, it natively supports multiple targets in same CaptureSession (with limitations depending on CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL). Here is an example of using MediaRecorder and ImageReader in the same session.

When camera2 is at LEGACY level on the device, it may still be safer to use the old API directly (this is the native language of such cameras).

If you are stuck with the old API, consider one of the samples that record video using MediaCodec and MediaMuxer. It is more powerful than MediaRecorder, but requires more work.

The catch, obviously, is that MediaCodec appears at API 21, so these examples are mostly relevant for LEGACY devices.

If you really must work with old devices, you have no choice but run some alternative video encoder, fed from the frames that come to you in onPreviewFrame() callback.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • MediaRecorder + Camera2 (or Surface Video Source) has issues https://github.com/googlesamples/android-Camera2Video/issues/86 – user155 Sep 16 '18 at 15:42
  • 1
    anyway, yes, I need to support old devices as well – user155 Sep 16 '18 at 15:45
  • @Alex Why do you "strongly recommend to switch to the new camera2 API" ? – leorleor Nov 26 '18 at 15:51
  • @leorleor I made an attempt to clarify my recommendation. – Alex Cohn Nov 27 '18 at 07:34
  • Hey All, @AlexCohn Is this working for anyone? I am actively looking to processing preview frame and record video simultaneously. Looking forward to hearing from you. – Bipin Vayalu Jan 04 '19 at 06:58
  • @BipinVayalu yes, it works for me. It happens so that the project currently opened in my AS actually runs such scenario: a Nougat device with legacy camera where I use onPreviewFrame() data for face recognition and also pass to MediaCodec to record an mp4 – Alex Cohn Jan 04 '19 at 15:46
  • Hey @AlexCohn Cool, I would like to learn and integrate kind of scenario in my app. I am following this `googlesamples/android-Camera2Video` but not sure how to achieve it. It would be great if you can share some example or point out any available sample for learning. – Bipin Vayalu Jan 05 '19 at 05:06
  • @BipinVayalu as you can see, googlesamples is not the most supported repo on GitHub. These samples have never been intended to become production code. – Alex Cohn Jan 05 '19 at 11:46
  • Yeah, It's not that much useful, It would be nice if you can share some codebase. Here is my use-case: https://stackoverflow.com/questions/54049275/get-video-and-audio-buffer-separately-while-recording-video-using-front-camera – Bipin Vayalu Jan 05 '19 at 11:52