英文:
MediaRecorder IOException prepare failed
问题
这个IOException在升级到AndroidX并将目标SDK版本升级到28之后开始发生在我的Android应用程序中。
在此之前,相同的代码正常工作。
这是一个Capacitor应用程序,它使用capacitor-video-recorder插件。在幕后,此插件使用与android.media.MediaRecorder
交互的fancycamera Java库。
以下是堆栈跟踪,当调用VideoRecorder.startRecording
时抛出,最终导致调用MediaRecorder.prepare:
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)
Android应用程序正在请求相机和录音权限,我可以确认用户首先会受到提示。
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"/>
我还可以看到在应用程序的数据目录中创建了一个输出文件。然而,文件仍然是空的。我也手动尝试写入相同的文件,这是可能的,所以文件IO似乎不是问题。
崩溃来自本地方法,所以调试器并不是很有帮助。但是,我至少可以确认,在调用_prepare
之前,MediaRecorder的mPath
变量似乎已正确设置。
我创建了一个具有相同堆栈的示例应用程序,可以在其中重现错误:https://github.com/disbelief/video-recorder-test
我也会对听到其他可能导致MediaRecorder.prepare
抛出此异常的原因感兴趣。
英文:
This IOException started happening in my Android app after upgrading to AndroidX and increasing the target SDK to version 28.
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
.
Here is the stack trace, which is thrown when calling VideoRecorder.startRecording
, eventually leading to the call to MediaRecorder.prepare:
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.
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. 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.
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
.
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
I'd also be interested to hear what other possible reasons there might be for MediaRecorder.prepare
to throw this exception.
答案1
得分: 1
2022年6月
在我的情况中,错误非常简单。某些手机默认情况下将相机分辨率设置为屏幕大小,但其他手机不会。这就是为什么在许多设备上会抛出 prepare failed: -2147483648
错误。MediaRecorder本质上就是相机本身。因此,您需要传递允许的大小。显然,最好是最大值(传递给 mMediaRecorder.setVideoSize()
):
CameraPropeties props_ = new CameraPropeties(this);
int width_ = props_.getWidth();
int height_ = props_.getHeight();
...
mMediaRecorder.setVideoSize(width_, height_)
...
以及CameraPropeties类:
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$) {
// 在进行任何最大尺寸计算之前,缩放显示尺寸。
displayWidth$ = displayWidth$ * sizePercentage$ / 100;
displayHeight$ = displayHeight$ * sizePercentage$ / 100;
if (cameraWidth$ == -1 && cameraHeight$ == -1) {
// 没有相机。退回到显示尺寸。
return new CameraInfo(displayWidth$, displayHeight$, cameraFrameRate$, displayDensity$);
}
int frameWidth_ = isLandscapeDevice$ ? cameraWidth$ : cameraHeight$;
int frameHeight_ = isLandscapeDevice$ ? cameraHeight$ : cameraWidth$;
if (frameWidth_ >= displayWidth$ && frameHeight_ >= displayHeight$) {
// 画面可以容纳整个显示屏。使用确切的值。
return new CameraInfo(displayWidth$, displayHeight$, cameraFrameRate$, displayDensity$);
}
// 计算新的宽度或高度以保持纵横比。
if (isLandscapeDevice$) {
frameWidth_ = displayWidth$ * frameHeight_ / displayHeight$;
} else {
frameHeight_ = displayHeight$ * frameWidth_ / displayWidth$;
}
return new CameraInfo(frameWidth_, frameHeight_, cameraFrameRate$, displayDensity$);
}
}
英文:
June, 2022
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. Thus you need to pass allowed size . Obviously at best the maximum (to mMediaRecorder.setVideoSize()
)
CameraPropeties props_ = new CameraPropeties(this);
int width_= = props_.getWidth();
int height_ = props_.getHeight();
...
mMediaRecorder.setVideoSize(width_,height_)
...
and CameraPropeties 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$);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论