有人问过这个问题,但这里从未回答过——但无论如何,它与我的需要有些不同。
我想录制视频,同时在后台运行 Google Vision 库,所以每当我的用户举起条形码(或足够接近条形码)时,相机都会自动检测并扫描条形码 - 并且一直在记录视频。我知道 Google Vision 演示是相当 CPU 密集型的,但是当我尝试一个更简单的版本时(即没有一直抓取每一帧并将其交给检测器)我没有得到可靠的条形码读取。
(我在 KitKat 4.4.3 上运行三星 Galaxy S4 Mini 不幸的是,由于只有三星知道的原因,他们不再报告 OnCameraFocused 事件,因此无法知道相机是否抓住焦点并调用读取条形码。这使得抓取和检查每一帧似乎是唯一可行的解决方案。)
所以至少为了证明这个概念,我想简单地修改 Google Vision Demo。(在这里找到)
似乎最简单的事情就是简单地跳入代码并添加一个媒体记录器。我在表面创建期间在 CameraSourcePreview 方法中执行了此操作。
像这样:
private class SurfaceCallback implements SurfaceHolder.Callback
{
@Override
public void surfaceCreated(SurfaceHolder surface)
{
mSurfaceAvailable = true;
try
{
startIfReady();
if (mSurfaceAvailable)
{
Camera camera = mCameraSource.getCameraSourceCamera();
/** ADD MediaRecorder to Google Example **/
if (camera != null && recordThis)
{
if (mMediaRecorder == null)
{
mMediaRecorder = new MediaRecorder();
camera.unlock();
SurfaceHolder sh = mSurfaceView.getHolder();
mMediaRecorder.setPreviewDisplay(sh.getSurface());
mMediaRecorder.setCamera(camera);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
String OutputFile = Environment.getExternalStorageDirectory() + "/" +
DateFormat.format("yyyy-MM-dd_kk-mm-ss", new Date().getTime()) + ".mp4";
File newOutPut = getVideoFile();
String newOutPutFileName = newOutPut.getPath();
mMediaRecorder.setOutputFile(newOutPutFileName);
Log.d("START MR", OutputFile);
try { mMediaRecorder.prepare(); } catch (Exception e) {}
mCameraSource.mediaRecorder = mMediaRecorder;
mMediaRecorder.start();
}
}
}
}
catch (SecurityException se)
{
Log.e(TAG, "Do not have permission to start the camera", se);
}
catch (IOException e)
{
Log.e(TAG, "Could not start camera source.", e);
}
}
这确实记录了事情,同时仍将每一帧交给视觉代码。但是,奇怪的是,当我这样做时,相机似乎没有正确调用自动对焦,并且没有扫描条形码——因为它们从未真正聚焦,因此无法识别。
我的下一个想法是在条形码检测器处理帧时简单地捕获帧,并将它们一个一个地保存到磁盘(我可以稍后将它们混合在一起。)
我在 CameraSource.java 中做到了这一点。
这似乎没有捕获所有帧,即使我将它们写在后台运行的单独 AsyncTask 中,我认为最终会得到它们——即使需要一段时间才能赶上。保存没有优化,但它看起来好像在整个丢帧,而不仅仅是在最后。
要添加此代码,我尝试将其放在 run() 方法中的私有类 FrameProcessingRunnable 中。
在 FrameBuilder 代码之后,我添加了以下内容:
if (saveImagesIsEnabled)
{
if (data == null)
{
Log.d(TAG, "data == NULL");
}
else
{
SaveImageAsync saveImage = new SaveImageAsync(mCamera.getParameters().getPreviewSize() );
saveImage.execute(data.array());
}
}
哪个调用这个类:
Camera.Size lastKnownPreviewSize = null;
public class SaveImageAsync extends AsyncTask<byte[], Void, Void>
{
Camera.Size previewSize;
public SaveImageAsync(Camera.Size _previewSize)
{
previewSize = _previewSize;
lastKnownPreviewSize = _previewSize;
}
@Override
protected Void doInBackground(byte[]... dataArray)
{
try
{
if (previewSize == null)
{
if (lastKnownPreviewSize != null)
previewSize = lastKnownPreviewSize;
else
return null;
}
byte[] bitmapData = dataArray[0];
if (bitmapData == null)
{
Log.d("doInBackground","NULL: ");
return null;
}
// where to put the output file (note: /sdcard requires WRITE_EXTERNAL_STORAGE permission)
File storageDir = Environment.getExternalStorageDirectory();
String imageFileName = baseFileName + "_" + Long.toString(sequentialCount++) + ".jpg";
String filePath = storageDir + "/" + "tmp" + "/" + imageFileName;
FileOutputStream out = null;
YuvImage yuvimage = new YuvImage(bitmapData, ImageFormat.NV21, previewSize.width,
previewSize.height, null);
try
{
out = new FileOutputStream(filePath);
yuvimage.compressToJpeg(new Rect(0, 0, previewSize.width,
previewSize.height), 100, out);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
if (out != null)
{
out.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
Log.d("doInBackground", ex.getMessage());
}
return null;
}
}
我对 mediarecorder 的想法或蛮力帧捕获的想法很满意,但似乎都没有正常工作。