165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn/*
265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Copyright (C) 2011 The Android Open Source Project
365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Licensed under the Apache License, Version 2.0 (the "License");
565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * you may not use this file except in compliance with the License.
665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * You may obtain a copy of the License at
765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *      http://www.apache.org/licenses/LICENSE-2.0
965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
1065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Unless required by applicable law or agreed to in writing, software
1165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * distributed under the License is distributed on an "AS IS" BASIS,
1265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * See the License for the specific language governing permissions and
1465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * limitations under the License.
1565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn */
1665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
1765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
1865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennpackage android.filterpacks.videosrc;
1965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
2065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.content.Context;
2165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.content.res.AssetFileDescriptor;
2265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.Filter;
2365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.FilterContext;
2465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.Frame;
2565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.FrameFormat;
2665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.FrameManager;
2765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.GenerateFieldPort;
2865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.GenerateFinalPort;
2965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.GLFrame;
3065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.KeyValueMap;
3165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.MutableFrameFormat;
3265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.NativeFrame;
3365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.Program;
3465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.ShaderProgram;
3565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.format.ImageFormat;
3665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.graphics.SurfaceTexture;
3765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.media.MediaPlayer;
382253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhuimport android.net.Uri;
3965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.os.ConditionVariable;
4065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.opengl.Matrix;
4165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.view.Surface;
4265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
4365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.io.IOException;
4465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.io.FileDescriptor;
4565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.lang.IllegalArgumentException;
4665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.List;
4765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.Set;
4865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
4965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.util.Log;
5065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
5165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn/**
5265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * @hide
5365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn */
5465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennpublic class MediaSource extends Filter {
5565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
5665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** User-visible parameters */
5765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
5865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** The source URL for the media source. Can be an http: link to a remote
5965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * resource, or a file: link to a local media file
6065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     */
6165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFieldPort(name = "sourceUrl", hasDefault = true)
6265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private String mSourceUrl = "";
6365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
6465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** An open asset file descriptor to a local media source. Default is null */
6565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFieldPort(name = "sourceAsset", hasDefault = true)
6665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private AssetFileDescriptor mSourceAsset = null;
6765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
682253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhu    /** The context for the MediaPlayer to resolve the sourceUrl.
692253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhu     * Make sure this is set before the sourceUrl to avoid unexpected result.
702253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhu     * If the sourceUrl is not a content URI, it is OK to keep this as null. */
712253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhu    @GenerateFieldPort(name = "context", hasDefault = true)
722253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhu    private Context mContext = null;
732253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhu
7465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Whether the media source is a URL or an asset file descriptor. Defaults
7565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * to false.
7665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     */
7765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFieldPort(name = "sourceIsUrl", hasDefault = true)
7865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mSelectedIsUrl = false;
7965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
8065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Whether the filter will always wait for a new video frame, or whether it
8165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * will output an old frame again if a new frame isn't available. Defaults
8265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * to true.
8365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     */
8465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFinalPort(name = "waitForNewFrame", hasDefault = true)
8565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mWaitForNewFrame = true;
8665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
8765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Whether the media source should loop automatically or not. Defaults to
8865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * true.
8965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     */
9065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFieldPort(name = "loop", hasDefault = true)
9165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mLooping = true;
9265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
9365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Volume control. Currently sound is piped directly to the speakers, so
9465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * this defaults to mute.
9565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     */
9665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFieldPort(name = "volume", hasDefault = true)
9765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private float mVolume = 0.f;
9865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
9965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Orientation. This controls the output orientation of the video. Valid
10065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * values are 0, 90, 180, 270
10165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     */
10265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFieldPort(name = "orientation", hasDefault = true)
10365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private int mOrientation = 0;
10465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
10565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private MediaPlayer mMediaPlayer;
10665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private GLFrame mMediaFrame;
10765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private SurfaceTexture mSurfaceTexture;
10865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private ShaderProgram mFrameExtractor;
10965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private MutableFrameFormat mOutputFormat;
11065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private int mWidth, mHeight;
11165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
11265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // Total timeouts will be PREP_TIMEOUT*PREP_TIMEOUT_REPEAT
11365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final int PREP_TIMEOUT = 100; // ms
11465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final int PREP_TIMEOUT_REPEAT = 100;
11565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final int NEWFRAME_TIMEOUT = 100; //ms
11665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final int NEWFRAME_TIMEOUT_REPEAT = 10;
11765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
11865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // This is an identity shader; not using the default identity
11965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // shader because reading from a SurfaceTexture requires the
12065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // GL_OES_EGL_image_external extension.
12165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private final String mFrameShader =
12265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "#extension GL_OES_EGL_image_external : require\n" +
12365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "precision mediump float;\n" +
12465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "uniform samplerExternalOES tex_sampler_0;\n" +
12565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "varying vec2 v_texcoord;\n" +
12665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "void main() {\n" +
12765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "  gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
12865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "}\n";
12965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
13065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // The following transforms enable rotation of the decoded source.
13165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // These are multiplied with the transform obtained from the
13265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // SurfaceTexture to get the final transform to be set on the media source.
13365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // Currently, given a device orientation, the MediaSource rotates in such a way
13465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // that the source is displayed upright. A particular use case
13565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // is "Background Replacement" feature in the Camera app
13665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // where the MediaSource rotates the source to align with the camera feed and pass it
13765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // on to the backdropper filter. The backdropper only does the blending
13865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // and does not have to do any rotation
13965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // (except for mirroring in case of front camera).
14065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // TODO: Currently the rotations are spread over a bunch of stages in the
14165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // pipeline. A cleaner design
14265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // could be to cast away all the rotation in a separate filter or attach a transform
14365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // to the frame so that MediaSource itself need not know about any rotation.
14465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final float[] mSourceCoords_0 = { 1, 1, 0, 1,
14565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                     0, 1, 0, 1,
14665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                     1, 0, 0, 1,
14765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                     0, 0, 0, 1 };
14865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final float[] mSourceCoords_270 = { 0, 1, 0, 1,
14965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                      0, 0, 0, 1,
15065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                      1, 1, 0, 1,
15165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                      1, 0, 0, 1 };
15265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final float[] mSourceCoords_180 = { 0, 0, 0, 1,
15365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                       1, 0, 0, 1,
15465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                       0, 1, 0, 1,
15565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                       1, 1, 0, 1 };
15665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final float[] mSourceCoords_90 = { 1, 0, 0, 1,
15765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                       1, 1, 0, 1,
15865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                       0, 0, 0, 1,
15965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                       0, 1, 0, 1 };
16065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
16165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mGotSize;
16265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mPrepared;
16365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mPlaying;
16465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mNewFrameAvailable;
16565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mOrientationUpdated;
16665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mPaused;
16765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mCompleted;
16865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
16965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private final boolean mLogVerbose;
17065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final String TAG = "MediaSource";
17165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
17265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public MediaSource(String name) {
17365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        super(name);
17465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mNewFrameAvailable = false;
17565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
17665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
17765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
17865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
17965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
18065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void setupPorts() {
18165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Add input port
18265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        addOutputPort("video", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
18365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                  FrameFormat.TARGET_GPU));
18465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
18565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
18665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void createFormats() {
18765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mOutputFormat = ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
18865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                           FrameFormat.TARGET_GPU);
18965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
19065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
19165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
19265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    protected void prepare(FilterContext context) {
19365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Preparing MediaSource");
19465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
19565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mFrameExtractor = new ShaderProgram(context, mFrameShader);
19665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // SurfaceTexture defines (0,0) to be bottom-left. The filter framework
19765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // defines (0,0) as top-left, so do the flip here.
19865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mFrameExtractor.setSourceRect(0, 1, 1, -1);
19965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
20065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        createFormats();
20165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
20265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
20365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
20465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void open(FilterContext context) {
20565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) {
20665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            Log.v(TAG, "Opening MediaSource");
20765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mSelectedIsUrl) {
20865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                Log.v(TAG, "Current URL is " + mSourceUrl);
20965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } else {
21065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                Log.v(TAG, "Current source is Asset!");
21165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
21265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
21365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
21465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mMediaFrame = (GLFrame)context.getFrameManager().newBoundFrame(
21565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mOutputFormat,
21665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                GLFrame.EXTERNAL_TEXTURE,
21765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                0);
21865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
21965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mSurfaceTexture = new SurfaceTexture(mMediaFrame.getTextureId());
22065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
22165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (!setupMediaPlayer(mSelectedIsUrl)) {
22265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn          throw new RuntimeException("Error setting up MediaPlayer!");
22365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
22465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
22565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
22665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
22765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void process(FilterContext context) {
22865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Note: process is synchronized by its caller in the Filter base class
22965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Processing new frame");
23065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
23165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mMediaPlayer == null) {
23265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Something went wrong in initialization or parameter updates
23365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new NullPointerException("Unexpected null media player!");
23465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
23565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
23665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mCompleted) {
23765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Video playback is done, so close us down
23865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            closeOutputPort("video");
23965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            return;
24065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
24165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
24265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (!mPlaying) {
24365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            int waitCount = 0;
24465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "Waiting for preparation to complete");
24565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            while (!mGotSize || !mPrepared) {
24665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                try {
24765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    this.wait(PREP_TIMEOUT);
24865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                } catch (InterruptedException e) {
24965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    // ignoring
25065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
25165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mCompleted) {
25265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    // Video playback is done, so close us down
25365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    closeOutputPort("video");
25465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    return;
25565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
25665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                waitCount++;
25765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (waitCount == PREP_TIMEOUT_REPEAT) {
25865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mMediaPlayer.release();
25965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    throw new RuntimeException("MediaPlayer timed out while preparing!");
26065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
26165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
26265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "Starting playback");
26365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mMediaPlayer.start();
26465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
26565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
26665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Use last frame if paused, unless just starting playback, in which case
26765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // we want at least one valid frame before pausing
26865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (!mPaused || !mPlaying) {
26965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mWaitForNewFrame) {
27065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mLogVerbose) Log.v(TAG, "Waiting for new frame");
27165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
27265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                int waitCount = 0;
27365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                while (!mNewFrameAvailable) {
27465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    if (waitCount == NEWFRAME_TIMEOUT_REPEAT) {
27565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        if (mCompleted) {
27665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                            // Video playback is done, so close us down
27765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                            closeOutputPort("video");
27865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                            return;
27965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        } else {
28065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                            throw new RuntimeException("Timeout waiting for new frame!");
28165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        }
28265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    }
28365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    try {
28465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        this.wait(NEWFRAME_TIMEOUT);
28565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } catch (InterruptedException e) {
28665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        if (mLogVerbose) Log.v(TAG, "interrupted");
28765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        // ignoring
28865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    }
28965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    waitCount++;
29065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
29165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mNewFrameAvailable = false;
29265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mLogVerbose) Log.v(TAG, "Got new frame");
29365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
29465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
29565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mSurfaceTexture.updateTexImage();
29665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mOrientationUpdated = true;
29765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
29865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mOrientationUpdated) {
29965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            float[] surfaceTransform = new float[16];
30065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mSurfaceTexture.getTransformMatrix(surfaceTransform);
30165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
30265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            float[] sourceCoords = new float[16];
30365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            switch (mOrientation) {
30465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                default:
30565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case 0:
30665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    Matrix.multiplyMM(sourceCoords, 0,
30765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                      surfaceTransform, 0,
30865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                      mSourceCoords_0, 0);
30965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
31065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case 90:
31165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    Matrix.multiplyMM(sourceCoords, 0,
31265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                      surfaceTransform, 0,
31365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                      mSourceCoords_90, 0);
31465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
31565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case 180:
31665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    Matrix.multiplyMM(sourceCoords, 0,
31765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                      surfaceTransform, 0,
31865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                      mSourceCoords_180, 0);
31965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
32065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case 270:
32165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    Matrix.multiplyMM(sourceCoords, 0,
32265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                      surfaceTransform, 0,
32365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                      mSourceCoords_270, 0);
32465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
32565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
32665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) {
32765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                Log.v(TAG, "OrientationHint = " + mOrientation);
32865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                String temp = String.format("SetSourceRegion: %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f",
32965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        sourceCoords[4], sourceCoords[5],sourceCoords[0], sourceCoords[1],
33065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        sourceCoords[12], sourceCoords[13],sourceCoords[8], sourceCoords[9]);
33165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                Log.v(TAG, temp);
33265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
33365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mFrameExtractor.setSourceRegion(sourceCoords[4], sourceCoords[5],
33465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    sourceCoords[0], sourceCoords[1],
33565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    sourceCoords[12], sourceCoords[13],
33665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    sourceCoords[8], sourceCoords[9]);
33765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mOrientationUpdated = false;
33865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
33965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
34065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        Frame output = context.getFrameManager().newFrame(mOutputFormat);
34165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mFrameExtractor.process(mMediaFrame, output);
34265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
34365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        long timestamp = mSurfaceTexture.getTimestamp();
34465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Timestamp: " + (timestamp / 1000000000.0) + " s");
34565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        output.setTimestamp(timestamp);
34665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
34765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        pushOutput("video", output);
34865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        output.release();
34965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
35065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mPlaying = true;
35165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
35265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
35365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
35465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void close(FilterContext context) {
35565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mMediaPlayer.isPlaying()) {
35665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mMediaPlayer.stop();
35765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
35865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mPrepared = false;
35965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mGotSize = false;
36065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mPlaying = false;
36165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mPaused = false;
36265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCompleted = false;
36365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mNewFrameAvailable = false;
36465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
36565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mMediaPlayer.release();
36665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mMediaPlayer = null;
36765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mSurfaceTexture.release();
36865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mSurfaceTexture = null;
36965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "MediaSource closed");
37065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
37165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
37265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
37365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void tearDown(FilterContext context) {
37465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mMediaFrame != null) {
37565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mMediaFrame.release();
37665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
37765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
37865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
37965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // When updating the port values of the filter, users can update sourceIsUrl to switch
38065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    //   between using URL objects or Assets.
38165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // If updating only sourceUrl/sourceAsset, MediaPlayer gets reset if the current player
38265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    //   uses Url objects/Asset.
38365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // Otherwise the new sourceUrl/sourceAsset is stored and will be used when users switch
38465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    //   sourceIsUrl next time.
38565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
38665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void fieldPortValueUpdated(String name, FilterContext context) {
38765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Parameter update");
38865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (name.equals("sourceUrl")) {
38965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn           if (isOpen()) {
39065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mLogVerbose) Log.v(TAG, "Opening new source URL");
39165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mSelectedIsUrl) {
39265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    setupMediaPlayer(mSelectedIsUrl);
39365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
39465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
39565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } else if (name.equals("sourceAsset") ) {
39665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (isOpen()) {
39765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mLogVerbose) Log.v(TAG, "Opening new source FD");
39865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (!mSelectedIsUrl) {
39965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    setupMediaPlayer(mSelectedIsUrl);
40065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
40165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
40265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } else if (name.equals("loop")) {
40365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (isOpen()) {
40465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mMediaPlayer.setLooping(mLooping);
40565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
40665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } else if (name.equals("sourceIsUrl")) {
40765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (isOpen()){
40865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mSelectedIsUrl){
40965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    if (mLogVerbose) Log.v(TAG, "Opening new source URL");
41065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                } else {
41165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    if (mLogVerbose) Log.v(TAG, "Opening new source Asset");
41265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
41365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                setupMediaPlayer(mSelectedIsUrl);
41465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
41565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } else if (name.equals("volume")) {
41665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (isOpen()) {
41765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mMediaPlayer.setVolume(mVolume, mVolume);
41865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
41965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } else if (name.equals("orientation") && mGotSize) {
42065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mOrientation == 0 || mOrientation == 180) {
42165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mOutputFormat.setDimensions(mWidth, mHeight);
42265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } else {
42365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mOutputFormat.setDimensions(mHeight, mWidth);
42465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
42565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mOrientationUpdated = true;
42665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
42765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
42865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
42965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    synchronized public void pauseVideo(boolean pauseState) {
43065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (isOpen()) {
43165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (pauseState && !mPaused) {
43265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mMediaPlayer.pause();
43365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } else if (!pauseState && mPaused) {
43465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mMediaPlayer.start();
43565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
43665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
43765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mPaused = pauseState;
43865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
43965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
44065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Creates a media player, sets it up, and calls prepare */
44165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    synchronized private boolean setupMediaPlayer(boolean useUrl) {
44265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mPrepared = false;
44365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mGotSize = false;
44465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mPlaying = false;
44565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mPaused = false;
44665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCompleted = false;
44765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mNewFrameAvailable = false;
44865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
44965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Setting up playback.");
45065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
45165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mMediaPlayer != null) {
45265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Clean up existing media players
45365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "Resetting existing MediaPlayer.");
45465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mMediaPlayer.reset();
45565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } else {
45665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Create new media player
45765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "Creating new MediaPlayer.");
45865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mMediaPlayer = new MediaPlayer();
45965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
46065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
46165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mMediaPlayer == null) {
46265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new RuntimeException("Unable to create a MediaPlayer!");
46365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
46465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
46565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Set up data sources, etc
46665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        try {
46765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (useUrl) {
46865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mLogVerbose) Log.v(TAG, "Setting MediaPlayer source to URI " + mSourceUrl);
4692253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhu                if (mContext == null) {
4702253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhu                    mMediaPlayer.setDataSource(mSourceUrl);
4712253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhu                } else {
4722253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhu                    mMediaPlayer.setDataSource(mContext, Uri.parse(mSourceUrl.toString()));
4732253eeab6c0c20e0b03f144c5bc23ae13e8ab234Teng-Hui Zhu                }
47465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } else {
47565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mLogVerbose) Log.v(TAG, "Setting MediaPlayer source to asset " + mSourceAsset);
47665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mMediaPlayer.setDataSource(mSourceAsset.getFileDescriptor(), mSourceAsset.getStartOffset(), mSourceAsset.getLength());
47765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
47865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } catch(IOException e) {
47965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mMediaPlayer.release();
48065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mMediaPlayer = null;
48165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (useUrl) {
48265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                throw new RuntimeException(String.format("Unable to set MediaPlayer to URL %s!", mSourceUrl), e);
48365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } else {
48465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                throw new RuntimeException(String.format("Unable to set MediaPlayer to asset %s!", mSourceAsset), e);
48565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
48665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } catch(IllegalArgumentException e) {
48765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mMediaPlayer.release();
48865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mMediaPlayer = null;
48965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (useUrl) {
49065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                throw new RuntimeException(String.format("Unable to set MediaPlayer to URL %s!", mSourceUrl), e);
49165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } else {
49265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                throw new RuntimeException(String.format("Unable to set MediaPlayer to asset %s!", mSourceAsset), e);
49365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
49465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
49565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
49665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mMediaPlayer.setLooping(mLooping);
49765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mMediaPlayer.setVolume(mVolume, mVolume);
49865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
49965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Bind it to our media frame
50065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        Surface surface = new Surface(mSurfaceTexture);
50165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mMediaPlayer.setSurface(surface);
50265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        surface.release();
50365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
50465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Connect Media Player to callbacks
50565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
50665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mMediaPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener);
50765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mMediaPlayer.setOnPreparedListener(onPreparedListener);
50865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mMediaPlayer.setOnCompletionListener(onCompletionListener);
50965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
51065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Connect SurfaceTexture to callback
51165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mSurfaceTexture.setOnFrameAvailableListener(onMediaFrameAvailableListener);
51265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
51365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Preparing MediaPlayer.");
51465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mMediaPlayer.prepareAsync();
51565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
51665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return true;
51765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
51865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
51965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private MediaPlayer.OnVideoSizeChangedListener onVideoSizeChangedListener =
52065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            new MediaPlayer.OnVideoSizeChangedListener() {
52165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
52265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "MediaPlayer sent dimensions: " + width + " x " + height);
52365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (!mGotSize) {
52465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mOrientation == 0 || mOrientation == 180) {
52565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mOutputFormat.setDimensions(width, height);
52665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                } else {
52765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mOutputFormat.setDimensions(height, width);
52865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
52965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mWidth = width;
53065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mHeight = height;
53165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } else {
53265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mOutputFormat.getWidth() != width ||
53365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mOutputFormat.getHeight() != height) {
53465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    Log.e(TAG, "Multiple video size change events received!");
53565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
53665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
53765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            synchronized(MediaSource.this) {
53865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mGotSize = true;
53965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                MediaSource.this.notify();
54065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
54165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
54265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    };
54365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
54465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private MediaPlayer.OnPreparedListener onPreparedListener =
54565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            new MediaPlayer.OnPreparedListener() {
54665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public void onPrepared(MediaPlayer mp) {
54765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "MediaPlayer is prepared");
54865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            synchronized(MediaSource.this) {
54965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mPrepared = true;
55065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                MediaSource.this.notify();
55165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
55265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
55365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    };
55465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
55565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private MediaPlayer.OnCompletionListener onCompletionListener =
55665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            new MediaPlayer.OnCompletionListener() {
55765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public void onCompletion(MediaPlayer mp) {
55865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "MediaPlayer has completed playback");
55965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            synchronized(MediaSource.this) {
56065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mCompleted = true;
56165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
56265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
56365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    };
56465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
56565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private SurfaceTexture.OnFrameAvailableListener onMediaFrameAvailableListener =
56665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            new SurfaceTexture.OnFrameAvailableListener() {
56765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public void onFrameAvailable(SurfaceTexture surfaceTexture) {
56865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "New frame from media player");
56965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            synchronized(MediaSource.this) {
57065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mLogVerbose) Log.v(TAG, "New frame: notify");
57165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mNewFrameAvailable = true;
57265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                MediaSource.this.notify();
57365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mLogVerbose) Log.v(TAG, "New frame: notify done");
57465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
57565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
57665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    };
57765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
57865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn}
579