If you have ever used Android MediaRecorder to record video or audio in your android application, you must have faced this error. Recently I had nightmare in using MediaRecorder in my android application. I think it's most unstable class in Android SDK and there is a different behavior in each device. In this blog I am going to explain more about this and possible resolution of this.
First lets see wha's the meaning of this error : Android MediaRecorder Start Failed - 19
There is no documentation on Android developer site about this error code but from my experience I found out that it's because of mis match in size of video preview frame and MediaRecorder video size and this error comes randomly in different devices. The best approach is to use CamCorder profile as mentioned on Android developer site.
CamcorderProfile profile =CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
mMediaRecorder.setProfile(profile);
When you use this most of the following default values are set in MediaRecorder from the selected profile.
But this does not work on all the devices as some the devices don't accept these default values hence MediaRecorder does not work and specifically due to VideoSize it gives error. So to solve this you should manually set all the values. Following settings works on almost all devices
int width = mCamera.getParameters().getPreviewSize().width;
int height = mCamera.getParameters().getPreviewSize().height;
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mMediaRecorder.setVideoSize(width, height);
mMediaRecorder.setVideoFrameRate(30);
However there are still some problem with it. When I tested in some high resolution devices. I got following width and height from getPreviewSize()
2048
1536
But MediaRecorder failed to start and when I checked error log I got error.
Unsupported Video Dimension 1920 X 1688
That means it changed the dimension by itself and ignored the width and height I set it in setVideoSize. So that means you can not depend on it. So what's the solution for this. Well the solution is find the optimal video size based on your preferred frame dimensions and available supported size in your device. Following is the function I used for it.
private Size getOptimalPreviewSize(List sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.2;
double targetRatio = (double) w / h;
if (sizes == null) {}
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
List sizes = mCamera.getParameters().getSupportedPreviewSizes();
Camera.Size optimal = getOptimalPreviewSize(sizes, 640, 480);
if(optimal != null){
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mMediaRecorder.setVideoSize(optimal.width, optimal.height);
mMediaRecorder.setVideoFrameRate(30);
}else{
int width = mCamera.getParameters().getPreviewSize().width;
int height = mCamera.getParameters().getPreviewSize().height;
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mMediaRecorder.setVideoSize(width, height);
mMediaRecorder.setVideoFrameRate(30);
}
For me above solution worked on almost all devices, first we get optimal size from available supported sizes and if there is no supported sizes then we use default preview size.
First lets see wha's the meaning of this error : Android MediaRecorder Start Failed - 19
There is no documentation on Android developer site about this error code but from my experience I found out that it's because of mis match in size of video preview frame and MediaRecorder video size and this error comes randomly in different devices. The best approach is to use CamCorder profile as mentioned on Android developer site.
CamcorderProfile profile =CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
mMediaRecorder.setProfile(profile);
When you use this most of the following default values are set in MediaRecorder from the selected profile.
- OutputFormat
- AudioEncoder
- VideoEncoder
- VideoSize
- VideoFrameRate
But this does not work on all the devices as some the devices don't accept these default values hence MediaRecorder does not work and specifically due to VideoSize it gives error. So to solve this you should manually set all the values. Following settings works on almost all devices
int width = mCamera.getParameters().getPreviewSize().width;
int height = mCamera.getParameters().getPreviewSize().height;
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mMediaRecorder.setVideoSize(width, height);
mMediaRecorder.setVideoFrameRate(30);
However there are still some problem with it. When I tested in some high resolution devices. I got following width and height from getPreviewSize()
2048
1536
But MediaRecorder failed to start and when I checked error log I got error.
Unsupported Video Dimension 1920 X 1688
That means it changed the dimension by itself and ignored the width and height I set it in setVideoSize. So that means you can not depend on it. So what's the solution for this. Well the solution is find the optimal video size based on your preferred frame dimensions and available supported size in your device. Following is the function I used for it.
private Size getOptimalPreviewSize(List
final double ASPECT_TOLERANCE = 0.2;
double targetRatio = (double) w / h;
if (sizes == null) {}
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
List
Camera.Size optimal = getOptimalPreviewSize(sizes, 640, 480);
if(optimal != null){
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mMediaRecorder.setVideoSize(optimal.width, optimal.height);
mMediaRecorder.setVideoFrameRate(30);
}else{
int width = mCamera.getParameters().getPreviewSize().width;
int height = mCamera.getParameters().getPreviewSize().height;
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mMediaRecorder.setVideoSize(width, height);
mMediaRecorder.setVideoFrameRate(30);
}
For me above solution worked on almost all devices, first we get optimal size from available supported sizes and if there is no supported sizes then we use default preview size.