1071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia/* 2071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Copyright (C) 2015 The Android Open Source Project 3071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 4071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Licensed under the Apache License, Version 2.0 (the "License"); 5071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * you may not use this file except in compliance with the License. 6071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * You may obtain a copy of the License at 7071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 8071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * http://www.apache.org/licenses/LICENSE-2.0 9071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 10071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Unless required by applicable law or agreed to in writing, software 11071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * distributed under the License is distributed on an "AS IS" BASIS, 12071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * See the License for the specific language governing permissions and 14071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * limitations under the License. 15071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 16071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 17071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiapackage android.media; 18071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 19071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport android.annotation.IntDef; 20217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jiaimport android.annotation.NonNull; 2199f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnarimport android.annotation.Nullable; 22071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport android.media.AudioTrack; 232d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jiaimport android.media.PlaybackParams; 24071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport android.os.Handler; 25071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport android.os.Looper; 26071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport android.os.Message; 27d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnarimport android.util.Log; 28071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport android.view.Surface; 29071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 30071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.lang.annotation.Retention; 31071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.lang.annotation.RetentionPolicy; 32071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.nio.ByteBuffer; 3325b802d47249702b9e5d175b3e7144934b67553dWei Jiaimport java.util.concurrent.TimeUnit; 34071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.util.LinkedList; 35071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.util.List; 36071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 37071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia/** 38071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * MediaSync class can be used to synchronously playback audio and video streams. 39071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * It can be used to play audio-only or video-only stream, too. 40071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 41071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p>MediaSync is generally used like this: 42071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <pre> 43071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * MediaSync sync = new MediaSync(); 44aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * sync.setSurface(surface); 45071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Surface inputSurface = sync.createInputSurface(); 46071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * ... 47071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // MediaCodec videoDecoder = ...; 48071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * videoDecoder.configure(format, inputSurface, ...); 49071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * ... 50aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * sync.setAudioTrack(audioTrack); 51071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync.setCallback(new MediaSync.Callback() { 52ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia * {@literal @Override} 539e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * public void onAudioBufferConsumed(MediaSync sync, ByteBuffer audioBuffer, int bufferId) { 54071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * ... 55071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * } 56161537c81664613c79a6397759aa9baa5db6dd64Wei Jia * }, null); 57071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // This needs to be done since sync is paused on creation. 585d177fff2f20f7805e86054912e5a64163f9ea43Wei Jia * sync.setPlaybackParams(new PlaybackParams().setSpeed(1.f)); 59071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 60071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * for (;;) { 61071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * ... 62071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // send video frames to surface for rendering, e.g., call 63071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // videoDecoder.releaseOutputBuffer(videoOutputBufferIx, videoPresentationTimeNs); 64071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // More details are available as below. 65071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * ... 669e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * sync.queueAudio(audioByteBuffer, bufferId, audioPresentationTimeUs); // non-blocking. 679e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * // The audioByteBuffer and bufferId will be returned via callback. 68071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // More details are available as below. 69071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * ... 70071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * ... 71071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * } 725d177fff2f20f7805e86054912e5a64163f9ea43Wei Jia * sync.setPlaybackParams(new PlaybackParams().setSpeed(0.f)); 73071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync.release(); 74071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync = null; 75071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 76071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // The following code snippet illustrates how video/audio raw frames are created by 77071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // MediaCodec's, how they are fed to MediaSync and how they are returned by MediaSync. 78071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // This is the callback from MediaCodec. 799e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * onOutputBufferAvailable(MediaCodec codec, int bufferId, BufferInfo info) { 80071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // ... 81071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * if (codec == videoDecoder) { 82071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // surface timestamp must contain media presentation time in nanoseconds. 839e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * codec.releaseOutputBuffer(bufferId, 1000 * info.presentationTime); 84071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * } else { 859e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * ByteBuffer audioByteBuffer = codec.getOutputBuffer(bufferId); 86d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar * sync.queueAudio(audioByteBuffer, bufferId, info.presentationTime); 87071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * } 88071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // ... 89071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * } 90071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 91071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // This is the callback from MediaSync. 929e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * onAudioBufferConsumed(MediaSync sync, ByteBuffer buffer, int bufferId) { 93071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // ... 949e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * audioDecoder.releaseBuffer(bufferId, false); 95071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // ... 96071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * } 97071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 98071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * </pre> 99071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 100aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * The client needs to configure corresponding sink by setting the Surface and/or AudioTrack 101aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * based on the stream type it will play. 102071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p> 103071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * For video, the client needs to call {@link #createInputSurface} to obtain a surface on 104071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * which it will render video frames. 105071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p> 106071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * For audio, the client needs to set up audio track correctly, e.g., using {@link 107071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * AudioTrack#MODE_STREAM}. The audio buffers are sent to MediaSync directly via {@link 108e0112b51a1e966192feadedcf72f65cb63a214dcLajos Molnar * #queueAudio}, and are returned to the client via {@link Callback#onAudioBufferConsumed} 109071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * asynchronously. The client should not modify an audio buffer till it's returned. 110071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p> 111071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * The client can optionally pre-fill audio/video buffers by setting playback rate to 0.0, 112071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * and then feed audio/video buffers to corresponding components. This can reduce possible 113071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * initial underrun. 114071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p> 115071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 1162d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jiapublic final class MediaSync { 117071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia /** 118071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * MediaSync callback interface. Used to notify the user asynchronously 119071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * of various MediaSync events. 120071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 121071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia public static abstract class Callback { 122071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia /** 123071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Called when returning an audio buffer which has been consumed. 124071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 125071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * @param sync The MediaSync object. 126071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * @param audioBuffer The returned audio buffer. 1279e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * @param bufferId The ID associated with audioBuffer as passed into 1289e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * {@link MediaSync#queueAudio}. 129071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 130e0112b51a1e966192feadedcf72f65cb63a214dcLajos Molnar public abstract void onAudioBufferConsumed( 1319e62c751323438c873f3e3e500cf4987667d28f3Wei Jia @NonNull MediaSync sync, @NonNull ByteBuffer audioBuffer, int bufferId); 132071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 133071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 134746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia /** Audio track failed. 135746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * @see android.media.MediaSync.OnErrorListener 136746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia */ 137746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia public static final int MEDIASYNC_ERROR_AUDIOTRACK_FAIL = 1; 138746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia 139746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia /** The surface failed to handle video buffers. 140746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * @see android.media.MediaSync.OnErrorListener 141746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia */ 142746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia public static final int MEDIASYNC_ERROR_SURFACE_FAIL = 2; 143746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia 144746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia /** 145746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * Interface definition of a callback to be invoked when there 146746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * has been an error during an asynchronous operation (other errors 147746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * will throw exceptions at method call time). 148746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia */ 149746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia public interface OnErrorListener { 150746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia /** 151746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * Called to indicate an error. 152746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * 153746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * @param sync The MediaSync the error pertains to 154746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * @param what The type of error that has occurred: 155746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * <ul> 156746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * <li>{@link #MEDIASYNC_ERROR_AUDIOTRACK_FAIL} 157746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * <li>{@link #MEDIASYNC_ERROR_SURFACE_FAIL} 158746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * </ul> 159746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * @param extra an extra code, specific to the error. Typically 160746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * implementation dependent. 161746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia */ 162746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia void onError(@NonNull MediaSync sync, int what, int extra); 163746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia } 164746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia 165071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private static final String TAG = "MediaSync"; 166071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 167071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private static final int EVENT_CALLBACK = 1; 168071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private static final int EVENT_SET_CALLBACK = 2; 169071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 170071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private static final int CB_RETURN_AUDIO_BUFFER = 1; 171071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 172071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private static class AudioBuffer { 173071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia public ByteBuffer mByteBuffer; 174071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia public int mBufferIndex; 175071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia long mPresentationTimeUs; 176071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 1779e62c751323438c873f3e3e500cf4987667d28f3Wei Jia public AudioBuffer(@NonNull ByteBuffer byteBuffer, int bufferId, 1782d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia long presentationTimeUs) { 179071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mByteBuffer = byteBuffer; 1809e62c751323438c873f3e3e500cf4987667d28f3Wei Jia mBufferIndex = bufferId; 181071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mPresentationTimeUs = presentationTimeUs; 182071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 183071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 184071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 185071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private final Object mCallbackLock = new Object(); 186071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private Handler mCallbackHandler = null; 187071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private MediaSync.Callback mCallback = null; 188071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 189746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia private final Object mOnErrorListenerLock = new Object(); 190746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia private Handler mOnErrorListenerHandler = null; 191746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia private MediaSync.OnErrorListener mOnErrorListener = null; 192746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia 193071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private Thread mAudioThread = null; 194071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia // Created on mAudioThread when mAudioThread is started. When used on user thread, they should 195071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia // be guarded by checking mAudioThread. 196071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private Handler mAudioHandler = null; 197071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private Looper mAudioLooper = null; 198071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 199071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private final Object mAudioLock = new Object(); 200071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private AudioTrack mAudioTrack = null; 201071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private List<AudioBuffer> mAudioBuffers = new LinkedList<AudioBuffer>(); 20205ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar // this is only used for paused/running decisions, so it is not affected by clock drift 203071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private float mPlaybackRate = 0.0f; 204071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 205071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private long mNativeContext; 206071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 207071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia /** 208071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Class constructor. On creation, MediaSync is paused, i.e., playback rate is 0.0f. 209071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 210071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia public MediaSync() { 211071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia native_setup(); 212071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 213071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 214071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private native final void native_setup(); 215071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 216071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia @Override 217071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia protected void finalize() { 218071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia native_finalize(); 219071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 220071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 221071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private native final void native_finalize(); 222071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 223071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia /** 224071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Make sure you call this when you're done to free up any opened 225071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * component instance instead of relying on the garbage collector 226071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * to do this for you at some point in the future. 227071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 228071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia public final void release() { 229071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia returnAudioBuffers(); 230071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (mAudioThread != null) { 231071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (mAudioLooper != null) { 232071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mAudioLooper.quit(); 233071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 234071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 235071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia setCallback(null, null); 236071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia native_release(); 237071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 238071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 239071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private native final void native_release(); 240071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 241071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia /** 242071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Sets an asynchronous callback for actionable MediaSync events. 24399f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar * <p> 24499f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar * This method can be called multiple times to update a previously set callback. If the 24599f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar * handler is changed, undelivered notifications scheduled for the old handler may be dropped. 24699f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar * <p> 24799f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar * <b>Do not call this inside callback.</b> 248071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 24999f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar * @param cb The callback that will run. Use {@code null} to stop receiving callbacks. 25099f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar * @param handler The Handler that will run the callback. Use {@code null} to use MediaSync's 251071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * internal handler if it exists. 252071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 25399f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar public void setCallback(@Nullable /* MediaSync. */ Callback cb, @Nullable Handler handler) { 254071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia synchronized(mCallbackLock) { 255071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (handler != null) { 256071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mCallbackHandler = handler; 257071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } else { 258071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia Looper looper; 259071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if ((looper = Looper.myLooper()) == null) { 260071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia looper = Looper.getMainLooper(); 261071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 262071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (looper == null) { 263071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mCallbackHandler = null; 264071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } else { 265071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mCallbackHandler = new Handler(looper); 266071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 267071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 268071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 269071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mCallback = cb; 270071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 271071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 272071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 273071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia /** 274746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * Sets an asynchronous callback for error events. 275746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * <p> 276746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * This method can be called multiple times to update a previously set listener. If the 277746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * handler is changed, undelivered notifications scheduled for the old handler may be dropped. 278746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * <p> 279746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * <b>Do not call this inside callback.</b> 280746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * 281746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * @param listener The callback that will run. Use {@code null} to stop receiving callbacks. 282746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * @param handler The Handler that will run the callback. Use {@code null} to use MediaSync's 283746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia * internal handler if it exists. 284746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia */ 285746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia public void setOnErrorListener(@Nullable /* MediaSync. */ OnErrorListener listener, 286746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia @Nullable Handler handler) { 287746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia synchronized(mOnErrorListenerLock) { 288746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia if (handler != null) { 289746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia mOnErrorListenerHandler = handler; 290746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia } else { 291746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia Looper looper; 292746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia if ((looper = Looper.myLooper()) == null) { 293746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia looper = Looper.getMainLooper(); 294746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia } 295746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia if (looper == null) { 296746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia mOnErrorListenerHandler = null; 297746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia } else { 298746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia mOnErrorListenerHandler = new Handler(looper); 299746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia } 300746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia } 301746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia 302746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia mOnErrorListener = listener; 303746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia } 304746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia } 305746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia 306746306521aa4a22fafaa0afee0993cd4d31d5d4eWei Jia /** 307aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * Sets the output surface for MediaSync. 308aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * <p> 309aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * Currently, this is only supported in the Initialized state. 310071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 311071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * @param surface Specify a surface on which to render the video data. 312aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * @throws IllegalArgumentException if the surface has been released, is invalid, 313071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * or can not be connected. 314aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * @throws IllegalStateException if setting the surface is not supported, e.g. 315d80d6f6b056e23a8d017fe8f3900f6a88e27aa1bWei Jia * not in the Initialized state, or another surface has already been set. 316071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 317aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar public void setSurface(@Nullable Surface surface) { 318d80d6f6b056e23a8d017fe8f3900f6a88e27aa1bWei Jia native_setSurface(surface); 319071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 320071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 321d80d6f6b056e23a8d017fe8f3900f6a88e27aa1bWei Jia private native final void native_setSurface(@Nullable Surface surface); 322071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 323071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia /** 324aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * Sets the audio track for MediaSync. 325aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * <p> 326aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * Currently, this is only supported in the Initialized state. 327071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 328071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * @param audioTrack Specify an AudioTrack through which to render the audio data. 329ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia * @throws IllegalArgumentException if the audioTrack has been released, or is invalid. 330aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * @throws IllegalStateException if setting the audio track is not supported, e.g. 331d80d6f6b056e23a8d017fe8f3900f6a88e27aa1bWei Jia * not in the Initialized state, or another audio track has already been set. 332071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 333aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar public void setAudioTrack(@Nullable AudioTrack audioTrack) { 334d80d6f6b056e23a8d017fe8f3900f6a88e27aa1bWei Jia native_setAudioTrack(audioTrack); 335071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mAudioTrack = audioTrack; 336ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia if (audioTrack != null && mAudioThread == null) { 337071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia createAudioThread(); 338071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 339071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 340071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 341d80d6f6b056e23a8d017fe8f3900f6a88e27aa1bWei Jia private native final void native_setAudioTrack(@Nullable AudioTrack audioTrack); 342071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 343071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia /** 344071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Requests a Surface to use as the input. This may only be called after 345aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * {@link #setSurface}. 346071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p> 347071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * The application is responsible for calling release() on the Surface when 348071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * done. 349d80d6f6b056e23a8d017fe8f3900f6a88e27aa1bWei Jia * @throws IllegalStateException if not set, or another input surface has 350071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * already been created. 351071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 35299f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar @NonNull 353071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia public native final Surface createInputSurface(); 354071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 355071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia /** 3562d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia * Sets playback rate using {@link PlaybackParams}. 357b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * <p> 3582d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia * When using MediaSync with {@link AudioTrack}, set playback params using this 359b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * call instead of calling it directly on the track, so that the sync is aware of 3602d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia * the params change. 361b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * <p> 362b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * This call also works if there is no audio track. 363071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 3642d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia * @param params the playback params to use. {@link PlaybackParams#getSpeed 365b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * Speed} is the ratio between desired playback rate and normal one. 1.0 means 366b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * normal playback speed. 0.0 means pause. Value larger than 1.0 means faster playback, 367b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * while value between 0.0 and 1.0 for slower playback. <b>Note:</b> the normal rate 368b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * does not change as a result of this call. To restore the original rate at any time, 369b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * use speed of 1.0. 370071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * 371071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * @throws IllegalStateException if the internal sync engine or the audio track has not 372071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * been initialized. 3732d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia * @throws IllegalArgumentException if the params are not supported. 374071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 3752d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia public void setPlaybackParams(@NonNull PlaybackParams params) { 37605ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar synchronized(mAudioLock) { 3772d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia mPlaybackRate = native_setPlaybackParams(params);; 37805ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar } 37905ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar if (mPlaybackRate != 0.0 && mAudioThread != null) { 38005ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar postRenderAudio(0); 381071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 382b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar } 383071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 384b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar /** 3852d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia * Gets the playback rate using {@link PlaybackParams}. 386b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * 387b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * @return the playback rate being used. 388b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * 389b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * @throws IllegalStateException if the internal sync engine or the audio track has not 390b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar * been initialized. 391b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar */ 392b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar @NonNull 3932d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia public native PlaybackParams getPlaybackParams(); 394071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 3952d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia private native float native_setPlaybackParams(@NonNull PlaybackParams params); 396071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 397c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar /** 398c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * Sets A/V sync mode. 399c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * 4002d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia * @param params the A/V sync params to apply 401c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * 402c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * @throws IllegalStateException if the internal player engine has not been 403c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * initialized. 4042d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia * @throws IllegalArgumentException if params are not supported. 405c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar */ 4062d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia public void setSyncParams(@NonNull SyncParams params) { 40705ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar synchronized(mAudioLock) { 4082d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia mPlaybackRate = native_setSyncParams(params);; 40905ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar } 41005ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar if (mPlaybackRate != 0.0 && mAudioThread != null) { 41105ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar postRenderAudio(0); 41205ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar } 41305ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar } 41405ebffe6bf6cdacc6de4b3bbf480c31869a81661Lajos Molnar 4152d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia private native float native_setSyncParams(@NonNull SyncParams params); 416c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar 417c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar /** 418c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * Gets the A/V sync mode. 419c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * 4202d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia * @return the A/V sync params 421c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * 422c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * @throws IllegalStateException if the internal player engine has not been 423c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * initialized. 424c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar */ 425c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar @NonNull 4262d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia public native SyncParams getSyncParams(); 427c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar 428c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar /** 429c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * Flushes all buffers from the sync object. 430c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * <p> 431d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar * All pending unprocessed audio and video buffers are discarded. If an audio track was 432d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar * configured, it is flushed and stopped. If a video output surface was configured, the 433d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar * last frame queued to it is left on the frame. Queue a blank video frame to clear the 434d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar * surface, 435d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar * <p> 436c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * No callbacks are received for the flushed buffers. 437c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * 438c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * @throws IllegalStateException if the internal player engine has not been 439c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar * initialized. 440c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar */ 441c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar public void flush() { 442c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar synchronized(mAudioLock) { 443c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar mAudioBuffers.clear(); 444c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar mCallbackHandler.removeCallbacksAndMessages(null); 445c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar } 446d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar if (mAudioTrack != null) { 447d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar mAudioTrack.pause(); 448d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar mAudioTrack.flush(); 449d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar // Call stop() to signal to the AudioSink to completely fill the 450d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar // internal buffer before resuming playback. 451d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar mAudioTrack.stop(); 452d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar } 453d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar native_flush(); 454c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar } 455c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar 456d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar private native final void native_flush(); 457d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar 458d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar /** 45905a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * Get current playback position. 46005a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * <p> 46105a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * The MediaTimestamp represents how the media time correlates to the system time in 46205a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * a linear fashion using an anchor and a clock rate. During regular playback, the media 46305a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * time moves fairly constantly (though the anchor frame may be rebased to a current 46405a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * system time, the linear correlation stays steady). Therefore, this method does not 46505a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * need to be called often. 46605a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * <p> 46705a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * To help users get current playback position, this method always anchors the timestamp 46805a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * to the current {@link System#nanoTime system time}, so 46905a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * {@link MediaTimestamp#getAnchorMediaTimeUs} can be used as current playback position. 47005a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * 47105a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp 47205a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * is available, e.g. because the media player has not been initialized. 47305a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * 47405a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar * @see MediaTimestamp 47505a822161fe0296b01f573192adf306c0ce38c9cLajos Molnar */ 4767f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar @Nullable 4777f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar public MediaTimestamp getTimestamp() 478217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia { 4797f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar try { 4807f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar // TODO: create the timestamp in native 4817f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar MediaTimestamp timestamp = new MediaTimestamp(); 4827f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar if (native_getTimestamp(timestamp)) { 4837f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar return timestamp; 4847f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar } else { 4857f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar return null; 4867f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar } 4877f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar } catch (IllegalStateException e) { 4887f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar return null; 489217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia } 490217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia } 491217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia 49299f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar private native final boolean native_getTimestamp(@NonNull MediaTimestamp timestamp); 493217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia 494071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia /** 495071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Queues the audio data asynchronously for playback (AudioTrack must be in streaming mode). 496d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar * If the audio track was flushed as a result of {@link #flush}, it will be restarted. 497071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * @param audioData the buffer that holds the data to play. This buffer will be returned 498071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * to the client via registered callback. 4999e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * @param bufferId an integer used to identify audioData. It will be returned to 5009e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * the client along with audioData. This helps applications to keep track of audioData, 5019e62c751323438c873f3e3e500cf4987667d28f3Wei Jia * e.g., it can be used to store the output buffer index used by the audio codec. 502071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * @param presentationTimeUs the presentation timestamp in microseconds for the first frame 503071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * in the buffer. 504d80d6f6b056e23a8d017fe8f3900f6a88e27aa1bWei Jia * @throws IllegalStateException if audio track is not set or internal configureation 505071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * has not been done correctly. 506071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */ 507071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia public void queueAudio( 5089e62c751323438c873f3e3e500cf4987667d28f3Wei Jia @NonNull ByteBuffer audioData, int bufferId, long presentationTimeUs) { 509071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (mAudioTrack == null || mAudioThread == null) { 510071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia throw new IllegalStateException( 511d80d6f6b056e23a8d017fe8f3900f6a88e27aa1bWei Jia "AudioTrack is NOT set or audio thread is not created"); 512071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 513071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 514071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia synchronized(mAudioLock) { 5159e62c751323438c873f3e3e500cf4987667d28f3Wei Jia mAudioBuffers.add(new AudioBuffer(audioData, bufferId, presentationTimeUs)); 516071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 517071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 518071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (mPlaybackRate != 0.0) { 519071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia postRenderAudio(0); 520071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 521071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 522071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 523071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia // When called on user thread, make sure to check mAudioThread != null. 524071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private void postRenderAudio(long delayMillis) { 525071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mAudioHandler.postDelayed(new Runnable() { 526071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia public void run() { 527071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia synchronized(mAudioLock) { 528071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (mPlaybackRate == 0.0) { 529071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia return; 530071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 531071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 532071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (mAudioBuffers.isEmpty()) { 533071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia return; 534071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 535071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 536071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia AudioBuffer audioBuffer = mAudioBuffers.get(0); 5372d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia int size = audioBuffer.mByteBuffer.remaining(); 538d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar // restart audio track after flush 539d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar if (size > 0 && mAudioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) { 540d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar try { 541d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar mAudioTrack.play(); 542d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar } catch (IllegalStateException e) { 543d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar Log.w(TAG, "could not start audio track"); 544d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar } 545d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar } 546071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia int sizeWritten = mAudioTrack.write( 547071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia audioBuffer.mByteBuffer, 5482d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia size, 549071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia AudioTrack.WRITE_NON_BLOCKING); 550071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (sizeWritten > 0) { 551071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (audioBuffer.mPresentationTimeUs != -1) { 552071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia native_updateQueuedAudioData( 5532d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia size, audioBuffer.mPresentationTimeUs); 554071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia audioBuffer.mPresentationTimeUs = -1; 555071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 556071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 5572d61e2b97c92ac2de80ebb3782b728ae5cdf5306Wei Jia if (sizeWritten == size) { 558071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia postReturnByteBuffer(audioBuffer); 559071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mAudioBuffers.remove(0); 560071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (!mAudioBuffers.isEmpty()) { 561071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia postRenderAudio(0); 562071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 563071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia return; 564071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 565071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 56625b802d47249702b9e5d175b3e7144934b67553dWei Jia long pendingTimeMs = TimeUnit.MICROSECONDS.toMillis( 56725b802d47249702b9e5d175b3e7144934b67553dWei Jia native_getPlayTimeForPendingAudioFrames()); 56825b802d47249702b9e5d175b3e7144934b67553dWei Jia postRenderAudio(pendingTimeMs / 2); 569071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 570071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 571071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia }, delayMillis); 572071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 573071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 574071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private native final void native_updateQueuedAudioData( 575071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia int sizeInBytes, long presentationTimeUs); 576071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 57725b802d47249702b9e5d175b3e7144934b67553dWei Jia private native final long native_getPlayTimeForPendingAudioFrames(); 57825b802d47249702b9e5d175b3e7144934b67553dWei Jia 57999f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar private final void postReturnByteBuffer(@NonNull final AudioBuffer audioBuffer) { 580071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia synchronized(mCallbackLock) { 581071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (mCallbackHandler != null) { 582071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia final MediaSync sync = this; 583071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mCallbackHandler.post(new Runnable() { 584071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia public void run() { 585d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar Callback callback; 586071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia synchronized(mCallbackLock) { 587d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar callback = mCallback; 588071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia if (mCallbackHandler == null 589071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia || mCallbackHandler.getLooper().getThread() 590071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia != Thread.currentThread()) { 591071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia // callback handler has been changed. 592071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia return; 593071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 594d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar } 595d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar if (callback != null) { 596d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar callback.onAudioBufferConsumed(sync, audioBuffer.mByteBuffer, 597d08debcf42d820fa8ef9916077a7bfc0a36f2db5Lajos Molnar audioBuffer.mBufferIndex); 598071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 599071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 600071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia }); 601071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 602071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 603071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 604071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 605071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private final void returnAudioBuffers() { 606071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia synchronized(mAudioLock) { 607071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia for (AudioBuffer audioBuffer: mAudioBuffers) { 608071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia postReturnByteBuffer(audioBuffer); 609071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 610071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mAudioBuffers.clear(); 611071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 612071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 613071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 614071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private void createAudioThread() { 615071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mAudioThread = new Thread() { 616071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia @Override 617071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia public void run() { 618071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia Looper.prepare(); 619071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia synchronized(mAudioLock) { 620071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mAudioLooper = Looper.myLooper(); 621071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mAudioHandler = new Handler(); 622071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mAudioLock.notify(); 623071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 624071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia Looper.loop(); 625071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 626071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia }; 627071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mAudioThread.start(); 628071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 629071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia synchronized(mAudioLock) { 630071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia try { 631071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia mAudioLock.wait(); 632071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } catch(InterruptedException e) { 633071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 634071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 635071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 636071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 637071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia static { 638071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia System.loadLibrary("media_jni"); 639071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia native_init(); 640071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia } 641071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia 642071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia private static native final void native_init(); 643071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia} 644