1

I'm having some trouble switching from mp4 to webm recording.

This code records mp4 audio at 24fps and 640x480 resolution

private boolean prepareVideoRecorder(){

    // BEGIN_INCLUDE (configure_preview)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if(     ( checkSelfPermission(android.Manifest.permission.CAMERA)
                    == PackageManager.PERMISSION_GRANTED ) &&
                (checkSelfPermission(android.Manifest.permission.RECORD_AUDIO)
                    == PackageManager.PERMISSION_GRANTED ) &&
                (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    == PackageManager.PERMISSION_GRANTED ) )
        {
            Log.v(TAG,"Permission is granted");
        } else {
            Log.v(TAG, "Permission is revoked");
            //ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.CAMERA}, 1);
            ActivityCompat.requestPermissions(this, new String[]{
                    android.Manifest.permission.CAMERA,
                    android.Manifest.permission.RECORD_AUDIO,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE  }, 1);
        }
    }

    mCamera = CameraHelper.getDefaultCameraInstance();
    if( mCamera == null )
    {
        return false;
    }
    // We need to make sure that our preview and recording video size are supported by the
    // camera. Query camera to find all the sizes and choose the optimal size given the
    // dimensions of our preview surface.
    Camera.Parameters parameters = mCamera.getParameters();
    List<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes();
    List<Camera.Size> mSupportedVideoSizes = parameters.getSupportedVideoSizes();

    int rotationCorrected = correctCameraRotation();
    parameters.setRotation( rotationCorrected );

    Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(mSupportedVideoSizes,
            mSupportedPreviewSizes, mPreview.getWidth(), mPreview.getHeight());

    // Use the same size for recording profile.
    parameters.setPreviewSize( optimalSize.width, optimalSize.height);
    mCamera.setParameters(parameters);
    try {
        // Requires API level 11+, For backward compatibility use {@link setPreviewDisplay}
        // with {@link SurfaceView}
        mCamera.setPreviewTexture(mPreview.getSurfaceTexture());
    } catch (IOException e) {
        Log.e(TAG, "Surface texture is unavailable or unsuitable" + e.getMessage());
        return false;
    }
    // Step 1: Unlock and set camera to MediaRecorder
    mMediaRecorder = new MediaRecorder();
    try
    {
        mCamera.unlock();
        try {
            mMediaRecorder.setCamera(mCamera);
        }catch( Exception ex )
        {
            Log.e(TAG, "I don't know what happened... " + ex.getMessage());
        }
        // Step 2: Set sources            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT );
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

        CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
        profile.videoFrameWidth = optimalSize.width;
        profile.videoFrameHeight = optimalSize.height;
        profile.fileFormat = MediaRecorder.OutputFormat.MPEG_4;
        profile.videoCodec = MediaRecorder.VideoEncoder.MPEG_4_SP;
        profile.videoFrameRate = 24;
        // reduce audio quality
        profile.audioBitRate = 16000;
        profile.audioChannels = 1;

        // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
        /*  This method should be called after the video AND audio sources
        are set, and before setOutputFile()  */
        mMediaRecorder.setProfile(profile);

        mMediaRecorder.setOrientationHint( rotationCorrected );

        // Step 4: Set output file
        mOutputFile = CameraHelper.getOutputMediaFile(CameraHelper.MEDIA_TYPE_VIDEO);
        if (mOutputFile == null) {
            return false;
        }
        mMediaRecorder.setOutputFile(mOutputFile.getPath());
        // END_INCLUDE (configure_media_recorder)
    }
    catch( IllegalStateException ex )
    {
        Log.e(TAG, "IllegalStateException preparing MediaRecorder: " + ex.getMessage());
        return false;
    }
    catch( Exception ex )
    {
        Log.e(TAG, "I don't know what happened... " + ex.getMessage());
        return false;
    }
    // more stuff here
}

So, when I try to change the output format to webm, I have this

        CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
        profile.videoFrameWidth = optimalSize.width;
        profile.videoFrameHeight = optimalSize.height;
        // FIXME mp4 to webm
        profile.fileFormat = MediaRecorder.OutputFormat.WEBM;
        profile.videoCodec = MediaRecorder.VideoEncoder.VP8;
        profile.audioCodec = MediaRecorder.AudioEncoder.AMR_NB;
        //profile.videoFrameRate = 24;
        // reduce audio quality
        //profile.audioBitRate = 16000;
        //profile.audioChannels = 1;


        // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
        /*  This method should be called after the video AND audio sources
        are set, and before setOutputFile()  */
        mMediaRecorder.setProfile(profile);

And the setProfile method is throwing an IllegalStateException that says E/MediaRecorder: output format (9) is meant for audio recording only and incompatible with video recording. Googling this message I arrived here:

So, my problem is that my profile.fileFormat = MediaRecorder.OutputFormat.WEBM is triggering this

    if (mIsVideoSourceSet
        && of >= OUTPUT_FORMAT_AUDIO_ONLY_START //first non-video output format
        && of < OUTPUT_FORMAT_AUDIO_ONLY_END) {
    ALOGE("output format (%d) is meant for audio recording only"
          " and incompatible with video recording", of);
    return INVALID_OPERATION;
}

Taking a look at the OUTPUT_FORMAT_AUDIO_ONLY_START and OUTPUT_FORMAT_AUDIO_ONLY_END values I can see that OUTPUT_FORMAT_AUDIO_ONLY_END should be 9 and, in MediaRecorder.java, the Webm format is

/** VP8/VORBIS data in a WEBM container */ public static final int WEBM = 9;

What can I do to record video straight to webm?

Héctor C.
  • 433
  • 4
  • 17

2 Answers2

1

Well, it seems to be a problem with the Android SDK version.

The IllegalStateException appeared on a device with SDK 17. Running the same app on a device with SDK 23 works fine and I can record webm video.

There is no sound, however :(

Héctor C.
  • 433
  • 4
  • 17
0

As far as I know, WebM container does not support AMR-NB voice codec. It supports only Vorbis and Opus audio codecs. See here: https://www.webmproject.org/about/faq/

Why you dont want to use WebM's Vorbis audio codec (Opus is for voice)? That would be logical.

  • Yeah, I tried using Vorbis, and switching to the new Camera2 API (see: http://stackoverflow.com/questions/42946397/cant-get-audio-when-recording-webm-in-android ) but webm are still silent. – Héctor C. Mar 22 '17 at 15:33
  • 2
    I am not sure that audio is supported in webm container by android, see here (Audio format and codec support): https://developer.android.com/guide/topics/media/media-formats.html#audio-formats – Alexey Doilnitsyn Mar 23 '17 at 16:59
  • it doesn't record audio at all: https://stackoverflow.com/questions/51332386/mediarecorder-and-videosource-surface-stop-failed-1007-a-serious-android-bug – user25 Jul 13 '18 at 22:29
  • Android doesn't support VORBIS encoder. It supports only VP8 video codec encoding which can only work with VORBIS. Opus encoder is only from Android 10+ and it can only work with VP9 codec which is not supported on Android at all https://developer.android.com/guide/topics/media/media-formats – user924 Oct 28 '21 at 18:21