For nearly 15 days I 'm stuck on the recording of frames in the event onPreviewFrame Android camera . My goal is very simple, I want to record five consecutive images at the click of a button. Only I do not want to block the preview of the camera , so I use the previewFrame . But during the recording of my image in jpeg on the sd card it is green and pink with many horizontal bars. A term I will send a list of byte [] on a Web API.
近15天来,我一直被困在Android摄像头的事件记录中。我的目标很简单,我想在点击一个按钮时记录下五个连续的图像。只是我不想阻止相机的预览,所以我使用了previewFrame。但在我用jpeg在sd卡上录制图像时,它是绿色和粉红色的,有许多横条。我将在Web API上发送一个字节[]列表。
I already do a lot of research , trying to change the PictureSize and PreviewSize but nothing changes .
我已经做了很多研究,试图改变图片大小和预览大小,但是没有任何改变。
Another option would it not go through recording a video and extract the frame of it ?
另一个选择是它不通过录制视频并提取它的帧吗?
Here is the code in my fragment :
这是我片段中的代码:
package rocketweb.com.videocatcher.fragment;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import rocketweb.com.videocatcher.R;
import rocketweb.com.videocatcher.utilities.ImageHelper;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
/**
* Created by Bastien on 15/03/2015.
*/
public class CameraFragment extends BaseFragment {
private Camera mCamera;
private CameraPreview mPreview;
private View mCameraView;
private List<byte[]> imagesFrame;
private boolean activeFrameCapture = false;
public CameraFragment(){
super();
}
public static CameraFragment newInstance(int sectionNumber) {
CameraFragment fragment = new CameraFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_video_catcher, container, false);
boolean opened = safeCameraOpenInView(view);
if(opened == false){
Log.d("Camera", "Error, Camera failed to open");
return view;
}
Button captureButton = (Button) view.findViewById(R.id.button_capture);
captureButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
activeFrameCapture = !activeFrameCapture;
}
}
);
imagesFrame = new ArrayList<byte[]>(){};
return view;
}
private boolean safeCameraOpenInView(View view) {
boolean qOpened = false;
releaseCameraAndPreview();
mCamera = getCameraInstance();
mCameraView = view;
qOpened = (mCamera != null);
if(qOpened == true){
mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera,view);
FrameLayout preview = (FrameLayout) view.findViewById(R.id.camera_preview);
preview.addView(mPreview);
mPreview.startCameraPreview();
}
return qOpened;
}
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
e.printStackTrace();
}
return c; // returns null if camera is unavailable
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
releaseCameraAndPreview();
}
private void releaseCameraAndPreview() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
if(mPreview != null){
mPreview.destroyDrawingCache();
mPreview.mCamera = null;
}
}
class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private Context mContext;
private Camera.Size mPreviewSize;
private List<Camera.Size> mSupportedPreviewSizes;
private List<String> mSupportedFlashModes;
private List<Camera.Size> mSupportedSizePicture;
private View mCameraView;
public CameraPreview(Context context, Camera camera, View cameraView) {
super(context);
// Capture the context
mCameraView = cameraView;
mContext = context;
setCamera(camera);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setKeepScreenOn(true);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
if (activeFrameCapture && imagesFrame.size() <= 5) {
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = parameters.getPreviewSize();
YuvImage im = new YuvImage(data, ImageFormat.NV21, size.width,size.height, null);
Rect r = new Rect(0,0,size.width,size.height);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
im.compressToJpeg(r, parameters.getJpegQuality(), baos);
try{
FileOutputStream output = new FileOutputStream(String.format(
"/sdcard/%s_%d.jpg", "test", System.currentTimeMillis()));
output.write(baos.toByteArray());
output.flush();
output.close();
}catch(FileNotFoundException e)
{
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
imagesFrame.add(data);
}
}
});
}
public void startCameraPreview()
{
try{
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
}
catch(Exception e){
e.printStackTrace();
}
}
private void setCamera(Camera camera)
{
mCamera = camera;
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
mSupportedFlashModes = mCamera.getParameters().getSupportedFlashModes();
mSupportedSizePicture = mCamera.getParameters().getSupportedPictureSizes();
Camera.Parameters parameters = mCamera.getParameters();
// Set the camera to Auto Flash mode.
if (mSupportedFlashModes != null && mSupportedFlashModes.contains(Camera.Parameters.FLASH_MODE_AUTO))
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
mCamera.setParameters(parameters);
requestLayout();
}
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null){
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
Camera.Parameters parameters = mCamera.getParameters();
// Set the auto-focus mode to "continuous"
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
// Preview size must exist.
if(mPreviewSize != null) {
// 0 = landscape
// 90 = portrait
int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 90; break;
case Surface.ROTATION_90: degrees = 0; break;
}
mCamera.setDisplayOrientation(degrees);
if(degrees == 0)
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
else
parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
}
mCamera.setParameters(parameters);
mCamera.startPreview();
} catch (Exception e){
e.printStackTrace();
}
}
/**
* Calculate the measurements of the layout
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// Source: http://*.com/questions/7942378/android-camera-will-not-work-startpreview-fails
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null){
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
// Source: http://*.com/questions/7942378/android-camera-will-not-work-startpreview-fails
if (changed) {
final int width = right - left;
final int height = bottom - top;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null){
Display display = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
switch (display.getRotation())
{
case Surface.ROTATION_0:
previewWidth = mPreviewSize.height;
previewHeight = mPreviewSize.width;
mCamera.setDisplayOrientation(90);
break;
case Surface.ROTATION_90:
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
break;
case Surface.ROTATION_180:
previewWidth = mPreviewSize.height;
previewHeight = mPreviewSize.width;
break;
case Surface.ROTATION_270:
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
mCamera.setDisplayOrientation(180);
break;
}
}
final int scaledChildHeight = previewHeight * width / previewWidth;
mCameraView.layout(0, height - scaledChildHeight, width, height);
}
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int width, int height)
{
// Source: http://*.com/questions/7942378/android-camera-will-not-work-startpreview-fails
Camera.Size optimalSize = null;
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) height / width;
// Try to find a size match which suits the whole screen minus the menu on the left.
for (Camera.Size size : sizes){
if (size.height != width) continue;
double ratio = (double) size.width / size.height;
if (ratio <= targetRatio + ASPECT_TOLERANCE && ratio >= targetRatio - ASPECT_TOLERANCE){
optimalSize = size;
}
}
// If we cannot find the one that matches the aspect ratio, ignore the requirement.
if (optimalSize == null) {
// TODO : Backup in case we don't get a size.
}
return optimalSize;
}
}
}
The layout of my fragment :
片段的布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<FrameLayout
android:id="@+id/camera_preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
/>
<RelativeLayout
android:id="@+id/innerRelativeLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" >
<Button
android:id="@+id/button_capture"
android:text="Capture"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_margin="10dp"
android:layout_gravity="bottom"
/>
</RelativeLayout>
</RelativeLayout>
My project configuration :
我的项目配置:
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
defaultConfig {
applicationId "rocketweb.com.videocatcher"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES.txt'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/LICENSE.txt'
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.google.code.gson:gson:2.3.1'
compile "org.apache.httpcomponents:httpmime:4.2.3"
}
End an exemple of image result : DropBox link to pictures
最后以图像结果为例:DropBox链接到图片
I can upload android studio project if needed. Thank you in advance for your help
如果需要,我可以上传android studio项目。事先谢谢你的帮助
1 个解决方案
#1
0
I think your problem might be when you try to setPreviewSize()
according to the rotation of the camera. Please make sure that you set it to a supported preview size contained in parameters.getSupportedPreviewSizes()
without swapping the dimensions.
我认为您的问题可能是当您试图根据摄像机的旋转来设置previewsize()时。请确保将其设置为参数中所包含的支持的预览大小。getsupportedpreviewsize()不需要交换维度。
I suspect your problem is that you are trying to swap a supported (width,height) pair and setting it to the camera and it's being rejected as a capture parameter, but it still being improperly used when telling the new YuvImage(...)
constructor how to interpret the image byte array.
我怀疑您的问题是,您正在尝试交换一个受支持的(宽度、高度)对,并将其设置为相机,它被拒绝作为捕获参数,但是在告诉新的YuvImage(…)构造函数如何解释图像字节数组时,它仍然被不正确地使用。
#1
0
I think your problem might be when you try to setPreviewSize()
according to the rotation of the camera. Please make sure that you set it to a supported preview size contained in parameters.getSupportedPreviewSizes()
without swapping the dimensions.
我认为您的问题可能是当您试图根据摄像机的旋转来设置previewsize()时。请确保将其设置为参数中所包含的支持的预览大小。getsupportedpreviewsize()不需要交换维度。
I suspect your problem is that you are trying to swap a supported (width,height) pair and setting it to the camera and it's being rejected as a capture parameter, but it still being improperly used when telling the new YuvImage(...)
constructor how to interpret the image byte array.
我怀疑您的问题是,您正在尝试交换一个受支持的(宽度、高度)对,并将其设置为相机,它被拒绝作为捕获参数,但是在告诉新的YuvImage(…)构造函数如何解释图像字节数组时,它仍然被不正确地使用。