这个问题被问到但从未回答过here - 但不管怎么说,它与我的需求有所不同。
我想录制视频,同时在后台运行Google Vision库,因此每当我的用户拿起条形码(或接近一个条形码)时,相机会自动检测并扫描条形码 - 而且它一直在运行正在录制视频。我知道谷歌视觉演示是相当CPU密集型的,但是当我尝试更简单的版本时(即没有抓住每一帧并将其交给探测器)我没有得到可靠的条形码读取。
(我在KitKat 4.4.3上运行三星Galaxy S4 Mini不幸的是,由于三星已知的原因,他们不再报告OnCameraFocused事件,因此无法知道相机是否抓住焦点并调用条形码然后阅读。这使得抓取和检查每一帧似乎是唯一可行的解决方案。)
所以至少要证明这个概念,我想简单地修改一下Google Vision Demo。 (找到Here)
似乎最简单的事情就是跳转代码并添加一个媒体记录器。我在表面创建过程中在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);
}
}
那些记录的东西,同时仍然将每一帧交给Vision代码。但是,奇怪的是,当我这样做时,相机似乎没有正确调用自动对焦,并且条形码不会被扫描 - 因为它们从未真正聚焦,因此无法识别。
我的下一个想法是在条形码检测器处理帧时简单地捕获帧,并将它们逐个保存到磁盘上(我可以稍后将它们复合在一起。)
我是在CameraSource.java中做到的。
这似乎并没有捕获所有的帧,即使我在后台运行的单独的AsyncTask中写出它们,我认为最终会得到它们 - 即使它需要一段时间才能赶上。保存没有得到优化,但它看起来好像是在整个帧中丢帧,而不仅仅是在最后。
要添加此代码,我尝试将其放入 私有类FrameProcessingRunnable 在run()方法中。
在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的想法,或强力帧捕获的想法,但似乎都没有正常工作。