android:SurfaceTexure,相机帧等待超时 [英] android: SurfaceTexure, camera frame wait time out

查看:32
本文介绍了android:SurfaceTexure,相机帧等待超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 MediaCodec 和 MediaMux,但遇到了一些麻烦.

I'm trying to use MediaCodec and MediaMux, and I meet some trouble.

这是来自 logcat 的错误:

Here is the errors from the logcat:

12-13 11:59:58.238: E/AndroidRuntime(23218): FATAL EXCEPTION: main
12-13 11:59:58.238: E/AndroidRuntime(23218): java.lang.RuntimeException: Unable to resume activity {com.brendon.cameratompeg/com.brendon.cameratompeg.CameraToMpeg}: java.lang.IllegalStateException: Can't stop due to wrong state.
12-13 11:59:58.238: E/AndroidRuntime(23218):    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2918)

代码在 onResume 函数中的mStManager.awaitNewImage();"处出错.logcat 显示相机帧等待超时".
mStManager 是 SurfaceTextureManager 类的一个实例.相机帧等待超时"来自 awaitNewImage() 函数.我已将该课程添加到我的帖子中.

The code get wrong at "mStManager.awaitNewImage();", which is in the onResume function. And the logcat says "camera frame wait time out".
mStManager is an instance of the class SurfaceTextureManager. And "camera frame wait time out" comes from the awaitNewImage() function. I've added that class to my post.

我的部分代码是这样的(onCreate函数和onResume函数):

Part of my code is like this(The onCreate function and onResume function):

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        // arbitrary but popular values
        int encWidth = 640;
        int encHeight = 480;
        int encBitRate = 6000000;      // Mbps
        Log.d(TAG, MIME_TYPE + " output " + encWidth + "x" + encHeight + " @" + encBitRate);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_to_mpeg);

           prepareCamera(encWidth, encHeight);
           prepareEncoder(encWidth, encHeight, encBitRate);
           mInputSurface.makeCurrent();
           prepareSurfaceTexture();

           mCamera.startPreview();         
}


@Override
public void onResume(){

    try {

         long startWhen = System.nanoTime();
           long desiredEnd = startWhen + DURATION_SEC * 1000000000L;
           SurfaceTexture st = mStManager.getSurfaceTexture();
           int frameCount = 0;

        while (System.nanoTime() < desiredEnd) {
            // Feed any pending encoder output into the muxer.
            drainEncoder(false);

            // Switch up the colors every 15 frames.  Besides demonstrating the use of
            // fragment shaders for video editing, this provides a visual indication of
            // the frame rate: if the camera is capturing at 15fps, the colors will change
            // once per second.
            if ((frameCount % 15) == 0) {
                String fragmentShader = null;
                if ((frameCount & 0x01) != 0) {
                    fragmentShader = SWAPPED_FRAGMENT_SHADER;
                }
                mStManager.changeFragmentShader(fragmentShader);
            }
            frameCount++;

            // Acquire a new frame of input, and render it to the Surface.  If we had a
            // GLSurfaceView we could switch EGL contexts and call drawImage() a second
            // time to render it on screen.  The texture can be shared between contexts by
            // passing the GLSurfaceView's EGLContext as eglCreateContext()'s share_context
            // argument.
            mStManager.awaitNewImage();
            mStManager.drawImage();

            // Set the presentation time stamp from the SurfaceTexture's time stamp.  This
            // will be used by MediaMuxer to set the PTS in the video.
            if (VERBOSE) {
                Log.d(TAG, "present: " +
                        ((st.getTimestamp() - startWhen) / 1000000.0) + "ms");
            }
            mInputSurface.setPresentationTime(st.getTimestamp());

            // Submit it to the encoder.  The eglSwapBuffers call will block if the input
            // is full, which would be bad if it stayed full until we dequeued an output
            // buffer (which we can't do, since we're stuck here).  So long as we fully drain
            // the encoder before supplying additional input, the system guarantees that we
            // can supply another frame without blocking.
            if (VERBOSE) Log.d(TAG, "sending frame to encoder");
            mInputSurface.swapBuffers();
        }

        // send end-of-stream to encoder, and drain remaining output
        drainEncoder(true);
    } catch(Exception e) {
        Log.d(TAG,  e.getMessage());
        // release everything we grabbed
        releaseCamera();
        releaseEncoder();
        releaseSurfaceTexture();
    }
}

代码中与错误相关的类

 private static class SurfaceTextureManager
            implements SurfaceTexture.OnFrameAvailableListener {
        private SurfaceTexture mSurfaceTexture;
        private CameraToMpeg.STextureRender mTextureRender;

        private Object mFrameSyncObject = new Object();     // guards mFrameAvailable
        private boolean mFrameAvailable;

        /**
         * Creates instances of TextureRender and SurfaceTexture.
         */
        public SurfaceTextureManager() {
            mTextureRender = new CameraToMpeg.STextureRender();
            mTextureRender.surfaceCreated();

            if (VERBOSE) Log.d(TAG, "textureID=" + mTextureRender.getTextureId());
            mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId());

            // This doesn't work if this object is created on the thread that CTS started for
            // these test cases.
            //
            // The CTS-created thread has a Looper, and the SurfaceTexture constructor will
            // create a Handler that uses it.  The "frame available" message is delivered
            // there, but since we're not a Looper-based thread we'll never see it.  For
            // this to do anything useful, OutputSurface must be created on a thread without
            // a Looper, so that SurfaceTexture uses the main application Looper instead.
            //
            // Java language note: passing "this" out of a constructor is generally unwise,
            // but we should be able to get away with it here.
            mSurfaceTexture.setOnFrameAvailableListener(this);
        }

        public void release() {
            // this causes a bunch of warnings that appear harmless but might confuse someone:
            //  W BufferQueue: [unnamed-3997-2] cancelBuffer: BufferQueue has been abandoned!
            //mSurfaceTexture.release();

            mTextureRender = null;
            mSurfaceTexture = null;
        }

        /**
         * Returns the SurfaceTexture.
         */
        public SurfaceTexture getSurfaceTexture() {
            return mSurfaceTexture;
        }

        /**
         * Replaces the fragment shader.
         */
        public void changeFragmentShader(String fragmentShader) {
            mTextureRender.changeFragmentShader(fragmentShader);
        }

        /**
         * Latches the next buffer into the texture.  Must be called from the thread that created
         * the OutputSurface object.
         */
        public void awaitNewImage() {
            final int TIMEOUT_MS = 2500;

            synchronized (mFrameSyncObject) {
                while (!mFrameAvailable) {
                    try {
                        // Wait for onFrameAvailable() to signal us.  Use a timeout to avoid
                        // stalling the test if it doesn't arrive.
                        mFrameSyncObject.wait(TIMEOUT_MS);
                        if (!mFrameAvailable) {
                            // TODO: if "spurious wakeup", continue while loop
                            throw new RuntimeException("Camera frame wait timed out");
                        }
                    } catch (InterruptedException ie) {
                        // shouldn't happen
                        throw new RuntimeException(ie);
                    }
                }
                mFrameAvailable = false;
            }

            // Latch the data.
            mTextureRender.checkGlError("before updateTexImage");
            mSurfaceTexture.updateTexImage();
        }

        /**
         * Draws the data from SurfaceTexture onto the current EGL surface.
         */
        public void drawImage() {
            mTextureRender.drawFrame(mSurfaceTexture);
        }

        @Override
        public void onFrameAvailable(SurfaceTexture st) {
            if (VERBOSE) Log.d(TAG, "new frame available");
            synchronized (mFrameSyncObject) {
                if (mFrameAvailable) {
                    throw new RuntimeException("mFrameAvailable already set, frame could be dropped");
                }
                mFrameAvailable = true;
                mFrameSyncObject.notifyAll();
            }
        }
    }

有人有什么想法吗?谢谢!

Does anyone have any ideas? Thank you!

推荐答案

我也遇到了这个问题.因此,原因是您的代码在具有循环程序的线程上运行.您必须确保代码在没有循环程序的线程上运行.如果是这样,SurfaceTexture.OnFrameAvailableListener 会将帧可用"消息传递给等待线程,而不是将消息发送到主线程上的处理程序,您会卡住.

I encountered this issue as well. The reason therefore is that your code is running on a thread that has a looper. You have to make sure that the code is running on a thread that does not have a looper. If it does, SurfaceTexture.OnFrameAvailableListener will deliver the "frame available" message to the waiting thread, rather than sending the Message to the Handler on the main thread, and you'll get stuck.

Bigflake 的示例为您提供了详细说明:

Bigflake's examples provide you with a detailed description on that:

/**
 * Wraps testEditVideo, running it in a new thread.  Required because of the way
 * SurfaceTexture.OnFrameAvailableListener works when the current thread has a Looper
 * configured.
 */
private static class VideoEditWrapper implements Runnable {
    private Throwable mThrowable;
    private DecodeEditEncodeTest mTest;
    private VideoEditWrapper(DecodeEditEncodeTest test) {
        mTest = test;
    }
    @Override
    public void run() {
        try {
            mTest.videoEditTest();
        } catch (Throwable th) {
            mThrowable = th;
        }
    }
    /** Entry point. */
    public static void runTest(DecodeEditEncodeTest obj) throws Throwable {
        VideoEditWrapper wrapper = new VideoEditWrapper(obj);
        Thread th = new Thread(wrapper, "codec test");
        th.start();
        th.join();
        if (wrapper.mThrowable != null) {
            throw wrapper.mThrowable;
        }
    }
}

这篇关于android:SurfaceTexure,相机帧等待超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆