Я не могу сказать вам, почему вы получаете искаженные данные с одних устройств, а не с других, но я могу предложить обходной путь, который, похоже, успешно работает для моего приложения.
Ваш пример кода масштабирует JPEG камеры до 640x480, прежде чем сохранить его на SD-карту. Поэтому я предполагаю, что вам не нужно полноразмерное изображение с камеры.
Если это предположение верно, вы можете полностью пропустить API takePicture()
камеры и просто сохранить предварительный просмотр на SD-карту. Самый простой способ сделать это с помощью setOneShotPreviewCallback()
:
mCamera.setOneShotPreviewCallback( new StillPictureCallback() );
Это вызовет один раз и вернет вам буфер данных с камеры:
private class StillPictureCallback implements Camera.PreviewCallback {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
mPictureTask = new SaveStillPictureTask();
byte[] myData = null;
if ( data != null ) {
myData = data.clone();
}
mPictureTask.execute(myData);
}
}
Обратный вызов вызывает фоновую задачу для сжатия данных и сохранения их в файл. Единственный фрагмент кода, который я пропускаю, - это часть, которая запрашивает у камеры формат кадра предварительного просмотра, ширину и высоту с помощью getCameraInfo()
. Также обратите внимание, что класс Android YUVImage был представлен вместе с Froyo, поэтому, если вам нужно поддерживать более ранние версии Android, вам нужно будет свернуть свой собственный код конвертации (здесь приведены примеры в StackOverflow).
/**
* Background task to compress captured image data and save to JPEG file.
*
*/
private class SaveStillPictureTask extends AsyncTask<byte[], Void, Void> {
private static final String TAG="VideoRecorder.SaveStillPictureTask";
@Override
protected Void doInBackground(byte[]... params) {
byte[] data = params[0];
FileOutputStream out = null;
Bitmap bitmap = null;
if ( data == null ) {
Log.e(TAG, "doInBackground: data is null");
return null;
}
try {
out = new FileOutputStream(mSnapshotFilePath);
// Use the preview image format, as documented in Android SDK javadoc
if ( (mPreviewImageFormat == ImageFormat.NV21) || (mPreviewImageFormat == ImageFormat.YUY2) ) {
saveYUVToJPEG( mCamera, out, data );
} else if (mPreviewImageFormat == ImageFormat.JPEG) {
Log.d(TAG, "directly write JPEG to storage");
out.write(data);
} else {
Log.d(TAG, "try decoding to byte array");
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
if ( bitmap != null ) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
} else {
Log.e(TAG, "decodeByteArray failed, no decoded data");
}
}
}
catch (FileNotFoundException ignore) {;}
catch (IOException ignore) {;}
finally {
if ( out != null ) {
try {
out.close();
} catch (IOException ignore) {;}
out = null;
}
if ( bitmap != null ) {
bitmap.recycle();
bitmap = null;
}
data = null;
}
return null;
}
}
/**
* Save YUV image data (aka NV21 or YUV420sp) data to JPEG file.
*
* @param camera
* @param out
* @param data
*/
protected void saveYUVToJPEG( Camera camera, FileOutputStream out, byte[] data ) {
YuvImage yuvimg = null;
try {
int width = mPreviewWidth;
int height = mPreviewHeight;
Rect rect = new Rect();
rect.left = 0;
rect.top = 0;
rect.right = width - 1;
rect.bottom = height - 1; // The -1 is required, otherwise a buffer overrun occurs
yuvimg = new YuvImage(data, mPreviewImageFormat, width, height, null);
yuvimg.compressToJpeg(rect, 90, out);
} finally {
yuvimg = null;
}
}