@Override public void onSurfaceCreated(GL10 unused, EGLConfig config) { Log.d(TAG, "onSurfaceCreated"); // We're starting up or coming back. Either way we've got a new EGLContext that will // need to be shared with the video encoder, so figure out if a recording is already // in progress. mRecordingEnabled = mVideoEncoder.isRecording(); if (mRecordingEnabled) { mRecordingStatus = RECORDING_RESUMED; } else { mRecordingStatus = RECORDING_OFF; } // Set up the texture blitter that will be used for on-screen display. This // is *not* applied to the recording, because that uses a separate shader. mFullScreen = new FullFrameRect(new Texture2dProgram(Texture2dProgram.ProgramType.TEXTURE_EXT)); mTextureId = mFullScreen.createTextureObject(); // Create a SurfaceTexture, with an external texture, in this EGL context. We don't // have a Looper in this thread -- GLSurfaceView doesn't create one -- so the frame // available messages will arrive on the main thread. mSurfaceTexture = new SurfaceTexture(mTextureId); // Tell the UI thread to enable the camera preview. mCameraHandler.sendMessage( mCameraHandler.obtainMessage( CameraCaptureActivity.CameraHandler.MSG_SET_SURFACE_TEXTURE, mSurfaceTexture)); }
/** * Notifies the renderer thread that the activity is pausing. * * <p>For best results, call this *after* disabling Camera preview. */ public void notifyPausing() { if (mSurfaceTexture != null) { Log.d(TAG, "renderer pausing -- releasing SurfaceTexture"); mSurfaceTexture.release(); mSurfaceTexture = null; } if (mFullScreen != null) { mFullScreen.release(false); // assume the GLSurfaceView EGL context is about mFullScreen = null; // to be destroyed } mIncomingWidth = mIncomingHeight = -1; }
@Override public void onDrawFrame(GL10 unused) { if (VERBOSE) Log.d(TAG, "onDrawFrame tex=" + mTextureId); boolean showBox = false; // Latch the latest frame. If there isn't anything new, we'll just re-use whatever // was there before. mSurfaceTexture.updateTexImage(); // If the recording state is changing, take care of it here. Ideally we wouldn't // be doing all this in onDrawFrame(), but the EGLContext sharing with GLSurfaceView // makes it hard to do elsewhere. if (mRecordingEnabled) { switch (mRecordingStatus) { case RECORDING_OFF: Log.d(TAG, "START recording"); // start recording mVideoEncoder.startRecording( new TextureMovieEncoder.EncoderConfig( mOutputFile, 480, 480, 1000000, EGL14.eglGetCurrentContext())); // TODO: get optimal width and height according to specified devices mRecordingStatus = RECORDING_ON; break; case RECORDING_RESUMED: Log.d(TAG, "RESUME recording"); mVideoEncoder.updateSharedContext(EGL14.eglGetCurrentContext()); mRecordingStatus = RECORDING_ON; break; case RECORDING_ON: // yay break; default: throw new RuntimeException("unknown status " + mRecordingStatus); } } else { switch (mRecordingStatus) { case RECORDING_ON: case RECORDING_RESUMED: // stop recording Log.d(TAG, "STOP recording"); mVideoEncoder.stopRecording(); mRecordingStatus = RECORDING_OFF; break; case RECORDING_OFF: // yay break; default: throw new RuntimeException("unknown status " + mRecordingStatus); } } // Set the video encoder's texture name. We only need to do this once, but in the // current implementation it has to happen after the video encoder is started, so // we just do it here. // // TODO: be less lame. mVideoEncoder.setTextureId(mTextureId); // Tell the video encoder thread that a new frame is available. // This will be ignored if we're not actually recording. mVideoEncoder.frameAvailable(mSurfaceTexture); if (mIncomingWidth <= 0 || mIncomingHeight <= 0) { // Texture size isn't set yet. This is only used for the filters, but to be // safe we can just skip drawing while we wait for the various races to resolve. // (This seems to happen if you toggle the screen off/on with power button.) Log.i(TAG, "Drawing before incoming texture size set; skipping"); return; } // Update the filter, if necessary. if (mCurrentFilter != mNewFilter) { updateFilter(); } if (mIncomingSizeUpdated) { mFullScreen.getProgram().setTexSize(mIncomingWidth, mIncomingHeight); mIncomingSizeUpdated = false; } // Draw the video frame. mSurfaceTexture.getTransformMatrix(mSTMatrix); mFullScreen.drawFrame(mTextureId, mSTMatrix); // Draw a flashing box if we're recording. This only appears on screen. showBox = (mRecordingStatus == RECORDING_ON); if (showBox && (++mFrameCount & 0x04) == 0) { drawBox(); } }
/** Updates the filter program. */ public void updateFilter() { Texture2dProgram.ProgramType programType; float[] kernel = null; float colorAdj = 0.0f; Log.d(TAG, "Updating filter to " + mNewFilter); switch (mNewFilter) { case CameraCaptureActivity.FILTER_NONE: programType = Texture2dProgram.ProgramType.TEXTURE_EXT; break; case CameraCaptureActivity.FILTER_BLACK_WHITE: // (In a previous version the TEXTURE_EXT_BW variant was enabled by a flag called // ROSE_COLORED_GLASSES, because the shader set the red channel to the B&W color // and green/blue to zero.) programType = Texture2dProgram.ProgramType.TEXTURE_EXT_BW; break; case CameraCaptureActivity.FILTER_BLUR: programType = Texture2dProgram.ProgramType.TEXTURE_EXT_FILT; kernel = new float[] { 1f / 16f, 2f / 16f, 1f / 16f, 2f / 16f, 4f / 16f, 2f / 16f, 1f / 16f, 2f / 16f, 1f / 16f }; break; case CameraCaptureActivity.FILTER_SHARPEN: programType = Texture2dProgram.ProgramType.TEXTURE_EXT_FILT; kernel = new float[] {0f, -1f, 0f, -1f, 5f, -1f, 0f, -1f, 0f}; break; case CameraCaptureActivity.FILTER_EDGE_DETECT: programType = Texture2dProgram.ProgramType.TEXTURE_EXT_FILT; kernel = new float[] { -1f, -1f, -1f, -1f, 8f, -1f, -1f, -1f, -1f }; break; case CameraCaptureActivity.FILTER_EMBOSS: programType = Texture2dProgram.ProgramType.TEXTURE_EXT_FILT; kernel = new float[] { 2f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, -1f }; colorAdj = 0.5f; break; default: throw new RuntimeException("Unknown filter mode " + mNewFilter); } // Do we need a whole new program? (We want to avoid doing this if we don't have // too -- compiling a program could be expensive.) if (programType != mFullScreen.getProgram().getProgramType()) { mFullScreen.changeProgram(new Texture2dProgram(programType)); // If we created a new program, we need to initialize the texture width/height. mIncomingSizeUpdated = true; } // Update the filter kernel (if any). if (kernel != null) { mFullScreen.getProgram().setKernel(kernel, colorAdj); } mCurrentFilter = mNewFilter; }