How to get camera preview frame after every 500ms

2019-02-28 09:15发布

问题:

I am developing sample application which gives me color code of pointed image or object via Camera in android. My application is similar to this application and I am using this application code for this.

Using this application code I am able to get camera preview frames continuously and it gives me color code of current previewframe. I want to make it some delay. I want to get only 1 camera preview frame after every 500ms.

How Can i do that, what modification I need to do in this code.

Code :

class Preview extends SurfaceView implements SurfaceHolder.Callback, PreviewCallback { 

    public interface PreviewListener {
        public void OnPreviewUpdated(int[] pixels, int width, int height);
    }

    PreviewListener listener;
    SurfaceHolder mHolder;  
    Camera mCamera = null;  
    byte[] buffer;
    int bufferSize;
    private boolean isFrontCamera = false;
    boolean lightOn = false;

    private boolean isPaused = false;

    //This variable is responsible for getting and setting the camera settings  
    private Parameters parameters;  
    //this variable stores the camera preview size   
    private Size previewSize;  
    //this array stores the pixels as hexadecimal pairs   
    private int[] pixels;  

    Preview(Context context) {  
        super(context);  

        // Install a SurfaceHolder.Callback so we get notified when the  
        // underlying surface is created and destroyed.  
        mHolder = getHolder();  
        mHolder.addCallback(this);  
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  

        try {
            // Instantiate the EnterColorListener so we can send events to the host
            listener = (PreviewListener) context;
        } catch (ClassCastException e) {
            // The activity doesn't implement the interface, throw exception
            throw new ClassCastException(context.toString()
                    + " must implement PreviewListener");
        }
    }  

    public void surfaceCreated(SurfaceHolder holder) {  
        // The Surface has been created, acquire the camera and tell it where  
        // to draw.  
        try {
            CameraInfo info = new CameraInfo();
            Camera.getCameraInfo(0, info);
            if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
                this.isFrontCamera = true;
            }
            mCamera = Camera.open(0); // attempt to get a Camera instance.  index b/c front cameras are ok too.

        }
        catch (Exception e){
            // Camera is not available (in use or does not exist)
            Log.e("camera error", "could not open camera");
            return;
        }
        try {  
           mCamera.setDisplayOrientation(90);
           mCamera.setPreviewDisplay(holder);  
        } catch (IOException exception) {  
            mCamera.release();  
            mCamera = null;  
        }  
    } 

    // thanks cheatcoder@github
    public void flash() {
        if (supportsFlash()) {
            if (!lightOn) {
                lightOn = true;
                mCamera.stopPreview();
                mCamera.setPreviewCallbackWithBuffer(this);// mCamera.setPreviewCallback(this);//setPreviewCallbackWithBuffer(this);
                parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
                mCamera.setParameters(parameters);
                mCamera.startPreview();
            } else {
                lightOn = false;
                mCamera.stopPreview();
                mCamera.setPreviewCallbackWithBuffer(this); //setPreviewCallback(this);//setPreviewCallbackWithBuffer(this);
                parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
                mCamera.setParameters(parameters);
                mCamera.startPreview();
            }
        }
    }

    // thanks http://ikravchenko.blogspot.com/2013/09/nexus-7-2013-torch-issue.html
    public boolean supportsFlash() {
         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {
             parameters = mCamera.getParameters();
             if (parameters.getFlashMode() != null) {
                List<String> supportedFlashModes = parameters.getSupportedFlashModes();
                 if (supportedFlashModes == null || supportedFlashModes.isEmpty() || supportedFlashModes.size() == 1 && supportedFlashModes.get(0).equals(Camera.Parameters.FLASH_MODE_OFF)) {
                     return false;
                 }
                 return true;
             }
         }
         return false;
    }


    public void surfaceDestroyed(SurfaceHolder holder) {  
        // Surface will be destroyed when we return, so stop the preview.  
        // Because the CameraDevice object is not a shared resource, it's very  
        // important to release it when the activity is paused.  
        if (mCamera != null) {
            mCamera.stopPreview();  
            mCamera.setPreviewCallback(null);//setPreviewCallbackWithBuffer(null);
            mCamera.release();  
            mCamera = null;
        }
    }  

    public boolean isFrontCamera() {
        return isFrontCamera;
    }

    /* TODO: fix the bug with the null pointer exception */
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {  
        // Now that the size is known, set up the camera parameters and begin  
        // the preview.  
        if (mCamera != null) {
            parameters = mCamera.getParameters();

            //to do autofocus, need to set the parameters if available
            //http://stackoverflow.com/questions/11623266/camera-parameters-setfocusmode-is-not-working
            List<String> focusModes = parameters.getSupportedFocusModes();
            if (focusModes != null) {
                if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
                    parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                else if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
                    parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
                else if (focusModes.contains(Parameters.FOCUS_MODE_AUTO))
                    parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
            }

            //have to get previewSizes because not all devices support arbitrary previews
            //the following is from Stack Overflow
            int width = this.getWidth();
            int height = this.getHeight();
            Size best = null;
            List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
            // You need to choose the most appropriate previewSize for your app
            for (int i = 0; i < previewSizes.size(); i++) {
                Size size = previewSizes.get(i);
                if ((size.width <= width && size.height <= height) || (size.height <= width && size.width <= height))  {
                    if (best==null) {
                        best=size;
                    } else {
                        int resultArea=best.width*best.height;
                        int newArea=size.width*size.height;

                        if (newArea>resultArea) {
                            best=size;
                        }
                   }
                }
            }

            // make sure something is picked.  previewSizes is guarenteed to have at least one thing.
            if (best != null) {
                previewSize = best; 
            } else {
                previewSize = previewSizes.get(0);
            }

            parameters.setPreviewSize(previewSize.width, previewSize.height);
            pixels = new int[previewSize.width * previewSize.height];  
            mCamera.setParameters(parameters);

            //sets the camera callback to be the one defined in this class  
            mCamera.setPreviewCallbackWithBuffer(this);//setPreviewCallback(this);//setPreviewCallbackWithBuffer(this);  
            bufferSize = previewSize.width*previewSize.height*ImageFormat.getBitsPerPixel(parameters.getPreviewFormat())/8;
            buffer = new byte[bufferSize];
            resetBuffer();

            if (!isPaused) mCamera.startPreview();

        }
    }

    public void pause(boolean isPaused) {
        this.isPaused = isPaused;
        if (isPaused) {
            parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
            mCamera.setParameters(parameters);
            mCamera.stopPreview();
        } else {
            if (mCamera != null) {
                mCamera.setPreviewCallbackWithBuffer(this);//setPreviewCallback(this);//setPreviewCallbackWithBuffer(this);

                if(lightOn)
                    parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);

                mCamera.setParameters(parameters);
                mCamera.startPreview();
            }
        }
    }

    public void resetBuffer() {
        if (mCamera != null) {
            mCamera.addCallbackBuffer(buffer);
        }
    }


    @Override  
    public void onPreviewFrame(byte[] data, Camera camera) {  
        //transforms NV21 pixel data into RGB pixels  
        decodeYUV420SP(pixels, data, previewSize.width,  previewSize.height);  
        listener.OnPreviewUpdated(pixels, previewSize.width, previewSize.height);
    }  

    //Method from Ketai project! Not mine! See below...  
    void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) {  

            final int frameSize = width * height;  

            for (int j = 0, yp = 0; j < height; j++) {       int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;  
              for (int i = 0; i < width; i++, yp++) {  
                int y = (0xff & ((int) yuv420sp[yp])) - 16;  
                if (y < 0)  
                  y = 0;  
                if ((i & 1) == 0) {  
                  v = (0xff & yuv420sp[uvp++]) - 128;  
                  u = (0xff & yuv420sp[uvp++]) - 128;  
                }  

                int y1192 = 1192 * y;  
                int r = (y1192 + 1634 * v);  
                int g = (y1192 - 833 * v - 400 * u);  
                int b = (y1192 + 2066 * u);  

                if (r < 0)                  r = 0;               else if (r > 262143)  
                   r = 262143;  
                if (g < 0)                  g = 0;               else if (g > 262143)  
                   g = 262143;  
                if (b < 0)                  b = 0;               else if (b > 262143)  
                   b = 262143;  

                rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);  
              }  
         }  
    }  

}  

回答1:

You can calculate the elapsed time in onPreviewFrame(). For example:

    boolean isFirstTime = true;
    long startTime = 0;
    PreviewCallback callback = new PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
            // TODO Auto-generated method stub
            if (isFirstTime) {
                isFirstTime = false;
                startTime = SystemClock.currentThreadTimeMillis();
                decodeYUV420SP(pixels, data, previewSize.width,  previewSize.height);  
                listener.OnPreviewUpdated(pixels, previewSize.width, previewSize.height);
            }
            else {
                long currentTime = SystemClock.currentThreadTimeMillis();
                long elapsedTime = currentTime - startTime;
                if (elapsedTime >= 500) { // trigger your event
                    startTime = currentTime;
                    decodeYUV420SP(pixels, data, previewSize.width,  previewSize.height);  
                    listener.OnPreviewUpdated(pixels, previewSize.width, previewSize.height);
                }
            }
        }
    };

Do not forget to reset the boolean value and start time when you switch the preview status.



回答2:

Nothing will guarantee precise 500ms - camera hardware is not set to respond immediately. Still, you can be rather close to that. First of all, open the camera from a background Handler thread (see https://stackoverflow.com/a/19154438/192373).

public void onPreviewFrame(byte[] data, Camera camera) {
    long releaseTime = SystemClock.currentThreadTimeMillis() + 500;
    decodeYUV420SP(pixels, data, previewSize.width,  previewSize.height);  
    listener.OnPreviewUpdated(pixels, previewSize.width, previewSize.height);
    resetBuffer();
    SystemClock.sleep(releaseTime-SystemClock.currentThreadTimeMillis());
}

This assumes that decodeYUV420SP() and OnPreviewUpdated() together take much less than 500ms.