i using texturefromcameraactivity grafika record video in square ( 1:1 ) resolution. therefor gles20.glviewport video gets moved top , appears squared. record square view using mediarecorder or @ least record camera normal resolutiona , crop using ffmpeg. same error on , on again , cant figure out why.
the error get:
start called in invalid state: 4
and yes added necessary permissions.
android.permission.write_external_storage android.permission.camera android.permission.record_video android.permission.record_audio android.permission.storage android.permission.read_external_storage
here modified code:
https://github.com/google/grafika
thanks :d
package com.android.grafika; import android.graphics.surfacetexture; import android.hardware.camera; import android.media.camcorderprofile; import android.media.mediarecorder; import android.opengl.gles20; import android.opengl.matrix; import android.os.bundle; import android.os.environment; import android.os.handler; import android.os.looper; import android.os.message; import android.util.log; import android.view.motionevent; import android.view.surface; import android.view.surfaceholder; import android.view.surfaceview; import android.view.view; import android.widget.button; import android.widget.seekbar; import android.widget.textview; import android.app.activity; import android.widget.toast; import com.android.grafika.gles.drawable2d; import com.android.grafika.gles.eglcore; import com.android.grafika.gles.glutil; import com.android.grafika.gles.sprite2d; import com.android.grafika.gles.texture2dprogram; import com.android.grafika.gles.windowsurface; import java.io.file; import java.io.ioexception; import java.lang.ref.weakreference; public class texturefromcameraactivity extends activity implements view.onclicklistener, surfaceholder.callback, seekbar.onseekbarchangelistener { private static final int default_zoom_percent = 0; // 0-100 private static final int default_size_percent = 80; // 0-100 private static final int default_rotate_percent = 75; // 0-100 // requested values; actual may differ. private static final int req_camera_width = 720; private static final int req_camera_height = 720; private static final int req_camera_fps = 30; // holder our surfaceview. surface can outlive activity (e.g. when // screen turned off , on power button). // // becomes non-null after surfacecreated() callback called, , gets set // null when surfacedestroyed() called. private static surfaceholder ssurfaceholder; // thread handles rendering , controls camera. started in onresume(), // stopped in onpause(). private renderthread mrenderthread; // receives messages renderer thread. private mainhandler mhandler; // user controls. private seekbar mzoombar; private seekbar msizebar; private seekbar mrotatebar; // these values passed camera/render thread, , displayed in ui. // peek @ values in renderthread object, we'd need // synchronize access carefully. private int mcamerapreviewwidth, mcamerapreviewheight; private float mcamerapreviewfps; private int mrectwidth, mrectheight; private int mzoomwidth, mzoomheight; private int mrotatedeg; surfaceholder sh; mediarecorder recorder; surfaceholder holder; boolean recording = false; public static final string tag = "videocapture"; private static final file output_dir = environment.getexternalstoragedirectory(); @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); recorder = new mediarecorder(); setcontentview(r.layout.activity_texture_from_camera); mhandler = new mainhandler(this); surfaceview cameraview = (surfaceview) findviewbyid(r.id.cameraontexture_surfaceview); sh = cameraview.getholder(); cameraview.setclickable(true);// make surface view clickable sh.addcallback(this); //preparerecorder(); mzoombar = (seekbar) findviewbyid(r.id.tfczoom_seekbar); msizebar = (seekbar) findviewbyid(r.id.tfcsize_seekbar); mrotatebar = (seekbar) findviewbyid(r.id.tfcrotate_seekbar); mzoombar.setprogress(default_zoom_percent); msizebar.setprogress(default_size_percent); mrotatebar.setprogress(default_rotate_percent); mzoombar.setonseekbarchangelistener(this); msizebar.setonseekbarchangelistener(this); mrotatebar.setonseekbarchangelistener(this); button record_btn = (button)findviewbyid(r.id.button); record_btn.setonclicklistener(this); initrecorder(); updatecontrols(); } @override protected void onresume() { log.d(tag, "onresume begin"); super.onresume(); mrenderthread = new renderthread(mhandler); mrenderthread.setname("texfromcam render"); mrenderthread.start(); mrenderthread.waituntilready(); renderhandler rh = mrenderthread.gethandler(); rh.sendzoomvalue(mzoombar.getprogress()); rh.sendsizevalue(msizebar.getprogress()); rh.sendrotatevalue(mrotatebar.getprogress()); if (ssurfaceholder != null) { log.d(tag, "sending previous surface"); rh.sendsurfaceavailable(ssurfaceholder, false); } else { log.d(tag, "no previous surface"); } log.d(tag, "onresume end"); } @override protected void onpause() { log.d(tag, "onpause begin"); super.onpause(); renderhandler rh = mrenderthread.gethandler(); rh.sendshutdown(); try { mrenderthread.join(); } catch (interruptedexception ie) { // not expected throw new runtimeexception("join interrupted", ie); } mrenderthread = null; log.d(tag, "onpause end"); } @override // surfaceholder.callback public void surfacecreated(surfaceholder holder) { log.d(tag, "surfacecreated holder=" + holder + " (static=" + ssurfaceholder + ")"); if (ssurfaceholder != null) { throw new runtimeexception("ssurfaceholder set"); } ssurfaceholder = holder; if (mrenderthread != null) { // normal case -- render thread running, tell new surface. renderhandler rh = mrenderthread.gethandler(); rh.sendsurfaceavailable(holder, true); } else { // see on 4.4.x n5: power off, power on, unlock, device in // landscape , lock screen requires portrait. surface-created // message showing after onpause(). // // chances surface destroyed before activity // unpaused, track anyway. if activity un-paused , start // renderthread, surfaceholder passed in right after thread // created. log.d(tag, "render thread not running"); } recorder.setpreviewdisplay(holder.getsurface()); } @override // surfaceholder.callback public void surfacechanged(surfaceholder holder, int format, int width, int height) { log.d(tag, "surfacechanged fmt=" + format + " size=" + width + "x" + height + " holder=" + holder); if (mrenderthread != null) { renderhandler rh = mrenderthread.gethandler(); rh.sendsurfacechanged(format, width, height); } else { log.d(tag, "ignoring surfacechanged"); return; } } @override // surfaceholder.callback public void surfacedestroyed(surfaceholder holder) { // in theory should tell renderthread surface has been destroyed. if (mrenderthread != null) { renderhandler rh = mrenderthread.gethandler(); rh.sendsurfacedestroyed(); } log.d(tag, "surfacedestroyed holder=" + holder); ssurfaceholder = null; } @override // seekbar.onseekbarchangelistener public void onprogresschanged(seekbar seekbar, int progress, boolean fromuser) { if (mrenderthread == null) { // happen if programmatically update values after setting listener // before starting thread. also, easy cause scrubbing seek // bar 1 finger tapping "recents" another. log.w(tag, "ignoring onprogresschanged received w/o rt running"); return; } renderhandler rh = mrenderthread.gethandler(); // "progress" ranges 0 100 if (seekbar == mzoombar) { //log.v(tag, "zoom: " + progress); rh.sendzoomvalue(progress); } else if (seekbar == msizebar) { //log.v(tag, "size: " + progress); rh.sendsizevalue(progress); } else if (seekbar == mrotatebar) { //log.v(tag, "rotate: " + progress); rh.sendrotatevalue(progress); } else { throw new runtimeexception("unknown seek bar"); } // if we're getting preview frames enough don't need this, // don't want have chunky-looking resize movement if camera slow. // otoh, if updates (60fps camera?), jam // , cause run behind. use caution. rh.sendredraw(); } @override // seekbar.onseekbarchangelistener public void onstarttrackingtouch(seekbar seekbar) {} @override // seekbar.onseekbarchangelistener public void onstoptrackingtouch(seekbar seekbar) {} @override /** * handles touch events aren't grabbed 1 of controls. */ public boolean ontouchevent(motionevent e) { float x = e.getx(); float y = e.gety(); switch (e.getaction()) { case motionevent.action_move: case motionevent.action_down: //log.v(tag, "ontouchevent act=" + e.getaction() + " x=" + x + " y=" + y); if (mrenderthread != null) { renderhandler rh = mrenderthread.gethandler(); rh.sendposition((int) x, (int) y); // forcing redraw can cause sluggish-looking behavior if touch // events arrive quickly. //rh.sendredraw(); } break; default: break; } return true; } /** * updates current state of controls. */ private void updatecontrols() { string str = getstring(r.string.tfccameraparams, mcamerapreviewwidth, mcamerapreviewheight, mcamerapreviewfps); textview tv = (textview) findviewbyid(r.id.tfccameraparams_text); tv.settext(str); str = getstring(r.string.tfcrectsize, mrectwidth, mrectheight); tv = (textview) findviewbyid(r.id.tfcrectsize_text); tv.settext(str); str = getstring(r.string.tfczoomarea, mzoomwidth, mzoomheight); tv = (textview) findviewbyid(r.id.tfczoomarea_text); tv.settext(str); } @override public void onclick(view view) { if (recording) { recorder.stop(); recording = false; // let's initrecorder can record again initrecorder(); preparerecorder(); } else { recording = true; recorder.start(); } } private void initrecorder() { recorder.setaudiosource(mediarecorder.audiosource.default); recorder.setvideosource(mediarecorder.videosource.default); camcorderprofile cphigh = camcorderprofile .get(camcorderprofile.quality_high); recorder.setprofile(cphigh); string path = environment.getexternalstoragedirectory() + file.separator + environment.directory_dcim + file.separator + "alpharun"; recorder.setoutputfile(path); recorder.setmaxduration(50000); // 50 seconds recorder.setmaxfilesize(5000000); // approximately 5 megabytes } private void preparerecorder() { try { recorder.prepare(); } catch (illegalstateexception e) { e.printstacktrace(); finish(); } catch (ioexception e) { e.printstacktrace(); finish(); } } /** * thread handles rendering , camera operations. */ private static class renderthread extends thread implements surfacetexture.onframeavailablelistener { // object must created on render thread correct looper, used // ui thread, need declare volatile ensure ui thread sees // constructed object. private volatile renderhandler mhandler; // used wait thread start. private object mstartlock = new object(); private boolean mready = false; private mainhandler mmainhandler; private camera mcamera; private int mcamerapreviewwidth, mcamerapreviewheight; private eglcore meglcore; private windowsurface mwindowsurface; private int mwindowsurfacewidth; private int mwindowsurfaceheight; // receives output camera preview. private surfacetexture mcameratexture; // orthographic projection matrix. private float[] mdisplayprojectionmatrix = new float[16]; private texture2dprogram mtexprogram; private final scaleddrawable2d mrectdrawable = new scaleddrawable2d(drawable2d.prefab.rectangle); private final sprite2d mrect = new sprite2d(mrectdrawable); private int mzoompercent = default_zoom_percent; private int msizepercent = default_size_percent; private int mrotatepercent = default_rotate_percent; private float mposx, mposy; /** * constructor. pass in mainhandler, allows send stuff * activity. */ public renderthread(mainhandler handler) { mmainhandler = handler; } /** * thread entry point. */ @override public void run() { looper.prepare(); // need create handler before reporting ready. mhandler = new renderhandler(this); synchronized (mstartlock) { mready = true; mstartlock.notify(); // signal waituntilready() } // prepare egl , open camera before start handling messages. meglcore = new eglcore(null, 0); opencamera(req_camera_width, req_camera_height, req_camera_fps); looper.loop(); log.d(tag, "looper quit"); releasecamera(); releasegl(); meglcore.release(); synchronized (mstartlock) { mready = false; } } /** * waits until render thread ready receive messages. * <p> * call ui thread. */ public void waituntilready() { synchronized (mstartlock) { while (!mready) { try { mstartlock.wait(); } catch (interruptedexception ie) { /* not expected */ } } } } /** * shuts down. */ private void shutdown() { log.d(tag, "shutdown"); looper.mylooper().quit(); } /** * returns render thread's handler. may called thread. */ public renderhandler gethandler() { return mhandler; } /** * handles surface-created callback surfaceview. prepares gles , surface. */ private void surfaceavailable(surfaceholder holder, boolean newsurface) { surface surface = holder.getsurface(); mwindowsurface = new windowsurface(meglcore, surface, false); mwindowsurface.makecurrent(); // create , configure surfacetexture, receive frames // camera. set textured rect's program render it. mtexprogram = new texture2dprogram(texture2dprogram.programtype.texture_ext); int textureid = mtexprogram.createtextureobject(); mcameratexture = new surfacetexture(textureid); mrect.settexture(textureid); if (!newsurface) { // surface established on previous run, no surfacechanged() // message forthcoming. finish surface setup now. // // call unconditionally, , perhaps unnecessary // bit of reallocating if surface-changed message arrives. mwindowsurfacewidth = mwindowsurface.getwidth(); mwindowsurfaceheight = mwindowsurface.getwidth(); finishsurfacesetup(); } mcameratexture.setonframeavailablelistener(this); } /** * releases of gl resources hold (anything allocated * surfaceavailable()). * <p> * not release eglcore. */ private void releasegl() { glutil.checkglerror("releasegl start"); if (mwindowsurface != null) { mwindowsurface.release(); mwindowsurface = null; } if (mtexprogram != null) { mtexprogram.release(); mtexprogram = null; } glutil.checkglerror("releasegl done"); meglcore.makenothingcurrent(); } /** * handles surfacechanged message. * <p> * receive surfacechanged() after surfacecreated(), surfaceavailable() * called surface created on previous run. may not * called. */ private void surfacechanged(int width, int height) { log.d(tag, "renderthread surfacechanged " + width + "x" + height); mwindowsurfacewidth = width; mwindowsurfaceheight = width; finishsurfacesetup(); } /** * handles surfacedestroyed message. */ private void surfacedestroyed() { // in practice never appears called -- activity paused // before surface destroyed. in theory called though. log.d(tag, "renderthread surfacedestroyed"); releasegl(); } /** * sets depends on window size. * <p> * open camera (to set mcameraaspectratio) before calling here. */ private void finishsurfacesetup() { int width = mwindowsurfacewidth; int height = mwindowsurfaceheight; log.d(tag, "finishsurfacesetup size=" + width + "x" + height + " camera=" + mcamerapreviewwidth + "x" + mcamerapreviewheight); // use full window. gles20.glviewport(0, 700, width, height); // simple orthographic projection, (0,0) in lower-left corner. matrix.orthom(mdisplayprojectionmatrix, 0, 0, width, 0, height, -1, 1); // default position center of screen. mposx = width / 2.0f; mposy = height / 2.0f; updategeometry(); // ready go, start camera. log.d(tag, "starting camera preview"); try { mcamera.setpreviewtexture(mcameratexture); } catch (ioexception ioe) { throw new runtimeexception(ioe); } mcamera.startpreview(); } /** * updates geometry of mrect, based on size of window , current * values set ui. */ private void updategeometry() { int width = mwindowsurfacewidth; int height = mwindowsurfaceheight; int smalldim = math.min(width, height); // max scale bit larger screen, can show over-size. float scaled = smalldim * (msizepercent / 100.0f) * 1.25f; float cameraaspect = (float) mcamerapreviewwidth / mcamerapreviewheight; int newwidth = math.round(scaled * cameraaspect); int newheight = math.round(scaled); float zoomfactor = 1.0f - (mzoompercent / 100.0f); int rotangle = math.round(360 * (mrotatepercent / 100.0f)); mrect.setscale(newwidth, newheight); mrect.setposition(mposx, mposy); mrect.setrotation(rotangle); mrectdrawable.setscale(zoomfactor); mmainhandler.sendrectsize(newwidth, newheight); mmainhandler.sendzoomarea(math.round(mcamerapreviewwidth * zoomfactor), math.round(mcamerapreviewheight * zoomfactor)); mmainhandler.sendrotatedeg(rotangle); } @override // surfacetexture.onframeavailablelistener; runs on arbitrary thread public void onframeavailable(surfacetexture surfacetexture) { mhandler.sendframeavailable(); } /** * handles incoming frame of data camera. */ private void frameavailable() { mcameratexture.updateteximage(); draw(); } /** * draws scene , submits buffer. */ private void draw() { glutil.checkglerror("draw start"); gles20.glclearcolor(0.0f, 0.0f, 0.0f, 1.0f); gles20.glclear(gles20.gl_color_buffer_bit); mrect.draw(mtexprogram, mdisplayprojectionmatrix); mwindowsurface.swapbuffers(); glutil.checkglerror("draw done"); } /** * opens camera, , attempts establish preview mode @ specified width * , height fixed frame rate. * <p> * sets mcamerapreviewwidth / mcamerapreviewheight. */ private void opencamera(int desiredwidth, int desiredheight, int desiredfps) { if (mcamera != null) { throw new runtimeexception("camera initialized"); } camera.camerainfo info = new camera.camerainfo(); // try find front-facing camera (e.g. videoconferencing). int numcameras = camera.getnumberofcameras(); (int = 0; < numcameras; i++) { camera.getcamerainfo(i, info); if (info.facing == camera.camerainfo.camera_facing_back) { mcamera = camera.open(i); break; } } if (mcamera == null) { log.d(tag, "no front-facing camera found; opening default"); mcamera = camera.open(); // opens first back-facing camera } if (mcamera == null) { throw new runtimeexception("unable open camera"); } camera.parameters parms = mcamera.getparameters(); camerautils.choosepreviewsize(parms, desiredwidth, desiredheight); parms.setfocusmode(camera.parameters.focus_mode_continuous_picture); // try set frame rate constant value. int thousandfps = camerautils.choosefixedpreviewfps(parms, desiredfps * 1000); // give camera hint we're recording video. can have big // impact on frame rate. parms.setrecordinghint(true); mcamera.setparameters(parms); int[] fpsrange = new int[2]; camera.size mcamerapreviewsize = parms.getpreviewsize(); parms.getpreviewfpsrange(fpsrange); string previewfacts = mcamerapreviewsize.width + "x" + mcamerapreviewsize.height; if (fpsrange[0] == fpsrange[1]) { previewfacts += " @" + (fpsrange[0] / 1000.0) + "fps"; } else { previewfacts += " @[" + (fpsrange[0] / 1000.0) + " - " + (fpsrange[1] / 1000.0) + "] fps"; } log.i(tag, "camera config: " + previewfacts); mcamerapreviewwidth = mcamerapreviewsize.width; mcamerapreviewheight = mcamerapreviewsize.height; mmainhandler.sendcameraparams(mcamerapreviewwidth, mcamerapreviewheight, thousandfps / 1000.0f); } /** * stops camera preview, , releases camera system. */ private void releasecamera() { if (mcamera != null) { mcamera.stoppreview(); mcamera.release(); mcamera = null; log.d(tag, "releasecamera -- done"); } } } }
Comments
Post a Comment