faster camera preview using OpenGL? - android

Writing an app which takes the preview frames from camera does some transformation to it and then displays it on screen.
in
public void onPreviewFrame(byte[] data, Camera camera) {}
I take the data do yuv2rgb and some pixel manipulation in JNI in another thread. Then I create bitmap from the RGB int array and draw it using
canvas.drawBitmap(bmp, 0, 0, null);
I get around 15-20FPS on HTC Nexus One at 640x480 and 30+ FPS on Samsung Galaxy S II
I am wondering if I could speed things up by doing the drawing using Android OpenGL ES?
I would be following this guide:
http://obviam.net/index.php/texture-mapping-opengl-android-displaying-images-using-opengl-and-squares/

Are you using SurfaceView for your camera App? The preview frame that you get is a copy of the actual frame that gets displayed on the screen. I am guessing you add another view to display the callback preview frames.
I am not a graphics guy but why don't you try SurfaceTexture. You can use setpreviewtexture() instead of setpreviewwindow(). This way Camera Service will send the buffers directly to the app, instead of making a copy and also queuing it to the AndroidNativeWindow. This might improve your performance.
This is how android Camera app Panorama works. You can find the source code here

Implemented OpenGL ES and it does help with performance

Related

How to crop Camera2 preview without overlay object?

I want to crop the camera preview in Android using camera2 api. I am using android-Camera2Basic the official example.
This is the result I am getting
And, the result exactly I want to achieve is this
I don't want to overlay the object on textureView. I want it actually to be of this size without stretching.
You'll need to edit the image yourself before drawing it, since the default behavior of a TextureView is to just draw the whole image sent to its Surface.
And adjusting the TextureView's transform matrix will only scale or move the whole image, not crop it.
Doing this requires quite a bit of boilerplate, since you need to re-implement most of a TextureView. For best efficiency, you likely want to implement the cropping in OpenGL ES; so you'll need a GLSurfaceView, and then you need to use the OpenGL context of that GLSurfaceView to create a SurfaceTexture object, and then using that texture, draw a quadrilateral with the cropping behavior you want in the fragment shader.
That's fairly basic EGL, but it's quite a bit if you've never done any OpenGL programming before. There's a small test program within the Android OS tree that uses this kind of path: https://android.googlesource.com/platform/frameworks/native/+/master/opengl/tests/gl2_cameraeye/#

Android : Single SurfaceView vs multiple SurfaceView

I am trying to draw 3D object onto camera preview frames (Android). Should I use two surface views, one for camera preview and another GLSurfaceView for drawing. The views should be synchronized and frame rate of display should be good enough to provide a good user experience. So most of the tutorials talk about using multiple views. The alternate idea is to get texture from camera preview and merge it with the 3D object to be drawn so as to get the appropriate 2D raster image.
Which method would be better for performance gains?
P.S : I will be using the Java APIs for openGL es 2.0
Since two surface views increase the number of API calls per frame and require transparency, they will be slower.
You don't need two surface views for your purpose.
Disable depth writes.
Render the camera preview on a 2D quad filling the screen.
Enable depth writes.
Render 3D object.
This will make sure your 3D objects are rendered over the camera preview.
You can also achieve this with two surface views and transparency, but it will be slower.

CPU processing on camera image

Currently I'm showing a preview of the camera on the screen providing the camera preview texture - camera.setPreviewTexture(...) (doing it using opengl of course).
I have a native library which get bytes[] as an image, and return a byte[] - the result image related to the input image. I want to call it, and then draw the input image and the result to the screen - one on each other.
I know that in Opengl, in order to get the data of texture back in the CPU we must be read it using glReadPixel and after process i will have to load the result to a texture - which will have big impact on performances to do it each frame.
I thought about using camera.setPreviewCallback(...), There i'm getting the frame (Calling the process method and transfer the result to the my SurfaceView), and parallel continue using the texture preview Technic for drawing on the screen, but than i'm afraid of synchronizing between the frames that i got in the previewCallback to those i got in the texture.
Am i missing anything ? or there is not easy way to solve this issue?
One approach that may be useful is to direct the output of the Camera to an ImageReader, which provides a Surface. Each frame sent to the Surface is made available as YUV data without a copy, which makes it faster than some of the alternatives. The variations in color formats (stride, alignment, interleave) are handled by ImageReader.
Since you want the camera image to be presented simultaneously with the processing output, you can't send frames down two independent paths.
When the frame is ready, you will need to do a color-space conversion and upload the pixels with glTexImage2D(). This will likely be the performance-limiting factor.
From the comments it sounds like you're familiar with image filtering using a fragment shader; for anyone else who finds this, you can see an example here.

Use MediaCodec to record 720p video but fps of encoding video is too low

I managed to write a video recording demo, my implementation is the same as ContinuousCaptureActivity of Grafika.
In ContinuousCaptureActivity.java, The author create egl object in SurfaceCreated which run in UI thread and call drawFrame also in UI thread. He did 2 things in drawFrame, draw frame to screen and push data to encoder.
See the code here: ContinuousCaptureActivity
Because I set the encoding video size to 1280*720 which is large, the camera preview is not smooth and fps of target video is low.
I plan to create a new thread to do the encoding work but I do not know how to handle with multithread of opengl es. Who can give some advice?
Add: I found that drawFrame of Texture2dProgram use GLES20.glDrawArrays, Will GLES20.glDrawElements get a better performance?
First, 1280x720 shouldn't be an issue for mainstream devices. Some super-cheap low-end devices might struggle, but there isn't really anything you can do if the hardware simply can't handle 1280x720x30fps.
The most common reasons I've seen for low FPS at 720p are failure to configure the Camera with a reasonable fps value (using setPreviewFpsRange() with a value from getSupportedPreviewFpsRange()), and failing to call setRecordingHint(true) (or the Camera2 equivalent). The latter can take you from 15fps to 30fps, but may affect the aspect ratio of the preview.
The video encoding is performed in a separate process, called mediaserver, which manages all interaction with the video encoder hardware. There are already multiple threads in play, so adding another won't help.
The GLES code is drawing two textured triangles. Using a different API won't change the difference.
If you think there is a performance bottleneck, you need to use tools like systrace to narrow it down.
I finally found a way to make drawing frame to screen faster which eventually save the time of processing each frame.
The detail is as follows:
mPreviewWidth = mCamera.getParameters().getPreviewSize().width;
mPreviewHeight = mCamera.getParameters().getPreviewSize().height;
holder.setFixedSize(mPreviewWidth, mPreviewHeight);
add these code to https://github.com/google/grafika/blob/master/src/com/android/grafika/ContinuousCaptureActivity.java#L352
then use GLES20.glViewport(0, 0, mPreviewWidth, mPreviewHeight); to replace https://github.com/google/grafika/blob/master/src/com/android/grafika/ContinuousCaptureActivity.java#L436
This modification will reduce data size of frame to draw a lot.
But it will make preview image not so smooth if we use TextureView, and we can use setScaleX(1.00001f); setScaleY(1.00001f); to resolve it.

How to take snapshot of surfaceview?

Am working in H264 video rendering in Android application using SurfaceView. It has one feature to take snapshot while rendering the video on surface view. Whenever I take a snapshot, I get the Transparent/Black screen only. I use getDrawingCache() method to capture the screen that returns a null value only. I use the below code to capture the screen.
SurfaceView mSUrfaceView = new SurfaceView(this); //Member variable
if(mSUrfaceView!=null)
mSUrfaceView.setDrawingCacheEnabled(true); // After video render on surfaceview i enable the drawing cache
Bitmap bm = mSUrfaceView.getDrawingCache(); // return null
Unless you're rendering H.264 video frames in software with Canvas onto a View, the drawing-cache approach won't work (see e.g. this answer).
You cannot read pixels from the Surface part of the SurfaceView. The basic problem is that a Surface is a queue of buffers with a producer-consumer interface, and your app is on the producer side. The consumer, usually the system compositor (SurfaceFlinger), is able to capture a screen shot because it's on the other end of the pipe.
To grab snapshots while rendering video you can render video frames to a SurfaceTexture, which provides both producer and consumer within your app process. You can then render the texture for display with GLES, optionally grabbing pixels with glReadPixels() for the snapshot.
The Grafika app demonstrates various pieces, though none of the activities specifically solves your problem. For example, "continuous capture" directs the camera preview to a SurfaceTexture and then renders it twice (once for display, once for video encoding), which is similar to what you want to do. The GLES utility classes include a saveFrame() function that shows how to use glReadPixels() to create a bitmap.
See also the Android System-Level Graphics Architecture document.

Categories

Resources