1. ホーム
  2. Android

Android フロントカメラのビデオ録画に失敗しました (MediaRecorder: start failed: -19)

2022-02-07 14:47:48
<パス

エラーログ

       今日、電話録画のビデオエラーに遭遇しました。mx4のフロントカメラは録画できませんが、リアカメラはOKです。エラーログは以下の通りです。

com.example E/MediaRecorder: start failed: -19
com.example W/System.err: java.lang.RuntimeException: start failed.
com.example W/System.err: at android.media.MediaRecorder.start(Native Method)
com.example W/System.err: at com.example.activity.message.media.CaptureVideoActivity.startRecorderInternal(CaptureVideoActivity.java: 448)
com.example W/System.err: at com.example.activity.message.media.CaptureVideoActivity.startRecorder(CaptureVideoActivity.java:406)
com.example W/System.err: at com.example.activity.message.media.CaptureVideoActivity.onClick(CaptureVideoActivity.java:166)
com.example W/System.err: at android.view.View.performClick(View.java:4869)
com.example W/System.err: at android.view.View$PerformClick.run(View.java:20243)
com.example W/System.err: at android.os.Handler.handleCallback(Handler.java:815)
com.example W/System.err: at android.os.Handler.dispatchMessage(Handler.java:104)
com.example W/System.err: at android.os.Looper.loop(Looper.java:194)
com.example W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5590)
com.example W/System.err: at java.lang.reflect.Method.invoke(Native Method)
com.example W/System.err: at java.lang.reflect.Method.invoke(Method.java:372)
com.example W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:964)
com.example W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:759)
com.example D/Camera-JNI: (tid:13619)[native_release] + context=0x9bf6afb0 camera=0x9eb867b0
com.example D/Camera-JNI: [native_release] context->getStrongCount(2) camera->getStrongCount(2)
com.example D/Camera-JNI: (tid:13619)[native_release] - context=0x9bf6afb0 camera=0x0
com.example D/Camera-JNI: (tid:13619)[release camera] - X context=0x9bf6afb0
com.example I/SurfaceView: updateWindow -- OnPreDrawListener, mHaveFrame = true, this = android.view.SurfaceView{
19c70f61 V.E..... ........ 0,82-1152,1618 #7f0e0704 app:id/videoView}
com.example D/ColorDrawable: Color = -328966canvas = android.view.GLES20RecordingCanvas@1af8758emTintMode = SRC_INmTint = nullColorDrawable = android.graphics.drawable.ColorDrawable@3832cd03
com.example D/ColorDrawable: Color = -16777216canvas = android.view.GLES20RecordingCanvas@2a207d60mTintMode = SRC_INmTint = nullColorDrawable = android.graphics.drawable.ColorDrawable@fe26d27
com.example D/IMGSRV: gralloc_register_buffer:1390: hnd=0x9a9c6be0 ID=72118 fd=135 ref=1
com.example D/GraphicBuffer: register, handle(0x9a9c6be0) (w:648 h:105 s:672 f:0x1 u:0x000b00)
com.example D/Camera-JNI: (tid:13848)[~MtkJNICameraContext] this:0x9bf6afb0
com.example D/OpenGLRenderer: Flushing caches (mode 0)
com.example D/GraphicBuffer: unregister, handle(0x9a9c6be0) (w:648 h:105 s:672 f:0x1 u:0x000b00)
com.example D/IMGSRV: gralloc_unregister_buffer:1503: ID=72118 ref=0
com.example D/OpenGLRenderer: Flushing caches (mode 0)

       ログからわかるのは、オープンに失敗してエラーコードが19になったということだけですが、19の意味もわからないんです?なぜなら、MediaRecorder.start関数はネイティブ関数で、ネイティブでなければ19の意味がわからないからです!

       ネイティブのスタート関数にアクセスしてみましょう。 リンク先を確認する :

static void
android_media_MediaRecorder_start(JNIEnv *env, jobject thiz)
{
    ALOGV("start");
    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
    process_media_recorder_call(env, mr->start(), "java/lang/RuntimeException", "start failed.");
}

static bool process_media_recorder_call(JNIEnv *env, status_t opStatus, const char* exception, const char* message)
{
    ALOGV("process_media_recorder_call");
    if (opStatus == (status_t)INVALID_OPERATION) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return true;
    } else if (opStatus ! = (status_t)OK) {
        jniThrowException(env, exception, message);
        return true;
    }
    return false;
}


       opStatusがokでない場合は例外が発生するので、opStatusの値が何であるかを確認する必要があることがわかります。mr-start関数を見てみましょう。

status_t MediaRecorder::start()
{
    ALOGV("start");
    if (mMediaRecorder == NULL) {
       ALOGE("media recorder is not initialized yet");
        return INVALID_OPERATION;
    }
    if (! (mCurrentState & MEDIA_RECORDER_PREPARED)) {
        ALOGE("start called in an invalid state: %d", mCurrentState);
        return INVALID_OPERATION;
    }

    status_t ret = mMediaRecorder->start();
    if (OK ! = ret) {
        ALOGE("start failed: %d", ret);
        mCurrentState = MEDIA_RECORDER_ERROR;
        return ret;
    }
    mCurrentState = MEDIA_RECORDER_RECORDING;
    return ret;
}


       一連の呼び出しの後、MediaPlayerService.cppに行き着きます。 リンクの表示 .

status_t MediaPlayerService::Client::start()
{
    ALOGV("[%d] start", mConnId);
    sp<MediaPlayerBase> p = getPlayer();
    if (p == 0) return UNKNOWN_ERROR;
    p->setLooping(mLoop);
    return p->start();
}

       この時点でMediaPlayerBaseのstart関数が再度呼び出されます。コード量が多いので、時間があるときにファックソースコードを読みます。

パラメータの設定

       そこで、コードのトラブルシューティングを行う必要がありましたが、その前にググって、パラメータの設定にエラーがあることを突き止めました。始める前に、MediaRecorderのパラメータをいくつか設定し、以下のようにパラメータを設定しました。

private CamcorderProfile getProfile(int cameraId, int quality) {
    if (CamcorderProfile.hasProfile(cameraId, quality)) { if (CamcorderProfile.hasProfile(cameraId, quality)) {
        return CamcorderProfile.get(cameraId, quality);
    }
    return null;
}


@SuppressLint("NewApi")
private void setCamcorderProfile() {
    CamcorderProfile profile = getProfile(cameraId, CamcorderProfile.QUALITY_CIF);
    if (profile == null) {
        profile = getProfile(cameraId, CamcorderProfile.QUALITY_LOW);
    }

    if (profile ! = null) {
        profile.fileFormat = MediaRecorder.OutputFormat.MPEG_4;

        if (Build.MODEL.equalsIgnoreCase("MB525") || Build.MODEL.equalsIgnoreCase("C8812") || Build.MODEL
                .equalsIgnoreCase("C8650")) {
            profile.videoCodec = MediaRecorder.VideoEncoder.H263;
        } else {
            profile.videoCodec = MediaRecorder;
        H264; }

        if (android.os.Build.VERSION.SDK_INT >= 14) {
            profile.audioCodec = MediaRecorder.AudioEncoder.AAC;
        } else {
            if (android.os.Build.DISPLAY ! = null && android.os.Build.DISPLAY.indexOf("MIUI") >= 0) {
                profile.audioCodec = MediaRecorder.AudioEncoder.AAC;
            } else {
                profile.audioCodec = MediaRecorder.AudioEncoder.AMR_NB;
            AMR_NB; }
        }

        mediaRecorder.setProfile(profile);
    } else {                  
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); 
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    }
}

       まず、目的のディスプレイに基づいてCamcorderProfileを取得し、ビデオ出力フォーマット、オーディオエンコーディング、ビデオエンコーディングを調整し、最後にMediaRecorder.setProfile(profile)を呼んで調整したパラメータを設定します。 setProfileのコードは以下のとおりです。

/**
 * Uses the settings from a CamcorderProfile object for recording.
 * This method should be called after the video AND audio sources are set, and before setOutputFile().
 * If a time lapse CamcorderProfile is used, audio related source or recording
 * If a time lapse CamcorderProfile is used, audio related source or recording parameters are ignored.
 If a time lapse CamcorderProfile is used, audio related source or recording * parameters are ignored.
 * @param profile the CamcorderProfile to use
 * CamcorderProfile
 CamcorderProfile */
public void setProfile(CamcorderProfile profile) {
    setOutputFormat(profile.fileFormat);
    setVideoFrameRate(profile.videoFrameRate);
    setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
    setVideoEncodingBitRate(profile.videoBitRate);
    setVideoEncoder(profile.videoCodec);
    if (profile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW &&
         profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_QVGA) {
        // Nothing needs to be done. call to setCaptureRate() enables
        // time lapse video recording.
    } else {
        setAudioEncodingBitRate(profile.audioBitRate);
        setAudioChannels(profile.audioChannels);
        setAudioSamplingRate(profile.audioSampleRate);
        setAudioEncoder(profile.audioCodec);
    }
}

解決方法

       ネット上の回答では、setVideoSizeが原因でエラーになるとのことですが、現在手元のスマホではmx4のみフロントカメラエラーになるので、setVideoSizeのところをコメントアウトして再テストしてください。コメントアウト後、録画はOKです。

       Androidは大穴! プロファイルはシステムから取得した値をMediaRecorderに設定するのですが、システムから取得した値が使えませんね。

@SuppressLint("NewApi")
private void setCamcorderProfile() {
    CamcorderProfile profile = getProfile(cameraId, CamcorderProfile.QUALITY_CIF);
    if (profile == null) {
        profile = getProfile(cameraId, CamcorderProfile.QUALITY_LOW);
    }

    if (profile ! = null) {
        profile.videoCodec = CameraUtil.getVideoCodec();
        profile.audioCodec = CameraUtil.getAudioCodec();

        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mediaRecorder.setVideoFrameRate(profile.videoFrameRate);

        // mx4 front camera size is not recognized by hardware after setting, resulting in failure to open
        if (Build.MODEL.equalsIgnoreCase("MX4") && cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        } else {
            mediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
        }
        mediaRecorder.setVideoEncodingBitRate(profile.videoBitRate);
        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        if (profile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW && profile.quality <= CamcorderProfile
                .QUALITY_TIME_LAPSE_QVGA) {
            // nothing to do
        } else {
            mediaRecorder.setAudioEncodingBitRate(profile.audioBitRate);
            mediaRecorder.setAudioChannels(profile.audioChannels);
            mediaRecorder.setAudioSamplingRate(profile.audioSampleRate);
            mediaRecorder.setAudioEncoder(profile.audioCodec);
        }

    } else {
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    }

}


エラー理由

       問題点やパフォーマンスから推測すると、具体的な原因は、設定した録画幅と高さが、ハードウェアがサポートする幅と高さに一致せず、録画エラーになっていると思われます。本当の原因を探るには、コードのフォローアップが必要ですね。
       ネットではsetVideoFrameRateもアピールエラーになると書いてありますが、私の持っているマシンではまだ起こっていません。