2015-04-01 78 views
0

近15天,我停留在事件onPreviewFrame Android相機的幀記錄。我的目標非常簡單,我想通過點擊一個按鈕來記錄五個連續的圖像。只有我不想阻止相機的預覽,所以我使用了previewFrame。但在SD卡上以jpeg格式錄製圖像時,它呈現綠色和粉紅色,並帶有許多橫條。術語我將在Web API上發送一個byte []列表。預覽幀與NV21到JPEG的圖像轉換顏色

我已經做了很多研究,試圖改變PictureSize和PreviewSize,但沒有改變。

另一種選擇是否不通過錄制視頻並提取其視頻?

這裏是我的片斷代碼:

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://stackoverflow.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://stackoverflow.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://stackoverflow.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; 
 
     } 
 

 
    } 
 
}

我片段的佈局:

<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>

我的項目合作nfiguration:

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" 
 
}

結束圖像結果的爲例: DropBox link to pictures

,如果需要,我可以上傳Android Studio項目。 預先感謝您的幫助

+0

Nv21格式有大步。創建'YuvImage'時,'null'代替'strides'。 – 2015-08-17 04:01:10

回答

0

我認爲您的問題可能是當您根據相機的旋轉嘗試setPreviewSize()時。請確保將其設置爲包含在parameters.getSupportedPreviewSizes()中的支持的預覽大小,而不交換尺寸。

我懷疑你的問題是你正試圖交換支持的(寬度,高度)對並將其設置到相機,它被拒絕作爲捕獲參數,但它在告訴new YuvImage(...)構造函數時仍然被錯誤地使用來解釋圖像字節數組。