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