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