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