我试图搜索这个问题很多,但从未见过任何令人满意的答案,所以现在我有了最后的希望。
我设置了onPreviewFrame
回调。其中byte[]
原始帧具有支持的预览格式(NV21
带H.264
编码类型)。
现在,问题是回调始终从固定方向开始提供byte[]
帧,无论何时设备旋转它都不会反映到捕获的byte[]
帧。我尝试使用setDisplayOrientation
和setRotation
,但这些api仅反映预览,而这些预览根本不会显示在捕获的byte []
帧上。
Android文档甚至说,Camera.setDisplayOrientation
仅影响显示预览,而不影响帧字节:
这不会影响onPreviewFrame(byte [],Camera),JPEG图片或录制视频中传递的字节数组的顺序。
最后,在任何API级别,是否有办法改变byte[]
帧的方向?
答案 0 :(得分:2)
如果您不关心格式,一种可能的方法是使用YuvImage
类来获取JPEG缓冲区,使用此缓冲区创建Bitmap
并将其旋转到相应的角度。这样的事情:
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
Size previewSize = camera.getParameters().getPreviewSize();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] rawImage = null;
// Decode image from the retrieved buffer to JPEG
YuvImage yuv = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
yuv.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), YOUR_JPEG_COMPRESSION, baos);
rawImage = baos.toByteArray();
// This is the same image as the preview but in JPEG and not rotated
Bitmap bitmap = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
ByteArrayOutputStream rotatedStream = new ByteArrayOutputStream();
// Rotate the Bitmap
Matrix matrix = new Matrix();
matrix.postRotate(YOUR_DEFAULT_ROTATION);
// We rotate the same Bitmap
bitmap = Bitmap.createBitmap(bitmap, 0, 0, previewSize.width, previewSize.height, matrix, false);
// We dump the rotated Bitmap to the stream
bitmap.compress(CompressFormat.JPEG, YOUR_JPEG_COMPRESSION, rotatedStream);
rawImage = rotatedStream.toByteArray();
// Do something we this byte array
}
答案 1 :(得分:1)
我修改了此Open Source Android Touch-To-Record library的onPreviewFrame方法,以转置并调整已捕获的帧的大小。
我在setCameraParams()方法中定义了“yuvIplImage”。
IplImage yuvIplImage = IplImage.create(mPreviewSize.height, mPreviewSize.width, opencv_core.IPL_DEPTH_8U, 2);
这是我的onPreviewFrame()方法:
@Override
public void onPreviewFrame(byte[] data, Camera camera)
{
long frameTimeStamp = 0L;
if(FragmentCamera.mAudioTimestamp == 0L && FragmentCamera.firstTime > 0L)
{
frameTimeStamp = 1000L * (System.currentTimeMillis() - FragmentCamera.firstTime);
}
else if(FragmentCamera.mLastAudioTimestamp == FragmentCamera.mAudioTimestamp)
{
frameTimeStamp = FragmentCamera.mAudioTimestamp + FragmentCamera.frameTime;
}
else
{
long l2 = (System.nanoTime() - FragmentCamera.mAudioTimeRecorded) / 1000L;
frameTimeStamp = l2 + FragmentCamera.mAudioTimestamp;
FragmentCamera.mLastAudioTimestamp = FragmentCamera.mAudioTimestamp;
}
synchronized(FragmentCamera.mVideoRecordLock)
{
if(FragmentCamera.recording && FragmentCamera.rec && lastSavedframe != null && lastSavedframe.getFrameBytesData() != null && yuvIplImage != null)
{
FragmentCamera.mVideoTimestamp += FragmentCamera.frameTime;
if(lastSavedframe.getTimeStamp() > FragmentCamera.mVideoTimestamp)
{
FragmentCamera.mVideoTimestamp = lastSavedframe.getTimeStamp();
}
try
{
yuvIplImage.getByteBuffer().put(lastSavedframe.getFrameBytesData());
IplImage bgrImage = IplImage.create(mPreviewSize.width, mPreviewSize.height, opencv_core.IPL_DEPTH_8U, 4);// In my case, mPreviewSize.width = 1280 and mPreviewSize.height = 720
IplImage transposed = IplImage.create(mPreviewSize.height, mPreviewSize.width, yuvIplImage.depth(), 4);
IplImage squared = IplImage.create(mPreviewSize.height, mPreviewSize.height, yuvIplImage.depth(), 4);
int[] _temp = new int[mPreviewSize.width * mPreviewSize.height];
Util.YUV_NV21_TO_BGR(_temp, data, mPreviewSize.width, mPreviewSize.height);
bgrImage.getIntBuffer().put(_temp);
opencv_core.cvTranspose(bgrImage, transposed);
opencv_core.cvFlip(transposed, transposed, 1);
opencv_core.cvSetImageROI(transposed, opencv_core.cvRect(0, 0, mPreviewSize.height, mPreviewSize.height));
opencv_core.cvCopy(transposed, squared, null);
opencv_core.cvResetImageROI(transposed);
videoRecorder.setTimestamp(lastSavedframe.getTimeStamp());
videoRecorder.record(squared);
}
catch(com.googlecode.javacv.FrameRecorder.Exception e)
{
e.printStackTrace();
}
}
lastSavedframe = new SavedFrames(data, frameTimeStamp);
}
}
此代码使用方法“YUV_NV21_TO_BGR”,我在此link
中找到了该方法基本上这个方法用来解决,我称之为“Android上的绿魔问题”。您可以在其他SO线程上看到其他Android开发人员面临同样的问题。在我刚刚使用YuvIplImage的转置时添加“YUV_NV21_TO_BGR”方法之前,更重要的是转置,翻转(有或没有调整大小)的组合,在得到的视频中有绿色输出。这个“YUV_NV21_TO_BGR”方法保存了这一天。感谢来自google groups thread的@David Han。
另外你应该知道onPreviewFrame中的所有这些处理(转置,翻转和调整大小)需要花费很多时间,这会导致你的每秒帧数(FPS)率受到严重影响。当我在onPreviewFrame方法中使用此代码时,录制视频的最终FPS从30fps降至3帧/秒。
我建议不要使用这种方法。相反,您可以在AsyncTask中使用JavaCV进行视频文件的后期录制处理(转置,翻转和调整大小)。希望这会有所帮助。