android - Encode bitmaps to movie using MediaCodec and MediaMuxer with custom presentation time for each image -
i trying create movie series of bitmaps. target api 19 take advantage of api 21 if available on device.
i have been reading bigflake cts tests encodeandmux , encodedecodetest here
i use mediacodec , mediamuxer classes , not ffmpeg or jcodec etc.
i appreciate pointing out going wrong , how fix it. new android , struggling days! testing on samsung galaxy note 3
update: able reach eos codec error when trying release encoder.
here updated code:
public void createmovie(view view) { log.v(tag,"creating movie"); //1. prepare encoder , gpuimageview try { prepareencoder(); presentationtime = 0; int j = 0; (int = firstimageindex; <= lastimageindex; i++) { log.v(tag, "inloop: " + i); //1 durationinnanosec = (long) ((float) durations.get(j) * 100000); //get image int imageid = imageids[i]; uri imageuri = uri.withappendedpath(mediastore.images.media.external_content_uri, "" + imageid); imageview.setimage(imageuri); imageview.setfilter(filter); //run in background thread new kasynctask<void, void, byte[]>() { @override protected void onpreexecute() { // todo auto-generated method stub } @override protected void onpostexecute(byte[] result) { // todo auto-generated method stub //super.onpostexecute(result); log.v(tag, "converted bitmap nv21"); bytebuffer inputbuffer; int inputbytebufferindex = mencoder.dequeueinputbuffer(waitime); if (currentapiversion >= build.version_codes.lollipop) { inputbuffer = mencoder.getinputbuffer(inputbytebufferindex); } else { // phones running sdk before lollipop bytebuffer[] inputbuffers = mencoder.getinputbuffers(); inputbuffer = inputbuffers[inputbytebufferindex]; } inputbuffer.put(result); mencoder.queueinputbuffer(inputbytebufferindex, 0, inputbuffer.capacity(), presentationtime, mediacodec.buffer_flag_codec_config); presentationtime += durationinnanosec; log.v(tag, "presentationtime: " + presentationtime); } @override protected byte[] doinbackground(void... params) { // todo auto-generated method stub //get bitmap filter applied bitmap bmp = imageview.getgpuimage().getbitmapwithfilterapplied(); log.v(tag, "converting bitmap nv21"); byte[] bytes = getnv21(mwidth, mheight, bmp); return bytes; } }.setcontext(this).execute(); j++; if (i == lastimageindex) break; drainencoder(false); } drainencoder(true); } catch(exception e) { log.v(tag, "exception", e); } { // release encoder, muxer, , input surface releaseencoder(); log.v(tag,"video created"); toast.maketext(this, "video created!", toast.length_long).show(); } } private void prepareencoder() { mbufferinfo = new mediacodec.bufferinfo(); mediaformat format = mediaformat.createvideoformat(mime_type, mwidth, mheight); // set properties. failing specify of these can cause mediacodec // configure() call throw unhelpful exception. format.setinteger(mediaformat.key_color_format, mediacodecinfo.codeccapabilities.color_formatsurface); format.setinteger(mediaformat.key_bit_rate, 2000000); format.setinteger(mediaformat.key_frame_rate, frame_rate); format.setinteger(mediaformat.key_i_frame_interval, iframe_interval); log.v(tag, "format: " + format); // create mediacodec encoder, , configure our format. surface // can use input , wrap class handles egl work. // // if want have 2 egl contexts -- 1 display, 1 recording -- // want defer instantiation of codecinputsurface until after // "display" egl context created, modify eglcreatecontext call // take eglgetcurrentcontext() share_context argument. try { mencoder = mediacodec.createencoderbytype(mime_type); } catch (ioexception e) { e.printstacktrace(); } mencoder.configure(format, null, null, mediacodec.configure_flag_encode); minputsurface = new codecinputsurface(mencoder.createinputsurface()); mencoder.start(); // output filename. ideally use context.getfilesdir() rather // hard-coded output directory. string outputpath = new file(output_dir, "test." + mwidth + "x" + mheight + ".mp4").tostring(); log.d(tag, "output file " + outputpath); // create mediamuxer. can't add video track , start() muxer here, // because our mediaformat doesn't have magic goodies. these can // obtained encoder after has started processing data. // // we're not interested in multiplexing audio. want convert // raw h.264 elementary stream mediacodec .mp4 file. try { mmuxer = new mediamuxer(outputpath, mediamuxer.outputformat.muxer_output_mpeg_4); } catch (ioexception ioe) { throw new runtimeexception("mediamuxer creation failed", ioe); } mtrackindex = -1; mmuxerstarted = false; } /** * releases encoder resources. may called after partial / failed initialization. */ private void releaseencoder() { log.v(tag, "releasing encoder objects"); if (mencoder != null) { mencoder.stop(); mencoder.release(); mencoder = null; } if (minputsurface != null) { minputsurface.release(); minputsurface = null; } if (mmuxer != null) { mmuxer.stop(); mmuxer.release(); mmuxer = null; } } /** * extracts pending data encoder. * <p/> * if endofstream not set, returns when there no more data drain. if * set, send eos encoder, , iterate until see eos on output. * calling endofstream set should done once, right before stopping muxer. */ public void drainencoder(boolean endofstream) { final int timeout_usec = 10000; log.v(tag, "drainencoder(" + endofstream + ")"); if (endofstream) { log.v(tag, "sending eos encoder"); mencoder.signalendofinputstream(); } bytebuffer[] encoderoutputbuffers = null; while (true) { int encoderstatus = mencoder.dequeueoutputbuffer(mbufferinfo, timeout_usec); if (encoderstatus == mediacodec.info_try_again_later) { // no output available yet if (!endofstream) { break; // out of while } else { log.v(tag, "no output available, spinning await eos"); } } else if (currentapiversion < build.version_codes.lollipop) { if(encoderstatus == mediacodec.info_output_buffers_changed) { // not expected encoder encoderoutputbuffers = mencoder.getoutputbuffers(); } } else if (encoderstatus == mediacodec.info_output_format_changed) { // should happen before receiving buffers, , should happen once if (mmuxerstarted) { throw new runtimeexception("format changed twice"); } mediaformat newformat = mencoder.getoutputformat(); log.v(tag, "encoder output format changed: " + newformat); // have magic goodies, start muxer mtrackindex = mmuxer.addtrack(newformat); mmuxer.start(); mmuxerstarted = true; } else if (encoderstatus < 0) { log.v(tag, "unexpected result encoder.dequeueoutputbuffer: " + encoderstatus); // let's ignore } else { bytebuffer encodeddata = null; if (currentapiversion >= build.version_codes.lollipop) { encodeddata = mencoder.getoutputbuffer(encoderstatus); }else{ encodeddata = encoderoutputbuffers[encoderstatus]; } if (encodeddata == null) { throw new runtimeexception("encoderoutputbuffer " + encoderstatus + " null"); } if ((mbufferinfo.flags & mediacodec.buffer_flag_codec_config) != 0) { // codec config data pulled out , fed muxer when got // info_output_format_changed status. ignore it. log.v(tag, "ignoring buffer_flag_codec_config"); mbufferinfo.size = 0; } if (mbufferinfo.size != 0) { if (!mmuxerstarted) { throw new runtimeexception("muxer hasn't started"); } // adjust bytebuffer values match bufferinfo (not needed?) encodeddata.position(mbufferinfo.offset); encodeddata.limit(mbufferinfo.offset + mbufferinfo.size); mmuxer.writesampledata(mtrackindex, encodeddata, mbufferinfo); log.d(tag, "sent " + mbufferinfo.size + " bytes muxer, ts=" + mbufferinfo.presentationtimeus); } mencoder.releaseoutputbuffer(encoderstatus, false); if ((mbufferinfo.flags & mediacodec.buffer_flag_end_of_stream) != 0) { if (!endofstream) { log.v(tag, "reached end of stream unexpectedly"); } else { log.v(tag, "end of stream reached"); } break; // out of while } } } }
and updated logcat error:
04-30 09:55:55.242 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ drainencoder(false) 04-30 09:55:55.252 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ inloop: 55 04-30 09:55:55.257 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ drainencoder(false) 04-30 09:55:55.267 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ inloop: 56 04-30 09:55:55.267 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ drainencoder(false) 04-30 09:55:55.277 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ inloop: 57 04-30 09:55:55.277 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ drainencoder(false) 04-30 09:55:55.287 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ inloop: 58 04-30 09:55:55.287 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ drainencoder(true) 04-30 09:55:55.287 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ sending eos encoder 04-30 09:55:55.292 25209-25724/com.example.andreaskaitis.myapplication e/acodec﹕ [omx.exynos.avc.encoder] error(0x80001006) 04-30 09:55:55.292 25209-25722/com.example.andreaskaitis.myapplication e/mediacodec﹕ codec reported error. (omx error 0x80001006, internalerror -2147483648) 04-30 09:55:55.297 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ exception java.lang.illegalstateexception @ android.media.mediacodec.dequeueoutputbuffer(native method) @ com.example.andreaskaitis.myapplication.writemovieactivity.drainencoder(writemovieactivity.java:390) @ com.example.andreaskaitis.myapplication.writemovieactivity.createmovie(writemovieactivity.java:285) @ java.lang.reflect.method.invokenative(native method) @ java.lang.reflect.method.invoke(method.java:515) @ android.view.view$1.onclick(view.java:3964) @ android.view.view.performclick(view.java:4630) @ android.view.view$performclick.run(view.java:19339) @ android.os.handler.handlecallback(handler.java:733) @ android.os.handler.dispatchmessage(handler.java:95) @ android.os.looper.loop(looper.java:157) @ android.app.activitythread.main(activitythread.java:5335) @ java.lang.reflect.method.invokenative(native method) @ java.lang.reflect.method.invoke(method.java:515) @ com.android.internal.os.zygoteinit$methodandargscaller.run(zygoteinit.java:1265) @ com.android.internal.os.zygoteinit.main(zygoteinit.java:1081) @ dalvik.system.nativestart.main(native method) 04-30 09:55:55.297 25209-25209/com.example.andreaskaitis.myapplication v/writemovie﹕ releasing encoder objects 04-30 09:55:55.297 25209-25724/com.example.andreaskaitis.myapplication i/acodec﹕ [omx.exynos.avc.encoder] executing->idle 04-30 09:55:55.312 25209-25724/com.example.andreaskaitis.myapplication i/acodec﹕ [omx.exynos.avc.encoder] idle->loaded 04-30 09:55:55.312 25209-25724/com.example.andreaskaitis.myapplication i/acodec﹕ [omx.exynos.avc.encoder] loaded 04-30 09:55:55.312 25209-25724/com.example.andreaskaitis.myapplication i/acodec﹕ [omx.exynos.avc.encoder] uninitialized 04-30 09:55:55.317 25209-25209/com.example.andreaskaitis.myapplication d/androidruntime﹕ shutting down vm 04-30 09:55:55.317 25209-25209/com.example.andreaskaitis.myapplication w/dalvikvm﹕ threadid=1: thread exiting uncaught exception (group=0x41f84c08) 04-30 09:55:55.322 25209-25209/com.example.andreaskaitis.myapplication e/androidruntime﹕ fatal exception: main process: com.example.andreaskaitis.myapplication, pid: 25209 java.lang.illegalstateexception: not execute method of activity @ android.view.view$1.onclick(view.java:3969) @ android.view.view.performclick(view.java:4630) @ android.view.view$performclick.run(view.java:19339) @ android.os.handler.handlecallback(handler.java:733) @ android.os.handler.dispatchmessage(handler.java:95) @ android.os.looper.loop(looper.java:157) @ android.app.activitythread.main(activitythread.java:5335) @ java.lang.reflect.method.invokenative(native method) @ java.lang.reflect.method.invoke(method.java:515) @ com.android.internal.os.zygoteinit$methodandargscaller.run(zygoteinit.java:1265) @ com.android.internal.os.zygoteinit.main(zygoteinit.java:1081) @ dalvik.system.nativestart.main(native method) caused by: java.lang.reflect.invocationtargetexception @ java.lang.reflect.method.invokenative(native method) @ java.lang.reflect.method.invoke(method.java:515) @ android.view.view$1.onclick(view.java:3964) at android.view.view.performclick(view.java:4630) at android.view.view$performclick.run(view.java:19339) at android.os.handler.handlecallback(handler.java:733) at android.os.handler.dispatchmessage(handler.java:95) at android.os.looper.loop(looper.java:157) at android.app.activitythread.main(activitythread.java:5335) at java.lang.reflect.method.invokenative(native method) at java.lang.reflect.method.invoke(method.java:515) at com.android.internal.os.zygoteinit$methodandargscaller.run(zygoteinit.java:1265) at com.android.internal.os.zygoteinit.main(zygoteinit.java:1081) at dalvik.system.nativestart.main(native method) caused by: java.lang.illegalstateexception: can't stop due wrong state. @ android.media.mediamuxer.stop(mediamuxer.java:229) @ com.example.andreaskaitis.myapplication.writemovieactivity.releaseencoder(writemovieactivity.java:366) @ com.example.andreaskaitis.myapplication.writemovieactivity.createmovie(writemovieactivity.java:291) at java.lang.reflect.method.invokenative(native method) at java.lang.reflect.method.invoke(method.java:515) at android.view.view$1.onclick(view.java:3964) at android.view.view.performclick(view.java:4630) at android.view.view$performclick.run(view.java:19339) at android.os.handler.handlecallback(handler.java:733) at android.os.handler.dispatchmessage(handler.java:95) at android.os.looper.loop(looper.java:157) at android.app.activitythread.main(activitythread.java:5335) at java.lang.reflect.method.invokenative(native method) at java.lang.reflect.method.invoke(method.java:515) at com.android.internal.os.zygoteinit$methodandargscaller.run(zygoteinit.java:1265) at com.android.internal.os.zygoteinit.main(zygoteinit.java:1081) at dalvik.system.nativestart.main(native method)
Comments
Post a Comment