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. 1470ea5e57c6cc314a5d3a14aa0745c56c2a9101f0fPannag 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 1520ea5e57c6cc314a5d3a14aa0745c56c2a9101f0fPannag Sanketi /** The maximum duration (in milliseconds) of the recording session. 1530ea5e57c6cc314a5d3a14aa0745c56c2a9101f0fPannag Sanketi * By default, it will be 0 and will be passed on to the MediaRecorder. 1540ea5e57c6cc314a5d3a14aa0745c56c2a9101f0fPannag Sanketi * If the limit is zero or negative, MediaRecorder will record indefinitely*/ 1550ea5e57c6cc314a5d3a14aa0745c56c2a9101f0fPannag Sanketi @GenerateFieldPort(name = "maxDurationMs", hasDefault = true) 1560ea5e57c6cc314a5d3a14aa0745c56c2a9101f0fPannag Sanketi private int mMaxDurationMs = 0; 1570ea5e57c6cc314a5d3a14aa0745c56c2a9101f0fPannag Sanketi 158c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi /** TimeLapse Interval between frames. 159c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi * By default, it will be 0. Whether the recording is timelapsed 160c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi * is inferred based on its value being greater than 0 */ 161c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi @GenerateFieldPort(name = "timelapseRecordingIntervalUs", hasDefault = true) 162c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi private long mTimeBetweenTimeLapseFrameCaptureUs = 0; 163c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi 16484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // End of user visible parameters 16584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 1667b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private static final int NO_AUDIO_SOURCE = -1; 1677b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 16884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private int mSurfaceId; 16984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private ShaderProgram mProgram; 17084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private GLFrame mScreen; 17184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 1727b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private boolean mRecordingActive = false; 173c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi private long mTimestampNs = 0; 174c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi private long mLastTimeLapseFrameRealTimestampNs = 0; 175c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi private int mNumFramesEncoded = 0; 176c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // Used to indicate whether recording is timelapsed. 177c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // Inferred based on (mTimeBetweenTimeLapseFrameCaptureUs > 0) 178c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi private boolean mCaptureTimeLapse = false; 1797b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 18084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private boolean mLogVerbose; 18184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private static final String TAG = "MediaEncoderFilter"; 18284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 18384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Our hook to the encoder 1847b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private MediaRecorder mMediaRecorder; 18584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 1864aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni /** Callback to be called when media recording completes. */ 1874aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni 1884aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni public interface OnRecordingDoneListener { 1894aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni public void onRecordingDone(); 1904aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni } 1914aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni 19284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public MediaEncoderFilter(String name) { 19384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi super(name); 1942102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala Point bl = new Point(0, 0); 1952102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala Point br = new Point(1, 0); 1962102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala Point tl = new Point(0, 1); 1972102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala Point tr = new Point(1, 1); 1982102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala mSourceRegion = new Quad(bl, br, tl, tr); 19984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE); 20084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 20184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 20284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 20384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void setupPorts() { 20484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Add input port- will accept RGBA GLFrames 20584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi addMaskedInputPort("videoframe", ImageFormat.create(ImageFormat.COLORSPACE_RGBA, 20684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi FrameFormat.TARGET_GPU)); 20784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 20884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 20984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 21084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void fieldPortValueUpdated(String name, FilterContext context) { 2117b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mLogVerbose) Log.v(TAG, "Port " + name + " has been updated"); 2127b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (name.equals("recording")) return; 2132102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala if (name.equals("inputRegion")) { 2142102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala if (isOpen()) updateSourceRegion(); 2152102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala return; 2162102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala } 2176b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // TODO: Not sure if it is possible to update the maxFileSize 2186b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // when the recording is going on. For now, not doing that. 2197b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (isOpen() && mRecordingActive) { 2207b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala throw new RuntimeException("Cannot change recording parameters" 2217b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala + " when the filter is recording!"); 22284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 22384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 22484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 2252102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala private void updateSourceRegion() { 2262102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala // Flip source quad to map to OpenGL origin 2272102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala Quad flippedRegion = new Quad(); 2282102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala flippedRegion.p0 = mSourceRegion.p2; 2292102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala flippedRegion.p1 = mSourceRegion.p3; 2302102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala flippedRegion.p2 = mSourceRegion.p0; 2312102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala flippedRegion.p3 = mSourceRegion.p1; 2322102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala mProgram.setSourceRegion(flippedRegion); 2332102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala } 2342102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala 23584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // update the MediaRecorderParams based on the variables. 23684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // These have to be in certain order as per the MediaRecorder 23784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // documentation 23884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi private void updateMediaRecorderParams() { 239c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mCaptureTimeLapse = mTimeBetweenTimeLapseFrameCaptureUs > 0; 24084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi final int GRALLOC_BUFFER = 2; 24184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mMediaRecorder.setVideoSource(GRALLOC_BUFFER); 242c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (!mCaptureTimeLapse && (mAudioSource != NO_AUDIO_SOURCE)) { 2437b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setAudioSource(mAudioSource); 2447b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 2457b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mProfile != null) { 2467b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setProfile(mProfile); 247c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mFps = mProfile.videoFrameRate; 2487b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } else { 2497b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setOutputFormat(mOutputFormat); 2507b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setVideoEncoder(mVideoEncoder); 2517b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setVideoSize(mWidth, mHeight); 2527b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setVideoFrameRate(mFps); 2537b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 2547b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setOrientationHint(mOrientationHint); 2557b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setOnInfoListener(mInfoListener); 2567b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.setOnErrorListener(mErrorListener); 257e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi if (mFd != null) { 258e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi mMediaRecorder.setOutputFile(mFd); 259e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi } else { 260e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi mMediaRecorder.setOutputFile(mOutputFile); 261e60b806043134ed920a8c70431c305552d4d6a8bPannag Sanketi } 2626b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi try { 2636b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi mMediaRecorder.setMaxFileSize(mMaxFileSize); 2646b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi } catch (Exception e) { 2656b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // Following the logic in VideoCamera.java (in Camera app) 2666b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // We are going to ignore failure of setMaxFileSize here, as 2676b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // a) The composer selected may simply not support it, or 2686b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // b) The underlying media framework may not handle 64-bit range 2696b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi // on the size restriction. 2706b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi Log.w(TAG, "Setting maxFileSize on MediaRecorder unsuccessful! " 2716b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi + e.getMessage()); 2726b9780efb2b34058f24e462ae54e6a5b6df85e46Pannag Sanketi } 2730ea5e57c6cc314a5d3a14aa0745c56c2a9101f0fPannag Sanketi mMediaRecorder.setMaxDuration(mMaxDurationMs); 27484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 27584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 27684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 27784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void prepare(FilterContext context) { 27884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi if (mLogVerbose) Log.v(TAG, "Preparing"); 27984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 28084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mProgram = ShaderProgram.createIdentity(context); 28184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 2827b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mRecordingActive = false; 28384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 28484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 28584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 28684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void open(FilterContext context) { 28784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi if (mLogVerbose) Log.v(TAG, "Opening"); 2882102fe47f517fa51e742d816ad0fc5cff65d3e41Eino-Ville Talvala updateSourceRegion(); 2897b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mRecording) startRecording(context); 2907b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 2917b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 2927b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private void startRecording(FilterContext context) { 2937b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mLogVerbose) Log.v(TAG, "Starting recording"); 2947b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 295c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala // Create a frame representing the screen 296c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala MutableFrameFormat screenFormat = new MutableFrameFormat( 297c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala FrameFormat.TYPE_BYTE, FrameFormat.TARGET_GPU); 298c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala screenFormat.setBytesPerSample(4); 299c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala 300c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala int width, height; 301c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala if (mProfile != null) { 302c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala width = mProfile.videoFrameWidth; 303c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala height = mProfile.videoFrameHeight; 304c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala } else { 305c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala width = mWidth; 306c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala height = mHeight; 307c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala } 308c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala screenFormat.setDimensions(width, height); 309c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala mScreen = (GLFrame)context.getFrameManager().newBoundFrame( 310c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala screenFormat, GLFrame.EXISTING_FBO_BINDING, 0); 311c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala 312c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala // Initialize the media recorder 313c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala 3147b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder = new MediaRecorder(); 3157b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala updateMediaRecorderParams(); 3167b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 31784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi try { 31884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mMediaRecorder.prepare(); 31984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } catch (IllegalStateException e) { 32084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi throw e; 32184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } catch (IOException e) { 32284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi throw new RuntimeException("IOException in" 32384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi + "MediaRecorder.prepare()!", e); 32484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } catch (Exception e) { 32584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi throw new RuntimeException("Unknown Exception in" 32684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi + "MediaRecorder.prepare()!", e); 32784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 32884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Make sure start() is called before trying to 32984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // register the surface. The native window handle needed to create 33084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // the surface is initiated in start() 33184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mMediaRecorder.start(); 332c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "Open: registering surface from Mediarecorder"); 33384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mSurfaceId = context.getGLEnvironment(). 3347b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala registerSurfaceFromMediaRecorder(mMediaRecorder); 335c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mNumFramesEncoded = 0; 3367b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mRecordingActive = true; 33784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 33884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 339c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi public boolean skipFrameAndModifyTimestamp(long timestampNs) { 340c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // first frame- encode. Don't skip 341c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mNumFramesEncoded == 0) { 342c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mLastTimeLapseFrameRealTimestampNs = timestampNs; 343c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mTimestampNs = timestampNs; 344c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "timelapse: FIRST frame, last real t= " 345c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi + mLastTimeLapseFrameRealTimestampNs + 346c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi ", setting t = " + mTimestampNs ); 347c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi return false; 348c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } 349c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi 350c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // Workaround to bypass the first 2 input frames for skipping. 351c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // The first 2 output frames from the encoder are: decoder specific info and 352c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // the compressed video frame data for the first input video frame. 353c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mNumFramesEncoded >= 2 && timestampNs < 354c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi (mLastTimeLapseFrameRealTimestampNs + 1000L * mTimeBetweenTimeLapseFrameCaptureUs)) { 355c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // If 2 frames have been already encoded, 356c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // Skip all frames from last encoded frame until 357c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed. 358c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "timelapse: skipping intermediate frame"); 359c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi return true; 360c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } else { 361c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // Desired frame has arrived after mTimeBetweenTimeLapseFrameCaptureUs time: 362c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // - Reset mLastTimeLapseFrameRealTimestampNs to current time. 363c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // - Artificially modify timestampNs to be one frame time (1/framerate) ahead 364c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi // of the last encoded frame's time stamp. 365c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "timelapse: encoding frame, Timestamp t = " + timestampNs + 366c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi ", last real t= " + mLastTimeLapseFrameRealTimestampNs + 367c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi ", interval = " + mTimeBetweenTimeLapseFrameCaptureUs); 368c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mLastTimeLapseFrameRealTimestampNs = timestampNs; 369c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mTimestampNs = mTimestampNs + (1000000000L / (long)mFps); 370c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "timelapse: encoding frame, setting t = " 371c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi + mTimestampNs + ", delta t = " + (1000000000L / (long)mFps) + 372c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi ", fps = " + mFps ); 373c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi return false; 374c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } 375c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } 376c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi 37784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 37884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void process(FilterContext context) { 37984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi if (mLogVerbose) Log.v(TAG, "Starting frame processing"); 38084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 38184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi GLEnvironment glEnv = context.getGLEnvironment(); 38284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Get input frame 38384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi Frame input = pullInput("videoframe"); 38484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 3857b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala // Check if recording needs to start 3867b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (!mRecordingActive && mRecording) { 3877b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala startRecording(context); 3887b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 3897b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala // Check if recording needs to stop 3907b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mRecordingActive && !mRecording) { 3917b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala stopRecording(context); 3927b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 3937b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 3947b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (!mRecordingActive) return; 3957b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 396c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mCaptureTimeLapse) { 397c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (skipFrameAndModifyTimestamp(input.getTimestamp())) { 398c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi return; 399c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } 400c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } else { 401c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mTimestampNs = input.getTimestamp(); 402c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi } 403c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi 40484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Activate our surface 40584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi glEnv.activateSurfaceWithId(mSurfaceId); 40684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 40784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Process 40884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mProgram.process(input, mScreen); 40984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 4107b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala // Set timestamp from input 411c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi glEnv.setSurfaceTimestamp(mTimestampNs); 41284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // And swap buffers 41384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi glEnv.swapBuffers(); 414c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mNumFramesEncoded++; 415c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi if (mLogVerbose) Log.v(TAG, "numFramesEncoded = " + mNumFramesEncoded); 41684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 41784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 4187b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala private void stopRecording(FilterContext context) { 4197b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mLogVerbose) Log.v(TAG, "Stopping recording"); 4207b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 421d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi mRecordingActive = false; 422c1604923efb199d96ac78f68d5a211edebc404a5Pannag Sanketi mNumFramesEncoded = 0; 423f283a907a88fc348b770186562c7a1046a8b7367Pannag Sanketi GLEnvironment glEnv = context.getGLEnvironment(); 424470dfeec8a0fbae2a0e8f6021660ef66135b22cePannag Sanketi // The following call will switch the surface_id to 0 425470dfeec8a0fbae2a0e8f6021660ef66135b22cePannag Sanketi // (thus, calling eglMakeCurrent on surface with id 0) and 426470dfeec8a0fbae2a0e8f6021660ef66135b22cePannag Sanketi // then call eglDestroy on the surface. Hence, this will 427470dfeec8a0fbae2a0e8f6021660ef66135b22cePannag Sanketi // call disconnect the SurfaceMediaSource, which is needed to 428470dfeec8a0fbae2a0e8f6021660ef66135b22cePannag Sanketi // be called before calling Stop on the mediarecorder 429d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi if (mLogVerbose) Log.v(TAG, String.format("Unregistering surface %d", mSurfaceId)); 4307b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala glEnv.unregisterSurfaceId(mSurfaceId); 431d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi try { 432d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi mMediaRecorder.stop(); 433d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi } catch (RuntimeException e) { 434d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi throw new MediaRecorderStopException("MediaRecorder.stop() failed!", e); 435d383223561fe30ed667a347f87c442d4f21bff64Pannag Sanketi } 4367b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder.release(); 4377b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala mMediaRecorder = null; 4387b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 439c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala mScreen.release(); 440c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala mScreen = null; 4414aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni 4424aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni // Use an EffectsRecorder callback to forward a media finalization 4434aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni // call so that it creates the video thumbnail, and whatever else needs 4444aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni // to be done to finalize media. 4454aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni if (mRecordingDoneListener != null) { 4464aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni mRecordingDoneListener.onRecordingDone(); 4474aad9a241281b973b05c14eb48752d3afafec4ceRodrigo Carceroni } 4487b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala } 4497b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala 4507b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala @Override 4517b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala public void close(FilterContext context) { 4527b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mLogVerbose) Log.v(TAG, "Closing"); 4537b91f5e4994417f63f2024723d731fdad9f7e112Eino-Ville Talvala if (mRecordingActive) stopRecording(context); 45484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 45584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 45684a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi @Override 45784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi public void tearDown(FilterContext context) { 45884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // Release all the resources associated with the MediaRecorder 45984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi // and GLFrame members 46084a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi if (mMediaRecorder != null) { 46184a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mMediaRecorder.release(); 46284a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 46384a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi if (mScreen != null) { 46484a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi mScreen.release(); 46584a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 466c08028757ef67542ea7e53d991c74ff3da112f9eEino-Ville Talvala 46784a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi } 46884a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi 46984a9fb79433ee7c66fe1df7b0a754828ff89aec1Pannag Sanketi} 470