MediaEncoderFilter.java revision c1604923efb199d96ac78f68d5a211edebc404a5
184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi/* 284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * Copyright (C) 2011 The Android Open Source Project 384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * 484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * Licensed under the Apache License, Version 2.0 (the "License"); 584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * you may not use this file except in compliance with the License. 684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * You may obtain a copy of the License at 784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * 884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * http://www.apache.org/licenses/LICENSE-2.0 984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * 1084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * Unless required by applicable law or agreed to in writing, software 1184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * distributed under the License is distributed on an "AS IS" BASIS, 1284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * See the License for the specific language governing permissions and 1484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * limitations under the License. 1584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi */ 1684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 1784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 1884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketipackage android.filterpacks.videosink; 1984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 2084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.content.Context; 2184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.Filter; 2284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.FilterContext; 2384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.Frame; 2484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.FrameFormat; 2584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.FrameManager; 2684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.GenerateFieldPort; 2784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.GenerateFinalPort; 2884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.GLFrame; 2984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.KeyValueMap; 3084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.MutableFrameFormat; 3184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.NativeFrame; 3284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.Program; 3384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.ShaderProgram; 3484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.format.ImageFormat; 352102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvalaimport android.filterfw.geometry.Point; 362102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvalaimport android.filterfw.geometry.Quad; 3784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.os.ConditionVariable; 3884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.media.MediaRecorder; 397b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvalaimport android.media.CamcorderProfile; 4084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.filterfw.core.GLEnvironment; 4184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 4284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport java.io.IOException; 43e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketiimport java.io.FileDescriptor; 4484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport java.util.List; 4584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport java.util.Set; 4684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 4784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketiimport android.util.Log; 4884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 4984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi/** @hide */ 5084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketipublic class MediaEncoderFilter extends Filter { 5184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 5284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi /** User-visible parameters */ 5384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 547b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala /** Recording state. When set to false, recording will stop, or will not 557b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala * start if not yet running the graph. Instead, frames are simply ignored. 567b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala * When switched back to true, recording will restart. This allows a single 577b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala * graph to both provide preview and to record video. If this is false, 587b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala * recording settings can be updated while the graph is running. 597b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala */ 607b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala @GenerateFieldPort(name = "recording", hasDefault = true) 617b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private boolean mRecording = true; 627b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 637b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala /** Filename to save the output. */ 647b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala @GenerateFieldPort(name = "outputFile", hasDefault = true) 657b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private String mOutputFile = new String("/sdcard/MediaEncoderOut.mp4"); 667b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 67e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi /** File Descriptor to save the output. */ 68e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi @GenerateFieldPort(name = "outputFileDescriptor", hasDefault = true) 69e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi private FileDescriptor mFd = null; 70e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi 717b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala /** Input audio source. If not set, no audio will be recorded. 727b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala * Select from the values in MediaRecorder.AudioSource 737b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala */ 747b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala @GenerateFieldPort(name = "audioSource", hasDefault = true) 757b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private int mAudioSource = NO_AUDIO_SOURCE; 767b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 777b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala /** Media recorder info listener, which needs to implement 784aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni * MediaRecorder.OnInfoListener. Set this to receive notifications about 797b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala * recording events. 807b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala */ 817b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala @GenerateFieldPort(name = "infoListener", hasDefault = true) 827b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private MediaRecorder.OnInfoListener mInfoListener = null; 837b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 847b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala /** Media recorder error listener, which needs to implement 854aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni * MediaRecorder.OnErrorListener. Set this to receive notifications about 867b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala * recording errors. 877b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala */ 887b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala @GenerateFieldPort(name = "errorListener", hasDefault = true) 897b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private MediaRecorder.OnErrorListener mErrorListener = null; 907b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 914aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni /** Media recording done callback, which needs to implement OnRecordingDoneListener. 924aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni * Set this to finalize media upon completion of media recording. 934aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni */ 944aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni @GenerateFieldPort(name = "recordingDoneListener", hasDefault = true) 954aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni private OnRecordingDoneListener mRecordingDoneListener = null; 964aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni 977b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala /** Orientation hint. Used for indicating proper video playback orientation. 987b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala * Units are in degrees of clockwise rotation, valid values are (0, 90, 180, 997b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala * 270). 1007b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala */ 1017b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala @GenerateFieldPort(name = "orientationHint", hasDefault = true) 1027b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private int mOrientationHint = 0; 1037b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 1047b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala /** Camcorder profile to use. Select from the profiles available in 1057b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala * android.media.CamcorderProfile. If this field is set, it overrides 1067b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala * settings to width, height, framerate, outputFormat, and videoEncoder. 1077b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala */ 1087b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala @GenerateFieldPort(name = "recordingProfile", hasDefault = true) 1097b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private CamcorderProfile mProfile = null; 1107b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 1117b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala /** Frame width to be encoded, defaults to 320. 11284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * Actual received frame size has to match this */ 1137b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala @GenerateFieldPort(name = "width", hasDefault = true) 1147b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private int mWidth = 320; 11584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 1167b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala /** Frame height to to be encoded, defaults to 240. 11784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * Actual received frame size has to match */ 1187b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala @GenerateFieldPort(name = "height", hasDefault = true) 1197b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private int mHeight = 240; 12084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 12184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi /** Stream framerate to encode the frames at. 12284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * By default, frames are encoded at 30 FPS*/ 12384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @GenerateFieldPort(name = "framerate", hasDefault = true) 12484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private int mFps = 30; 12584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 12684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi /** The output format to encode the frames in. 12784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * Choose an output format from the options in 12884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * android.media.MediaRecorder.OutputFormat */ 12984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @GenerateFieldPort(name = "outputFormat", hasDefault = true) 130f283a907a88fc348b770186562c7a1046a8b7367Pannag Sanketi private int mOutputFormat = MediaRecorder.OutputFormat.MPEG_4; 13184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 13284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi /** The videoencoder to encode the frames with. 13384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * Choose a videoencoder from the options in 13484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi * android.media.MediaRecorder.VideoEncoder */ 13584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @GenerateFieldPort(name = "videoEncoder", hasDefault = true) 13684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private int mVideoEncoder = MediaRecorder.VideoEncoder.H264; 13784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 1382102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala /** The input region to read from the frame. The corners of this quad are 1392102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala * mapped to the output rectangle. The input frame ranges from (0,0)-(1,1), 1402102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala * top-left to bottom-right. The corners of the quad are specified in the 1412102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala * order bottom-left, bottom-right, top-left, top-right. 1422102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala */ 1432102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala @GenerateFieldPort(name = "inputRegion", hasDefault = true) 1442102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala private Quad mSourceRegion; 1452102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala 146c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi /** The maximum filesize (in bytes) of the recording session. 1476b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi * By default, it will be 0 and will be passed on to the MediaRecorder 1486b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi * If the limit is zero or negative, MediaRecorder will disable the limit*/ 1496b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi @GenerateFieldPort(name = "maxFileSize", hasDefault = true) 1506b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi private long mMaxFileSize = 0; 1516b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi 152c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi /** TimeLapse Interval between frames. 153c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi * By default, it will be 0. Whether the recording is timelapsed 154c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi * is inferred based on its value being greater than 0 */ 155c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi @GenerateFieldPort(name = "timelapseRecordingIntervalUs", hasDefault = true) 156c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi private long mTimeBetweenTimeLapseFrameCaptureUs = 0; 157c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi 15884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // End of user visible parameters 15984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 1607b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private static final int NO_AUDIO_SOURCE = -1; 1617b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 16284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private int mSurfaceId; 16384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private ShaderProgram mProgram; 16484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private GLFrame mScreen; 16584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 1667b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private boolean mRecordingActive = false; 167c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi private long mTimestampNs = 0; 168c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi private long mLastTimeLapseFrameRealTimestampNs = 0; 169c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi private int mNumFramesEncoded = 0; 170c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // Used to indicate whether recording is timelapsed. 171c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // Inferred based on (mTimeBetweenTimeLapseFrameCaptureUs > 0) 172c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi private boolean mCaptureTimeLapse = false; 1737b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 17484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private boolean mLogVerbose; 17584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private static final String TAG = "MediaEncoderFilter"; 17684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 17784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Our hook to the encoder 1787b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private MediaRecorder mMediaRecorder; 17984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 1804aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni /** Callback to be called when media recording completes. */ 1814aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni 1824aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni public interface OnRecordingDoneListener { 1834aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni public void onRecordingDone(); 1844aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni } 1854aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni 18684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public MediaEncoderFilter(String name) { 18784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi super(name); 1882102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala Point bl = new Point(0, 0); 1892102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala Point br = new Point(1, 0); 1902102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala Point tl = new Point(0, 1); 1912102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala Point tr = new Point(1, 1); 1922102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala mSourceRegion = new Quad(bl, br, tl, tr); 19384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE); 19484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 19584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 19684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 19784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void setupPorts() { 19884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Add input port- will accept RGBA GLFrames 19984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi addMaskedInputPort("videoframe", ImageFormat.create(ImageFormat.COLORSPACE_RGBA, 20084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi FrameFormat.TARGET_GPU)); 20184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 20284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 20384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 20484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void fieldPortValueUpdated(String name, FilterContext context) { 2057b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mLogVerbose) Log.v(TAG, "Port " + name + " has been updated"); 2067b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (name.equals("recording")) return; 2072102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala if (name.equals("inputRegion")) { 2082102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala if (isOpen()) updateSourceRegion(); 2092102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala return; 2102102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala } 2116b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // TODO: Not sure if it is possible to update the maxFileSize 2126b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // when the recording is going on. For now, not doing that. 2137b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (isOpen() && mRecordingActive) { 2147b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala throw new RuntimeException("Cannot change recording parameters" 2157b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala + " when the filter is recording!"); 21684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 21784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 21884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 2192102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala private void updateSourceRegion() { 2202102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala // Flip source quad to map to OpenGL origin 2212102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala Quad flippedRegion = new Quad(); 2222102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala flippedRegion.p0 = mSourceRegion.p2; 2232102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala flippedRegion.p1 = mSourceRegion.p3; 2242102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala flippedRegion.p2 = mSourceRegion.p0; 2252102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala flippedRegion.p3 = mSourceRegion.p1; 2262102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala mProgram.setSourceRegion(flippedRegion); 2272102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala } 2282102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala 22984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // update the MediaRecorderParams based on the variables. 23084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // These have to be in certain order as per the MediaRecorder 23184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // documentation 23284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private void updateMediaRecorderParams() { 233c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mCaptureTimeLapse = mTimeBetweenTimeLapseFrameCaptureUs > 0; 23484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi final int GRALLOC_BUFFER = 2; 23584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mMediaRecorder.setVideoSource(GRALLOC_BUFFER); 236c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (!mCaptureTimeLapse && (mAudioSource != NO_AUDIO_SOURCE)) { 2377b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setAudioSource(mAudioSource); 2387b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 2397b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mProfile != null) { 2407b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setProfile(mProfile); 241c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mFps = mProfile.videoFrameRate; 2427b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } else { 2437b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setOutputFormat(mOutputFormat); 2447b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setVideoEncoder(mVideoEncoder); 2457b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setVideoSize(mWidth, mHeight); 2467b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setVideoFrameRate(mFps); 2477b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 2487b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setOrientationHint(mOrientationHint); 2497b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setOnInfoListener(mInfoListener); 2507b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setOnErrorListener(mErrorListener); 251e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi if (mFd != null) { 252e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi mMediaRecorder.setOutputFile(mFd); 253e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi } else { 254e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi mMediaRecorder.setOutputFile(mOutputFile); 255e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi } 2566b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi try { 2576b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi mMediaRecorder.setMaxFileSize(mMaxFileSize); 2586b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi } catch (Exception e) { 2596b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // Following the logic in VideoCamera.java (in Camera app) 2606b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // We are going to ignore failure of setMaxFileSize here, as 2616b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // a) The composer selected may simply not support it, or 2626b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // b) The underlying media framework may not handle 64-bit range 2636b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // on the size restriction. 2646b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi Log.w(TAG, "Setting maxFileSize on MediaRecorder unsuccessful! " 2656b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi + e.getMessage()); 2666b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi } 26784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 26884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 26984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 27084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void prepare(FilterContext context) { 27184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi if (mLogVerbose) Log.v(TAG, "Preparing"); 27284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 27384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mProgram = ShaderProgram.createIdentity(context); 27484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 2757b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mRecordingActive = false; 27684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 27784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 27884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 27984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void open(FilterContext context) { 28084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi if (mLogVerbose) Log.v(TAG, "Opening"); 2812102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala updateSourceRegion(); 2827b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mRecording) startRecording(context); 2837b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 2847b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 2857b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private void startRecording(FilterContext context) { 2867b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mLogVerbose) Log.v(TAG, "Starting recording"); 2877b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 288c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala // Create a frame representing the screen 289c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala MutableFrameFormat screenFormat = new MutableFrameFormat( 290c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala FrameFormat.TYPE_BYTE, FrameFormat.TARGET_GPU); 291c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala screenFormat.setBytesPerSample(4); 292c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala 293c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala int width, height; 294c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala if (mProfile != null) { 295c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala width = mProfile.videoFrameWidth; 296c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala height = mProfile.videoFrameHeight; 297c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala } else { 298c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala width = mWidth; 299c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala height = mHeight; 300c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala } 301c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala screenFormat.setDimensions(width, height); 302c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala mScreen = (GLFrame)context.getFrameManager().newBoundFrame( 303c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala screenFormat, GLFrame.EXISTING_FBO_BINDING, 0); 304c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala 305c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala // Initialize the media recorder 306c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala 3077b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder = new MediaRecorder(); 3087b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala updateMediaRecorderParams(); 3097b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 31084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi try { 31184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mMediaRecorder.prepare(); 31284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } catch (IllegalStateException e) { 31384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi throw e; 31484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } catch (IOException e) { 31584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi throw new RuntimeException("IOException in" 31684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi + "MediaRecorder.prepare()!", e); 31784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } catch (Exception e) { 31884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi throw new RuntimeException("Unknown Exception in" 31984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi + "MediaRecorder.prepare()!", e); 32084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 32184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Make sure start() is called before trying to 32284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // register the surface. The native window handle needed to create 32384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // the surface is initiated in start() 32484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mMediaRecorder.start(); 325c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "Open: registering surface from Mediarecorder"); 32684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mSurfaceId = context.getGLEnvironment(). 3277b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala registerSurfaceFromMediaRecorder(mMediaRecorder); 328c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mNumFramesEncoded = 0; 3297b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mRecordingActive = true; 33084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 33184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 332c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi public boolean skipFrameAndModifyTimestamp(long timestampNs) { 333c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // first frame- encode. Don't skip 334c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mNumFramesEncoded == 0) { 335c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mLastTimeLapseFrameRealTimestampNs = timestampNs; 336c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mTimestampNs = timestampNs; 337c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "timelapse: FIRST frame, last real t= " 338c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi + mLastTimeLapseFrameRealTimestampNs + 339c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi ", setting t = " + mTimestampNs ); 340c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi return false; 341c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } 342c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi 343c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // Workaround to bypass the first 2 input frames for skipping. 344c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // The first 2 output frames from the encoder are: decoder specific info and 345c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // the compressed video frame data for the first input video frame. 346c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mNumFramesEncoded >= 2 && timestampNs < 347c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi (mLastTimeLapseFrameRealTimestampNs + 1000L * mTimeBetweenTimeLapseFrameCaptureUs)) { 348c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // If 2 frames have been already encoded, 349c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // Skip all frames from last encoded frame until 350c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed. 351c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "timelapse: skipping intermediate frame"); 352c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi return true; 353c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } else { 354c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // Desired frame has arrived after mTimeBetweenTimeLapseFrameCaptureUs time: 355c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // - Reset mLastTimeLapseFrameRealTimestampNs to current time. 356c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // - Artificially modify timestampNs to be one frame time (1/framerate) ahead 357c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // of the last encoded frame's time stamp. 358c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "timelapse: encoding frame, Timestamp t = " + timestampNs + 359c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi ", last real t= " + mLastTimeLapseFrameRealTimestampNs + 360c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi ", interval = " + mTimeBetweenTimeLapseFrameCaptureUs); 361c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mLastTimeLapseFrameRealTimestampNs = timestampNs; 362c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mTimestampNs = mTimestampNs + (1000000000L / (long)mFps); 363c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "timelapse: encoding frame, setting t = " 364c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi + mTimestampNs + ", delta t = " + (1000000000L / (long)mFps) + 365c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi ", fps = " + mFps ); 366c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi return false; 367c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } 368c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } 369c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi 37084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 37184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void process(FilterContext context) { 37284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi if (mLogVerbose) Log.v(TAG, "Starting frame processing"); 37384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 37484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi GLEnvironment glEnv = context.getGLEnvironment(); 37584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Get input frame 37684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi Frame input = pullInput("videoframe"); 37784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 3787b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala // Check if recording needs to start 3797b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (!mRecordingActive && mRecording) { 3807b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala startRecording(context); 3817b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 3827b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala // Check if recording needs to stop 3837b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mRecordingActive && !mRecording) { 3847b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala stopRecording(context); 3857b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 3867b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 3877b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (!mRecordingActive) return; 3887b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 389c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mCaptureTimeLapse) { 390c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (skipFrameAndModifyTimestamp(input.getTimestamp())) { 391c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi return; 392c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } 393c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } else { 394c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mTimestampNs = input.getTimestamp(); 395c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } 396c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi 39784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Activate our surface 39884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi glEnv.activateSurfaceWithId(mSurfaceId); 39984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 40084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Process 40184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mProgram.process(input, mScreen); 40284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 4037b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala // Set timestamp from input 404c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi glEnv.setSurfaceTimestamp(mTimestampNs); 40584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // And swap buffers 40684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi glEnv.swapBuffers(); 407c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mNumFramesEncoded++; 408c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "numFramesEncoded = " + mNumFramesEncoded); 40984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 41084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 4117b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private void stopRecording(FilterContext context) { 4127b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mLogVerbose) Log.v(TAG, "Stopping recording"); 4137b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 414d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi mRecordingActive = false; 415c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mNumFramesEncoded = 0; 416f283a907a88fc348b770186562c7a1046a8b7367Pannag Sanketi GLEnvironment glEnv = context.getGLEnvironment(); 417470dfeec8a0fbae2a0e8f6021660ef66135b22cePannag Sanketi // The following call will switch the surface_id to 0 418470dfeec8a0fbae2a0e8f6021660ef66135b22cePannag Sanketi // (thus, calling eglMakeCurrent on surface with id 0) and 419470dfeec8a0fbae2a0e8f6021660ef66135b22cePannag Sanketi // then call eglDestroy on the surface. Hence, this will 420470dfeec8a0fbae2a0e8f6021660ef66135b22cePannag Sanketi // call disconnect the SurfaceMediaSource, which is needed to 421470dfeec8a0fbae2a0e8f6021660ef66135b22cePannag Sanketi // be called before calling Stop on the mediarecorder 422d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi if (mLogVerbose) Log.v(TAG, String.format("Unregistering surface %d", mSurfaceId)); 4237b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala glEnv.unregisterSurfaceId(mSurfaceId); 424d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi try { 425d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi mMediaRecorder.stop(); 426d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi } catch (RuntimeException e) { 427d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi throw new MediaRecorderStopException("MediaRecorder.stop() failed!", e); 428d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi } 4297b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.release(); 4307b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder = null; 4317b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 432c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala mScreen.release(); 433c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala mScreen = null; 4344aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni 4354aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni // Use an EffectsRecorder callback to forward a media finalization 4364aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni // call so that it creates the video thumbnail, and whatever else needs 4374aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni // to be done to finalize media. 4384aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni if (mRecordingDoneListener != null) { 4394aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni mRecordingDoneListener.onRecordingDone(); 4404aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni } 4417b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 4427b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 4437b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala @Override 4447b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala public void close(FilterContext context) { 4457b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mLogVerbose) Log.v(TAG, "Closing"); 4467b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mRecordingActive) stopRecording(context); 44784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 44884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 44984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 45084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void tearDown(FilterContext context) { 45184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Release all the resources associated with the MediaRecorder 45284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // and GLFrame members 45384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi if (mMediaRecorder != null) { 45484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mMediaRecorder.release(); 45584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 45684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi if (mScreen != null) { 45784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mScreen.release(); 45884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 459c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala 46084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 46184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 46284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi} 463