class CameraPreview(Image):
play = BooleanProperty(False)
_camera = None
_previewCallback = None
_previewTexture = None
def __init__(self, **kwargs):
super(CameraPreview, self).__init__(**kwargs)
self.texture = Texture.create(size=(480, 640))
self.texture_size = (480, 640)
self._camera = Camera.open()
self._previewTexture = SurfaceTexture(int(self.texture.id))
self._camera.setPreviewTexture(self._previewTexture)
# Tried using a preview callback (see jnius class further down) - I get a crash...
# self._previewCallback = CameraPreviewCallback(self._previewTexture)
# self._camera.setPreviewCallback(self._previewCallback)
def on_play(self, instance, value):
if not self._camera:
return
if value:
self._camera.startPreview()
else:
self._camera.stopPreview()
# pyjnius-wrapped class that implements the camera's preview callback interface: doesn't work :(
class CameraPreviewCallback(PythonJavaClass):
__javainterfaces__ = ['android/hardware/Camera$PreviewCallback']
preview_texture = None
def __init__(self, preview_texture):
self.preview_texture = preview_texture
@java_method('(B[]Landroid/hardware/Camera;)V')
def onPreviewFrame(self, data, camera):
self.preview_texture.updateTexImage()
from jnius import autoclass, PythonJavaClass, java_method
Camera = autoclass('android.hardware.Camera')
SurfaceTexture = autoclass('android.graphics.SurfaceTexture')
GL_TEXTURE_EXTERNAL_OES = 36197
class CameraPreview(Image):
play = BooleanProperty(False)
resolution = ListProperty([640, 480])
_camera = None
_previewCallback = None
_previewTexture = None
secondary_texture = None
def __init__(self, **kwargs):
self.canvas = RenderContext()
super(CameraPreview, self).__init__(**kwargs)
self.bind(size=self.size_changed)
# (2)
self.canvas.shader.fs = '''
#extension GL_OES_EGL_image_external : require
#ifdef GL_ES
precision highp float;
#endif
/* Outputs from the vertex shader */
varying vec4 frag_color;
varying vec2 tex_coord0;
/* uniform texture samplers */
uniform sampler2D texture0;
uniform samplerExternalOES texture1;
void main()
{
gl_FragColor = texture2D(texture1, tex_coord0);
}
'''
# This is needed for the default vertex shader.
self.canvas['projection_mat'] = Window.render_context['projection_mat']
with self.canvas.before:
# (4)
Callback(self.draw_callback)
with self.canvas:
# (3)
BindTexture(texture=self.secondary_texture, index=1)
self.canvas['secondary_texture'] = 1
# (1)
tex_id = kivy.graphics.opengl.glGenTextures(1)[0]
kivy.graphics.opengl.glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex_id)
width, height = self.resolution
self.secondary_texture = Texture(width=width, height=height, target=GL_TEXTURE_EXTERNAL_OES, texid=int(tex_id), colorfmt='rgba')
# (6)
self._camera = Camera.open()
self._previewTexture = SurfaceTexture(int(tex_id))
self._camera.setPreviewTexture(self._previewTexture)
def draw_callback(self, instr):
if self._previewTexture:
self._previewTexture.updateTexImage()
def config_camera(self, surface):
self._camera.setPreviewTexture(surface)
def update_canvas(self, dt):
self.canvas.ask_update()
def size_changed(self, *largs):
pass
def on_play(self, instance, value):
if not self._camera:
return
if value:
self._camera.startPreview()
# (5)
Clock.schedule_interval(self.update_canvas, 1.0/30)
else:
Clock.unschedule(self.update_canvas)
self._camera.stopPreview()
Capturing a single image should'nt use a full camera provider, but the
Intent ACTION_IMAGE_CAPTURE instead.
http://developer.android.com/reference/android/provider/MediaStore.html#ACTION_IMAGE_CAPTURE
Le 09/05/2013 10:36, Gully a �crit :
import kivy
kivy.require('1.6.0')
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.image import Image
from kivy.properties import BooleanProperty, ListProperty, NumericProperty, StringProperty
from kivy.graphics import RenderContext, Callback, BindTexture
from kivy.graphics.texture import Texture
from kivy.core.window import Window
from kivy.logger import Logger
import sys
GL_TEXTURE_EXTERNAL_OES = 36197
IsAndroid = sys.platform == 'linux3'
if IsAndroid:
from jnius import autoclass, cast
# MediaRecorder Java classes
Camera = autoclass('android.hardware.Camera')
Surface = autoclass('android.view.Surface')
SurfaceTexture = autoclass('android.graphics.SurfaceTexture')
MediaRecorder = autoclass('android.media.MediaRecorder')
AudioSource = autoclass('android.media.MediaRecorder$AudioSource')
VideoSource = autoclass('android.media.MediaRecorder$VideoSource')
OutputFormat = autoclass('android.media.MediaRecorder$OutputFormat')
AudioEncoder = autoclass('android.media.MediaRecorder$AudioEncoder')
VideoEncoder = autoclass('android.media.MediaRecorder$VideoEncoder')
CamcorderProfile = autoclass('android.media.CamcorderProfile')
class AndroidCameraPreview(Image):
"""
Android camera preview widget. Extends Image with a custom shader that
displays textures of target GL_TEXTURE_EXTERNAL_OES. Note, this requires
Android API 11 because of it's use of SurfaceTexture.
"""
play = BooleanProperty(False)
resolution = ListProperty([720, 720])
camera_id = NumericProperty(-1)
_camera = None
_previewCallback = None
_previewTexture = None
_previewSurface = None
_secondary_texture = None
def __init__(self, **kwargs):
self.canvas = RenderContext()
super(AndroidCameraPreview, self).__init__(**kwargs)
self.bind(resolution=self._resolution_changed)
self.bind(camera_id=self._camera_id_changed)
self.bind(play=self._play_changed)
# This is needed for the default vertex shader.
self.canvas['projection_mat'] = Window.render_context['projection_mat']
with self.canvas:
Callback(self._draw_callback)
BindTexture(texture=self._secondary_texture, index=1)
self.canvas['secondary_texture'] = 1
self._init_texture(self.resolution)
def start(self, play=True):
self._init_camera(self.camera_id)
self.play = play
def stop(self):
self._release_camera()
def _init_texture(self, resolution):
width, height = resolution
# Image looks at self.texture to determine layout, so we create
# texture but don't actually display it.
self.texture = Texture.create(size=(height, width))
self.texture_size = self.texture.size
if IsAndroid:
self._secondary_texture = Texture(width=height, height=width, target=GL_TEXTURE_EXTERNAL_OES, colorfmt='rgba')
self._secondary_texture.bind()
self._previewTexture = SurfaceTexture(int(self._secondary_texture.id))
def _init_camera(self, camera_id):
Logger.info('Init camera %d' % camera_id)
if self._camera and self.camera_id == camera_id:
return
self._release_camera()
if IsAndroid:
self._camera = Camera.open(camera_id)
parameters = self._camera.getParameters()
#print parameters.flatten()
if parameters is None:
Logger.warning('Can''t read parameters')
return
supportedSizes = parameters.getSupportedVideoSizes()
if supportedSizes is None:
supportedSizes = parameters.getSupportedPreviewSizes()
sizes = []
if supportedSizes is not None:
iterator = supportedSizes.iterator()
while iterator.hasNext():
cameraSize = iterator.next()
sizes.append((cameraSize.width, cameraSize.height))
pickedSize = self._pick_optimal_size(sizes)
# Update texture according to picked size
self.resolution = list(pickedSize)
parameters.setPreviewSize(*pickedSize)
self._camera.setParameters(parameters)
self._camera.setPreviewTexture(self._previewTexture)
def get_camera(self):
return self._camera
def _resolution_changed(self, instance, value):
Logger.info('Resolution changed to ' + str(value))
self._init_texture(value)
def _camera_id_changed(self, instance, value):
Logger.info('Changed camera %d' % value)
if not IsAndroid:
return
# Transform orientation based on selected camera
if self.camera_id == 0:
sampleVec = ('1.0-tex_coord0.y','1.0-tex_coord0.x')
elif self.camera_id == 1:
sampleVec = ('tex_coord0.y','1.0-tex_coord0.x')
else:
raise Exception('Invalid camera id')
self.canvas.shader.fs = '''
#extension GL_OES_EGL_image_external : require
#ifdef GL_ES
precision highp float;
#endif
/* Outputs from the vertex shader */
varying vec4 frag_color;
varying vec2 tex_coord0;
/* uniform texture samplers */
uniform sampler2D texture0;
uniform samplerExternalOES texture1;
void main()
{
// Flip & Mirror coordinates to rotate source texture by 90 degress
gl_FragColor = texture2D(texture1, vec2(%s,%s));
}
''' % (sampleVec[0], sampleVec[1])
def _play_changed(self, instance, value):
Logger.info('camera %d _play_changed %d' % (self.camera_id, value))
if not IsAndroid:
return
if value:
if not self._camera:
self._init_camera(self.camera_id)
self._camera.startPreview()
Clock.schedule_interval(self._update_canvas, 1.0/30)
else:
if self._camera:
Clock.unschedule(self._update_canvas)
self._camera.stopPreview()
def _release_camera(self):
self.play = False
if self._camera:
self._camera.release()
self._camera = None
def _pick_optimal_size(self, sizes):
# Now just returns the one guaranteed support size
return sizes[0]
def _draw_callback(self, instr):
if self._previewTexture:
self._previewTexture.updateTexImage()
def _update_canvas(self, dt):
self.canvas.ask_update()
Hi Chen, thank you a lot for the code. I will test it. ;-)
You received this message because you are subscribed to a topic in the Google Groups "Kivy users support" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/kivy-users/hiyAJqjhFjo/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to kivy-users+...@googlegroups.com.
Do you think you could create a garden package for it with doc?
cam = AndroidCameraPreview(camera_id=0)
cam.play=True
cam._camera.takePicture(None, None, self.PictureCallback())
cam.stop()
V/CameraWrapper( 163): camera_preview_enabled
V/CameraWrapper( 163): camera_preview_enabled->41E086C8->41E08720
V/CameraWrapper( 163): camera_preview_enabled
V/CameraWrapper( 163): camera_preview_enabled->41E086C8->41E08720
V/CameraWrapper( 163): camera_set_preview_window->41E086C8->41E08720
V/CameraWrapper( 163): camera_start_preview
V/CameraWrapper( 163): camera_start_preview->41E086C8->41E08720
I/python (16972): [DEBUG ] Taking picture
V/CameraWrapper( 163): camera_enable_msg_type->41E086C8->41E08720
V/CameraWrapper( 163): camera_enable_msg_type
V/CameraWrapper( 163): camera_take_picture
V/CameraWrapper( 163): camera_take_picture->41E086C8->41E08720
D/NvOsDebugPrintf( 163): [CAM] [RAWCHIP] rawchip initialize.
D/NvOsDebugPrintf( 163): [CAM] [RAWCHIP] rawchip initialize done.
D/NvOsDebugPrintf( 163): [CAM] NvCameraSensorPowerOn: --
D/NvOsDebugPrintf( 163): [CAM]NvCameraPowerOnThread: --
D/NvOsDebugPrintf( 163): NvIspAfGetConvergeStatus, return status=2
D/NvOsDebugPrintf( 163): ENC_T3:Encoder_Open ++
I/ActivityManager( 552): Process com.endomondo.android (pid 16901) has died.
I/ActivityManager( 552): Process com.google.android.inputmethod.latin.dictionarypack (pid 16776) has died.
I/ActivityManager( 552): Process com.android.keychain (pid 16806) has died.
D/NvOsDebugPrintf( 163): ENC_T3:OutputThread ++
D/NvOsDebugPrintf( 163): ENC_T3:OutputThread Before Semaphore Wait
D/NvOsDebugPrintf( 163): ENC_T3:Encoder_Open --
D/NvOsDebugPrintf( 163): ENC_T3:FeedInputThread ++
D/NvOsDebugPrintf( 163): [CAM] StartStillCapture, mUsingFlash=0, NslNumberOfImages=0
D/NvOsDebugPrintf( 163): [CAM] CameraBlockExtension,StillCaptureActive=1
D/NvOsDebugPrintf( 163): [CAM] Selected Sensor Mode: 3280x2464 FrameRate: 21.0 StreamIndex: 2
I/ActivityManager( 552): Process com.android.voicedialer (pid 16840) has died.
E/Camera (16972): Unknown message type 32771
I/python (16972): [DEBUG ] Stopping camera
V/CameraWrapper( 163): camera_disable_msg_type->41E086C8->41E08720
V/CameraWrapper( 163): camera_disable_msg_type
V/CameraWrapper( 163): camera_disable_msg_type->41E086C8->41E08720
V/CameraWrapper( 163): camera_disable_msg_type
V/CameraWrapper( 163): camera_stop_preview
V/CameraWrapper( 163): camera_stop_preview->41E086C8->41E08720
V/CameraWrapper( 163): camera_cancel_picture
V/CameraWrapper( 163): camera_cancel_picture->41E086C8->41E08720
V/CameraWrapper( 163): camera_release
V/CameraWrapper( 163): camera_release->41E086C8->41E08720
D/NvOsDebugPrintf( 163): Waiting on hOutstandingRequestsServedSema failed 0x5
D/NvOsDebugPrintf( 163): [CAM]NvMMCameraClose: ++
D/NvOsDebugPrintf( 163): [CAM] NvMMCameraBlockPrivateClose NvCameraAp15Cleanup, release camera
D/NvOsDebugPrintf( 163): NvCameraSyncPointThread is terminated.
D/NvOsDebugPrintf( 163): [CAM]NvMMCameraClose: --
D/NvOsDebugPrintf( 163): ENC_T3:Encoder_Close ++
D/NvOsDebugPrintf( 163): ENC_T3:Encoder_Close SW Encoder : 0
D/NvOsDebugPrintf( 163): ENC_T3:OutputThread After Semaphore Wait, CloseEncoder : 1
D/NvOsDebugPrintf( 163): ENC_T3:OutputThread --
D/NvOsDebugPrintf( 163): ENC_T3:FeedInputThread --
D/NvOsDebugPrintf( 163): ENC_T3:Encoder_Close --
V/CameraWrapper( 163): camera_set_preview_window->41E086C8->41E08720
I/CameraClient( 163): Destroying camera 0
V/CameraWrapper( 163): camera_device_close
D/NvOsDebugPrintf( 163): NvIspAfGetConvergeStatus, return status=2
D/NvOsDebugPrintf( 163): ENC_T3:OutputThread After Semaphore Wait, CloseEncoder : 0
D/NvOsDebugPrintf( 163): ENC-DBG jpegencoder 0x4137ec78 , ptr for image 0x421d8003, hMem for 2d 0xc1f202e0
D/NvOsDebugPrintf( 163): ENC_T3:OutputThread Before Semaphore Wait
D/NvOsDebugPrintf( 163): ENC_T3:OutputThread After Semaphore Wait, CloseEncoder : 0
W/CameraClient( 163): lockIfMessageWanted(2): dropped unwanted message
D/NvOsDebugPrintf( 163): ENC_T3:OutputThread Before Semaphore Wait
protected void onCreateBeforeSDLSurface() {mFrameLayout = new FrameLayout(this);mCameraView = new SurfaceView(this);mCameraView.setZOrderOnTop(false);mCameraView.setFocusable(false);mCameraSurfaceHolder = mCameraView.getHolder();mCameraSurfaceHolder.addCallback(this);mCameraSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);mFrameLayout.addView(mCameraView);mCameraView.setVisibility(View.VISIBLE);}
protected void onCreateAfterSurface(SurfaceView mView) {mView.setZOrderOnTop(true); // necessarymView.getHolder().setFormat(PixelFormat.TRANSLUCENT);mFrameLayout.addView(mView);setContentView(mFrameLayout);}
public void surfaceCreated(SurfaceHolder holder) {mCameraSurfaceReady = true;}public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}public void surfaceDestroyed(SurfaceHolder holder) {mCameraSurfaceReady = false;}
public SurfaceHolder getCameraSurfaceHolder() {if (mCameraSurfaceReady)return mCameraSurfaceHolder;return null;}
Hi all,
I'm trying to run a simple camera preview application on Android. What I'm trying to do is basically this:
1. Create a widget based on the Image widget, with a new texture
2. Use the android's Camera class to open the camera
3. Create an android SurfaceTexture using my widget's texture ID
4. Set the SurfaceTexture as the preview texture using the Camera's setPreviewTexture
It took a while, but now I have a stable app, can start preview. Using the logs, I can see the camera object is updating frames, but nothing is displayed...
I've tried to call canvas.ask_update() from different locations but to no avail.
Also, tried to implement (using pyjnius) the camera's preview callback (although many examples say I don't have to) but that didn't work either. When I try to set the callback I get the following error (or at least I think it's related):
"W/dalvikvm(26697): Invalid indirect reference 0x5ded791c in decodeIndirectRef"
All of this is the first step before using the MediaRecorder class, since that requires even more tinkering, and it seems to me preview is the first thing I should be worried about...
Also, its worth mentioning the rest of the application is working well, but for now I've limited it to a button over the preview widget to start/stop the preview.
See below for the code.
Any ideas?
Thanks!
class CameraPreview(Image):
play = BooleanProperty(False)
_camera = None
_previewCallback = None
_previewTexture = None
def __init__(self, **kwargs):
super(CameraPreview, self).__init__(**kwargs)
self.texture = Texture.create(size=(480, 640))
self.texture_size = (480, 640)
self._camera = Camera.open()
self._previewTexture = SurfaceTexture(int(self.texture.id))
self._camera.setPreviewTexture(self._previewTexture)
# Tried using a preview callback (see jnius class further down) - I get a crash...
# self._previewCallback = CameraPreviewCallback(self._previewTexture)
# self._camera.setPreviewCallback(self._previewCallback)
def on_play(self, instance, value):
if not self._camera:
return
if value:
self._camera.startPreview()
else:
self._camera.stopPreview()
# pyjnius-wrapped class that implements the camera's preview callback interface: doesn't work :(
class CameraPreviewCallback(PythonJavaClass):
__javainterfaces__ = ['android/hardware/Camera$PreviewCallback']
preview_texture = None
def __init__(self, preview_texture):
self.preview_texture = preview_texture
@java_method('(B[]Landroid/hardware/Camera;)V')
def onPreviewFrame(self, data, camera):
self.preview_texture.updateTexImage()