简体   繁体   English

MediaRecorder IOException 准备失败

[英]MediaRecorder IOException prepare failed

This IOException started happening in my Android app after upgrading to AndroidX and increasing the target SDK to version 28.在升级到 AndroidX 并将目标 SDK 增加到版本 28 后,此 IOException 开始在我的 Android 应用程序中发生。

Previous to this, the same code was working fine.在此之前,相同的代码运行良好。

It is a Capacitor app, and uses the capacitor-video-recorder plugin.这是一个电容器应用程序,并使用电容器录像机插件。 Under the hood, this plugin uses the fancycamera java library which interacts with android.media.MediaRecorder .在后台,这个插件使用了与android.media.MediaRecorder交互的fancycamera java 库。

Here is the stack trace, which is thrown when calling VideoRecorder.startRecording , eventually leading to the call to MediaRecorder.prepare:这是调用 VideoRecorder.startRecording 时抛出的堆栈跟踪,最终导致调用VideoRecorder.startRecording

I/IMediaRecorder: prepare (BpMediaRecorder client) in file frameworks/av/media/libmedia/IMediaRecorder.cpp, function prepare, line 253
E/MediaRecorder: prepare failed: -2147483648
W/System.err: java.io.IOException: prepare failed.
W/System.err:     at android.media.MediaRecorder._prepare(Native Method)
W/System.err:     at android.media.MediaRecorder.prepare(MediaRecorder.java:1038)
W/System.err:     at co.fitcom.fancycamera.Camera2.setUpMediaRecorder(Camera2.java:607)
W/System.err:     at co.fitcom.fancycamera.Camera2.startRecording(Camera2.java:837)
W/System.err:     at co.fitcom.fancycamera.FancyCamera.startRecording(FancyCamera.java:323)
W/System.err:     at com.github.sbannigan.capacitor.VideoRecorder.startRecording(VideoRecorder.java:267)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
W/System.err:     at com.getcapacitor.Bridge$1.run(Bridge.java:515)
W/System.err:     at android.os.Handler.handleCallback(Handler.java:907)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:105)
W/System.err:     at android.os.Looper.loop(Looper.java:216)
W/System.err:     at android.os.HandlerThread.run(HandlerThread.java:65)

The Android app is requesting CAMERA and RECORD_AUDIO permissions, and I can confirm that the user is prompted for these first. Android 应用程序正在请求 CAMERA 和 RECORD_AUDIO 权限,我可以确认首先提示用户输入这些权限。

AndroidManifest.xml AndroidManifest.xml

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

I can also see that an output file is created in the app's data directory.我还可以看到在应用程序的数据目录中创建了一个 output 文件。 However the file remains empty.但是该文件仍然是空的。 I've also manually tried writing to the same file and it is possible, so file IO does not appear to be the problem.我也手动尝试写入同一个文件,这是可能的,所以文件 IO 似乎不是问题。

The crash is coming from a Native Method, so the debugger isn't very helpful.崩溃来自本机方法,因此调试器不是很有帮助。 However I can at least confirm that MediaRecorder's mPath variable appears to be set correctly just before the call to _prepare .但是,我至少可以确认 MediaRecorder 的mPath变量在调用_prepare之前似乎设置正确。

I have created an example app with an identical stack, where it's possible to recreate the error: https://github.com/disbelief/video-recorder-test我创建了一个具有相同堆栈的示例应用程序,可以在其中重新创建错误: https://github.com/disbelief/video-recorder-test

I'd also be interested to hear what other possible reasons there might be for MediaRecorder.prepare to throw this exception.我也有兴趣了解MediaRecorder.prepare抛出此异常的其他可能原因。

June, 2022 2022 年 6 月

The mistake in my case was very simple.我的错误非常简单。 Some phones have Camera resolution as screen size by default but others dont.某些手机默认将相机分辨率作为屏幕尺寸,但其他手机则没有。 That is why on many devices it throws prepare failed: -2147483648 That MediaRecorder in depth is that camera itself.这就是为什么在许多设备上它抛出prepare failed: -2147483648 MediaRecorder 的深度就是那个相机本身。 Thus you need to pass allowed size.因此,您需要通过允许的大小。 Obviously at best the maximum (to mMediaRecorder.setVideoSize() )显然充其量是最大值(到mMediaRecorder.setVideoSize()

CameraPropeties props_ = new CameraPropeties(this);
int width_= = props_.getWidth();
int height_ = props_.getHeight();
...
mMediaRecorder.setVideoSize(width_,height_)
...

and CameraPropeties class:和相机属性 class:

package <my.package>;
import android.content.Context;
import android.content.res.Configuration;
import android.media.CamcorderProfile;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;
import androidx.annotation.RequiresApi;
import static android.content.Context.WINDOW_SERVICE;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;

public class CameraPropeties {
  private int _width;
  private int _hight;
  @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
  private void _getSupportedSizes() {
    CameraInfo recordingInfo = _getRecordingInfo();
    _width = recordingInfo.width;
    _hight = recordingInfo.height;
    Log.e("MaxSupportedSizes --", "WIDTH = " + recordingInfo.width + " HEIGHT = " + recordingInfo.height);
}

private CameraInfo _getRecordingInfo() {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    WindowManager wm = (WindowManager) _context.getSystemService(WINDOW_SERVICE);
    wm.getDefaultDisplay().getRealMetrics(displayMetrics);
    int displayWidth = displayMetrics.widthPixels;
    int displayHeight = displayMetrics.heightPixels;
    int displayDensity = displayMetrics.densityDpi;

    Configuration configuration = _context.getResources().getConfiguration();
    boolean isLandscape = configuration.orientation == ORIENTATION_LANDSCAPE;

    CamcorderProfile camcorderProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
    int cameraWidth = camcorderProfile != null ? camcorderProfile.videoFrameWidth : -1;
    int cameraHeight = camcorderProfile != null ? camcorderProfile.videoFrameHeight : -1;
    int cameraFrameRate = camcorderProfile != null ? camcorderProfile.videoFrameRate : 30;
    return calculateCameraInfo(displayWidth, displayHeight, displayDensity, isLandscape,
            cameraWidth, cameraHeight, cameraFrameRate, 100);
}
private Context _context;
public CameraPropeties(Context c) {
    _context = c;
    _getSupportedSizes();
}
public int getWidth() {
    return _width;
}
public int getHeight() {
    return _hight;
}
static final class CameraInfo {
    final int width;
    final int height;
    final int frameRate;
    final int density;
    CameraInfo(int width$, int height$, int frameRate$, int density$) {
        this.width = width$;
        this.height = height$;
        this.frameRate = frameRate$;
        this.density = density$;
    }
}

static CameraInfo calculateCameraInfo(int displayWidth$, int displayHeight$, int displayDensity$, 
    boolean isLandscapeDevice$, int cameraWidth$, int cameraHeight$, int cameraFrameRate$,
                                      int sizePercentage$) {
    // Scale the display size before any maximum size calculations.
    displayWidth$ = displayWidth$ * sizePercentage$ / 100;
    displayHeight$ = displayHeight$ * sizePercentage$ / 100;

    if (cameraWidth$ == -1 && cameraHeight$ == -1) {
        // No cameras. Fall back to the display size.
        return new CameraInfo(displayWidth$, displayHeight$, cameraFrameRate$, displayDensity$);
    }

    int frameWidth_ = isLandscapeDevice$ ? cameraWidth$ : cameraHeight$;
    int frameHeight_ = isLandscapeDevice$ ? cameraHeight$ : cameraWidth$;
    if (frameWidth_ >= displayWidth$ && frameHeight_ >= displayHeight$) {
        // Frame can hold the entire display. Use exact values.
        return new CameraInfo(displayWidth$, displayHeight$, cameraFrameRate$, displayDensity$);
    }

    // Calculate new width or height to preserve aspect ratio.
    if (isLandscapeDevice$) {
        frameWidth_ = displayWidth$ * frameHeight_ / displayHeight$;
    } else {
        frameHeight_ = displayHeight$ * frameWidth_ / displayWidth$;
    }
    return new CameraInfo(frameWidth_, frameHeight_, cameraFrameRate$, displayDensity$);
}

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM