MediaSync.java revision 7f08763f4140822c30d0e18d4d3939488c8d26f8
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;
23b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnarimport android.media.PlaybackSettings;
24071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport android.os.Handler;
25071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport android.os.Looper;
26071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport android.os.Message;
27071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport android.view.Surface;
28071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
29071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.lang.annotation.Retention;
30071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.lang.annotation.RetentionPolicy;
31071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.nio.ByteBuffer;
32071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.util.LinkedList;
33071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.util.List;
34071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
35071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia/**
36071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * MediaSync class can be used to synchronously playback audio and video streams.
37071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * It can be used to play audio-only or video-only stream, too.
38071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
39071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p>MediaSync is generally used like this:
40071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <pre>
41071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * MediaSync sync = new MediaSync();
42071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync.configureSurface(surface);
43071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Surface inputSurface = sync.createInputSurface();
44071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * ...
45071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // MediaCodec videoDecoder = ...;
46071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * videoDecoder.configure(format, inputSurface, ...);
47071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * ...
48ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia * sync.configureAudioTrack(audioTrack);
49071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync.setCallback(new MediaSync.Callback() {
50ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia *     {@literal @Override}
51071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     public void onReturnAudioBuffer(MediaSync sync, ByteBuffer audioBuffer, int bufferIndex) {
52071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *         ...
53071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     }
54161537c81664613c79a6397759aa9baa5db6dd64Wei Jia * }, null);
55071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // This needs to be done since sync is paused on creation.
56071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync.setPlaybackRate(1.0f, MediaSync.PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
57071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
58071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * for (;;) {
59071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   ...
60071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   // send video frames to surface for rendering, e.g., call
61071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   // videoDecoder.releaseOutputBuffer(videoOutputBufferIx, videoPresentationTimeNs);
62071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   // More details are available as below.
63071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   ...
64071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   sync.queueAudio(audioByteBuffer, bufferIndex, size, audioPresentationTimeUs); // non-blocking.
65071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   // The audioByteBuffer and bufferIndex will be returned via callback.
66071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   // More details are available as below.
67071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   ...
68071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     ...
69071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * }
70071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync.setPlaybackRate(0.0f, MediaSync.PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
71071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync.release();
72071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync = null;
73071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
74071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // The following code snippet illustrates how video/audio raw frames are created by
75071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // MediaCodec's, how they are fed to MediaSync and how they are returned by MediaSync.
76071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // This is the callback from MediaCodec.
77071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * onOutputBufferAvailable(MediaCodec codec, int bufferIndex, BufferInfo info) {
78071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     // ...
79071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     if (codec == videoDecoder) {
80071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *         // surface timestamp must contain media presentation time in nanoseconds.
81071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *         codec.releaseOutputBuffer(bufferIndex, 1000 * info.presentationTime);
82071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     } else {
83071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *         ByteBuffer audioByteBuffer = codec.getOutputBuffer(bufferIndex);
84071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *         sync.queueByteBuffer(audioByteBuffer, bufferIndex, info.size, info.presentationTime);
85071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     }
86071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     // ...
87071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * }
88071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
89071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // This is the callback from MediaSync.
90071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * onReturnAudioBuffer(MediaSync sync, ByteBuffer buffer, int bufferIndex) {
91071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     // ...
92071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     audioDecoder.releaseBuffer(bufferIndex, false);
93071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     // ...
94071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * }
95071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
96071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * </pre>
97071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
98071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * The client needs to configure corresponding sink (i.e., Surface and AudioTrack) based on
99071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * the stream type it will play.
100071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p>
101071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * For video, the client needs to call {@link #createInputSurface} to obtain a surface on
102071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * which it will render video frames.
103071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p>
104071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * For audio, the client needs to set up audio track correctly, e.g., using {@link
105071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * AudioTrack#MODE_STREAM}. The audio buffers are sent to MediaSync directly via {@link
106071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * #queueAudio}, and are returned to the client via {@link Callback#onReturnAudioBuffer}
107071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * asynchronously. The client should not modify an audio buffer till it's returned.
108071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p>
109071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * The client can optionally pre-fill audio/video buffers by setting playback rate to 0.0,
110071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * and then feed audio/video buffers to corresponding components. This can reduce possible
111071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * initial underrun.
112071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p>
113071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */
114071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiafinal public class MediaSync {
115071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
116071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * MediaSync callback interface. Used to notify the user asynchronously
117071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * of various MediaSync events.
118071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
119071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public static abstract class Callback {
120071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        /**
121071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia         * Called when returning an audio buffer which has been consumed.
122071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia         *
123071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia         * @param sync The MediaSync object.
124071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia         * @param audioBuffer The returned audio buffer.
12599f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar         * @param bufferIndex The index associated with the audio buffer
126071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia         */
127071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        public abstract void onReturnAudioBuffer(
12899f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar                @NonNull MediaSync sync, @NonNull ByteBuffer audioBuffer, int bufferIndex);
129071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
130071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
131071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static final String TAG = "MediaSync";
132071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
133071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static final int EVENT_CALLBACK = 1;
134071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static final int EVENT_SET_CALLBACK = 2;
135071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
136071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static final int CB_RETURN_AUDIO_BUFFER = 1;
137071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
138071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static class AudioBuffer {
139071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        public ByteBuffer mByteBuffer;
140071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        public int mBufferIndex;
141071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        public int mSizeInBytes;
142071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        long mPresentationTimeUs;
143071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
14499f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar        public AudioBuffer(@NonNull ByteBuffer byteBuffer, int bufferIndex,
145071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                           int sizeInBytes, long presentationTimeUs) {
146071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mByteBuffer = byteBuffer;
147071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mBufferIndex = bufferIndex;
148071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mSizeInBytes = sizeInBytes;
149071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mPresentationTimeUs = presentationTimeUs;
150071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
151071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
152071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
153071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private final Object mCallbackLock = new Object();
154071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private Handler mCallbackHandler = null;
155071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private MediaSync.Callback mCallback = null;
156071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
157071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private Thread mAudioThread = null;
158071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    // Created on mAudioThread when mAudioThread is started. When used on user thread, they should
159071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    // be guarded by checking mAudioThread.
160071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private Handler mAudioHandler = null;
161071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private Looper mAudioLooper = null;
162071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
163071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private final Object mAudioLock = new Object();
164071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private AudioTrack mAudioTrack = null;
165071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private List<AudioBuffer> mAudioBuffers = new LinkedList<AudioBuffer>();
166071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private float mPlaybackRate = 0.0f;
167071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
168071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private long mNativeContext;
169071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
170071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
171071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Class constructor. On creation, MediaSync is paused, i.e., playback rate is 0.0f.
172071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
173071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public MediaSync() {
174071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_setup();
175071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
176071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
177071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_setup();
178071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
179071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    @Override
180071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    protected void finalize() {
181071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_finalize();
182071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
183071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
184071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_finalize();
185071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
186071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
187071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Make sure you call this when you're done to free up any opened
188071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * component instance instead of relying on the garbage collector
189071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * to do this for you at some point in the future.
190071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
191071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public final void release() {
192071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        returnAudioBuffers();
193071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        if (mAudioThread != null) {
194071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            if (mAudioLooper != null) {
195071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                mAudioLooper.quit();
196071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
197071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
198071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        setCallback(null, null);
199071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_release();
200071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
201071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
202071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_release();
203071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
204071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
205071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Sets an asynchronous callback for actionable MediaSync events.
20699f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * <p>
20799f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * This method can be called multiple times to update a previously set callback. If the
20899f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * handler is changed, undelivered notifications scheduled for the old handler may be dropped.
20999f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * <p>
21099f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * <b>Do not call this inside callback.</b>
211071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
21299f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * @param cb The callback that will run. Use {@code null} to stop receiving callbacks.
21399f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * @param handler The Handler that will run the callback. Use {@code null} to use MediaSync's
214071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     internal handler if it exists.
215071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
21699f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    public void setCallback(@Nullable /* MediaSync. */ Callback cb, @Nullable Handler handler) {
217071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        synchronized(mCallbackLock) {
218071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            if (handler != null) {
219071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                mCallbackHandler = handler;
220071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            } else {
221071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                Looper looper;
222071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                if ((looper = Looper.myLooper()) == null) {
223071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    looper = Looper.getMainLooper();
224071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                }
225071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                if (looper == null) {
226071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    mCallbackHandler = null;
227071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                } else {
228071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    mCallbackHandler = new Handler(looper);
229071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                }
230071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
231071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
232071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mCallback = cb;
233071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
234071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
235071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
236071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
237071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Configures the output surface for MediaSync.
238071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
239071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param surface Specify a surface on which to render the video data.
240071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @throws IllegalArgumentException if the surface has been released, or is invalid.
241071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     or can not be connected.
242071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @throws IllegalStateException if not in the Initialized state, or another surface
243071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     has already been configured.
244071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
24599f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    public void configureSurface(@Nullable Surface surface) {
246071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_configureSurface(surface);
247071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
248071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
24999f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    private native final void native_configureSurface(@Nullable Surface surface);
250071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
251071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
252071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Configures the audio track for MediaSync.
253071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
254071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param audioTrack Specify an AudioTrack through which to render the audio data.
255ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia     * @throws IllegalArgumentException if the audioTrack has been released, or is invalid.
256071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @throws IllegalStateException if not in the Initialized state, or another audio track
257071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     has already been configured.
258071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
25999f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    public void configureAudioTrack(@Nullable AudioTrack audioTrack) {
260ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia        // AudioTrack has sanity check for configured sample rate.
261ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia        int nativeSampleRateInHz = (audioTrack == null ? 0 : audioTrack.getSampleRate());
262ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia
263071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_configureAudioTrack(audioTrack, nativeSampleRateInHz);
264071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        mAudioTrack = audioTrack;
265ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia        if (audioTrack != null && mAudioThread == null) {
266071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            createAudioThread();
267071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
268071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
269071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
270071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_configureAudioTrack(
27199f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar            @Nullable AudioTrack audioTrack, int nativeSampleRateInHz);
272071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
273071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
274071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Requests a Surface to use as the input. This may only be called after
275071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * {@link #configureSurface}.
276071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * <p>
277071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * The application is responsible for calling release() on the Surface when
278071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * done.
279071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @throws IllegalStateException if not configured, or another input surface has
280071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     already been created.
281071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
28299f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    @NonNull
283071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public native final Surface createInputSurface();
284071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
285071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
286b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Resample audio data when changing playback speed.
287b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p>
288b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Resample the waveform based on the requested playback rate to get
289071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * a new waveform, and play back the new waveform at the original sampling
290071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * frequency.
291b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p><ul>
292b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <li>When rate is larger than 1.0, pitch becomes higher.
293b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <li>When rate is smaller than 1.0, pitch becomes lower.
294b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * </ul>
295071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
296b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2;
297071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
298071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
299b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Time stretch audio when changing playback speed.
300b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p>
301071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Time stretching changes the duration of the audio samples without
302b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * affecting their pitch. This is only supported for a limited range
303b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * of playback speeds, e.g. from 1/2x to 2x. If the rate is adjusted
304b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * beyond this limit, the rate change will fail.
305071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
306071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
307071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
308b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    /**
309b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Time stretch audio when changing playback speed, and may mute if
310b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * stretching is no longer supported.
311b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p>
312b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Time stretching changes the duration of the audio samples without
313b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * affecting their pitch. This is only supported for a limited range
314b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * of playback speeds, e.g. from 1/2x to 2x. When it is no longer
315b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * supported, the audio may be muted.  Using this mode will not fail
316b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * for non-negative playback rates.
317b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     */
318b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
319b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar
320071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /** @hide */
321071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    @IntDef(
322071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        value = {
323b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
324b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            PLAYBACK_RATE_AUDIO_MODE_STRETCH,
325071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
326b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        })
327071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    @Retention(RetentionPolicy.SOURCE)
328071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public @interface PlaybackRateAudioMode {}
329071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
330071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
331b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Sets playback rate and audio mode.
332071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
333071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param rate the ratio between desired playback rate and normal one. 1.0 means normal
334b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     playback speed. 0.0 means pause. Value larger than 1.0 means faster playback,
335b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     while value between 0.0 and 1.0 for slower playback. <b>Note:</b> the normal rate
336b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     does not change as a result of this call. To restore the original rate at any time,
337b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     use 1.0.
338b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @param audioMode audio playback mode. Must be one of the supported
339b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     audio modes.
340071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
341071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @throws IllegalStateException if the internal sync engine or the audio track has not
342071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     been initialized.
343b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @throws IllegalArgumentException if audioMode is not supported.
344071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
345b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    public void setPlaybackRate(float rate, @PlaybackRateAudioMode int audioMode) {
346b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        PlaybackSettings rateSettings = new PlaybackSettings();
347b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        rateSettings.allowDefaults();
348b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        switch (audioMode) {
349b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            case PLAYBACK_RATE_AUDIO_MODE_DEFAULT:
350b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                rateSettings.setSpeed(rate).setPitch(1.0f);
351b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                break;
352b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            case PLAYBACK_RATE_AUDIO_MODE_STRETCH:
353b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                rateSettings.setSpeed(rate).setPitch(1.0f)
354b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                        .setAudioFallbackMode(rateSettings.AUDIO_FALLBACK_MODE_FAIL);
355b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                break;
356b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE:
357b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                rateSettings.setSpeed(rate).setPitch(rate);
358b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                break;
359b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            default:
360b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            {
361b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                final String msg = "Audio playback mode " + audioMode + " is not supported";
362b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                throw new IllegalArgumentException(msg);
363b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            }
364b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        }
365b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        setPlaybackSettings(rateSettings);
366071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
367071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
368071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
369b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Sets playback rate using {@link PlaybackSettings}.
370b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p>
371b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * When using MediaSync with {@link AudioTrack}, set playback settings using this
372b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * call instead of calling it directly on the track, so that the sync is aware of
373b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * the settings change.
374b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p>
375b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * This call also works if there is no audio track.
376071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
377b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @param settings the playback settings to use. {@link PlaybackSettings#getSpeed
378b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     Speed} is the ratio between desired playback rate and normal one. 1.0 means
379b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     normal playback speed. 0.0 means pause. Value larger than 1.0 means faster playback,
380b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     while value between 0.0 and 1.0 for slower playback. <b>Note:</b> the normal rate
381b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     does not change as a result of this call. To restore the original rate at any time,
382b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     use speed of 1.0.
383071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
384071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @throws IllegalStateException if the internal sync engine or the audio track has not
385071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     been initialized.
386b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @throws IllegalArgumentException if the settings are not supported.
387071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
388b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    public void setPlaybackSettings(@NonNull PlaybackSettings settings) {
389b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        float rate;
390b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        try {
391b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            rate = settings.getSpeed();
392b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar
393b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            // rate is specified
394b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            if (mAudioTrack != null) {
395b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                try {
396b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                    if (rate == 0.0) {
397b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                        mAudioTrack.pause();
398b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                    } else {
399b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                        mAudioTrack.setPlaybackSettings(settings);
400b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                        mAudioTrack.play();
401b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                    }
402b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                } catch (IllegalStateException e) {
403b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                    throw e;
404071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                }
405071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
406071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
407b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            synchronized(mAudioLock) {
408b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                mPlaybackRate = rate;
409b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            }
410b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            if (mPlaybackRate != 0.0 && mAudioThread != null) {
411b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                postRenderAudio(0);
412b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            }
413b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            native_setPlaybackRate(mPlaybackRate);
414b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        } catch (IllegalStateException e) {
415b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            // rate is not specified; still, propagate settings to audio track
416b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            if (mAudioTrack != null) {
417b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                mAudioTrack.setPlaybackSettings(settings);
418b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            }
419071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
420b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    }
421071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
422b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    /**
423b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Gets the playback rate using {@link PlaybackSettings}.
424b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *
425b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @return the playback rate being used.
426b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *
427b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @throws IllegalStateException if the internal sync engine or the audio track has not
428b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     been initialized.
429b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     */
430b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    @NonNull
431b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    public PlaybackSettings getPlaybackSettings() {
432b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        if (mAudioTrack != null) {
433b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            return mAudioTrack.getPlaybackSettings();
434b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        } else {
435b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            PlaybackSettings settings = new PlaybackSettings();
436b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            settings.allowDefaults();
437b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            settings.setSpeed(mPlaybackRate);
438b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            return settings;
439071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
440071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
441071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
442071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_setPlaybackRate(float rate);
443071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
444c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    /**
445c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * Sets A/V sync mode.
446c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     *
447c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @param settings the A/V sync settings to apply
448c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     *
449c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @throws IllegalStateException if the internal player engine has not been
450c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * initialized.
451c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @throws IllegalArgumentException if settings are not supported.
452c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     */
453c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    public native void setSyncSettings(@NonNull SyncSettings settings);
454c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar
455c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    /**
456c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * Gets the A/V sync mode.
457c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     *
458c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @return the A/V sync settings
459c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     *
460c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @throws IllegalStateException if the internal player engine has not been
461c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * initialized.
462c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     */
463c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    @NonNull
464c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    public native SyncSettings getSyncSettings();
465c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar
466c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    /**
467c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * Flushes all buffers from the sync object.
468c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * <p>
469c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * No callbacks are received for the flushed buffers.
470c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     *
471c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @throws IllegalStateException if the internal player engine has not been
472c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * initialized.
473c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     */
474c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    public void flush() {
475c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar        synchronized(mAudioLock) {
476c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar            mAudioBuffers.clear();
477c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar            mCallbackHandler.removeCallbacksAndMessages(null);
478c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar        }
479c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar        // TODO implement this for surface buffers.
480c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    }
481c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar
482217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia   /**
483217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * Get current playback position.
484217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * <p>
4857f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * The MediaTimestamp represents how the media time correlates to the system time in
4867f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * a linear fashion. It contains the media time and system timestamp of an anchor frame
4877f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * ({@link MediaTimestamp#mediaTimeUs} and {@link MediaTimestamp#nanoTime})
4887f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * and the speed of the media clock ({@link MediaTimestamp#clockRate}).
4897f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * <p>
4907f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * During regular playback, the media time moves fairly constantly (though the
4917f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * anchor frame may be rebased to a current system time, the linear correlation stays
4927f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * steady). Therefore, this method does not need to be called often.
493217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * <p>
494217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * To help users to get current playback position, this method always returns the timestamp of
495217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * just-rendered frame, i.e., {@link System#nanoTime} and its corresponding media time. They
496217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * can be used as current playback position.
497217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    *
4987f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp
4997f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    *         is available, e.g. because the media sync has not been initialized.
500217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    */
5017f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    @Nullable
5027f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    public MediaTimestamp getTimestamp()
503217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    {
5047f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar        try {
5057f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            // TODO: create the timestamp in native
5067f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            MediaTimestamp timestamp = new MediaTimestamp();
5077f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            if (native_getTimestamp(timestamp)) {
5087f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar                return timestamp;
5097f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            } else {
5107f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar                return null;
5117f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            }
5127f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar        } catch (IllegalStateException e) {
5137f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            return null;
514217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia        }
515217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    }
516217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia
51799f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    private native final boolean native_getTimestamp(@NonNull MediaTimestamp timestamp);
518217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia
519071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
520071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Queues the audio data asynchronously for playback (AudioTrack must be in streaming mode).
521071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param audioData the buffer that holds the data to play. This buffer will be returned
522071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     to the client via registered callback.
523071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param bufferIndex the buffer index used to identify audioData. It will be returned to
524071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     the client along with audioData. This helps applications to keep track of audioData.
525071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param sizeInBytes number of bytes to queue.
526071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param presentationTimeUs the presentation timestamp in microseconds for the first frame
527071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     in the buffer.
528071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @throws IllegalStateException if audio track is not configured or internal configureation
529071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     has not been done correctly.
530071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
531071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public void queueAudio(
53299f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar            @NonNull ByteBuffer audioData, int bufferIndex, int sizeInBytes,
53399f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar            long presentationTimeUs) {
534071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        if (mAudioTrack == null || mAudioThread == null) {
535071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            throw new IllegalStateException(
536071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    "AudioTrack is NOT configured or audio thread is not created");
537071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
538071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
539071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        synchronized(mAudioLock) {
540071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mAudioBuffers.add(new AudioBuffer(
541071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    audioData, bufferIndex, sizeInBytes, presentationTimeUs));
542071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
543071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
544071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        if (mPlaybackRate != 0.0) {
545071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            postRenderAudio(0);
546071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
547071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
548071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
549071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    // When called on user thread, make sure to check mAudioThread != null.
550071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private void postRenderAudio(long delayMillis) {
551071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        mAudioHandler.postDelayed(new Runnable() {
552071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            public void run() {
553071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                synchronized(mAudioLock) {
554071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    if (mPlaybackRate == 0.0) {
555071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        return;
556071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    }
557071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
558071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    if (mAudioBuffers.isEmpty()) {
559071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        return;
560071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    }
561071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
562071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    AudioBuffer audioBuffer = mAudioBuffers.get(0);
563071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    int sizeWritten = mAudioTrack.write(
564071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            audioBuffer.mByteBuffer,
565071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            audioBuffer.mSizeInBytes,
566071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            AudioTrack.WRITE_NON_BLOCKING);
567071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    if (sizeWritten > 0) {
568071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        if (audioBuffer.mPresentationTimeUs != -1) {
569071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            native_updateQueuedAudioData(
570071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                    audioBuffer.mSizeInBytes, audioBuffer.mPresentationTimeUs);
571071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            audioBuffer.mPresentationTimeUs = -1;
572071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        }
573071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
574071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        if (sizeWritten == audioBuffer.mSizeInBytes) {
575071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            postReturnByteBuffer(audioBuffer);
576071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            mAudioBuffers.remove(0);
577071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            if (!mAudioBuffers.isEmpty()) {
578071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                postRenderAudio(0);
579071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            }
580071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            return;
581071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        }
582071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
583071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        audioBuffer.mSizeInBytes -= sizeWritten;
584071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    }
585071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    // TODO: wait time depends on fullness of audio track.
586071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    postRenderAudio(10);
587071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                }
588071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
589071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }, delayMillis);
590071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
591071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
592071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_updateQueuedAudioData(
593071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            int sizeInBytes, long presentationTimeUs);
594071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
59599f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    private final void postReturnByteBuffer(@NonNull final AudioBuffer audioBuffer) {
596071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        synchronized(mCallbackLock) {
597071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            if (mCallbackHandler != null) {
598071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                final MediaSync sync = this;
599071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                mCallbackHandler.post(new Runnable() {
600071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    public void run() {
601071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        synchronized(mCallbackLock) {
602071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            if (mCallbackHandler == null
603071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                    || mCallbackHandler.getLooper().getThread()
604071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                            != Thread.currentThread()) {
605071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                // callback handler has been changed.
606071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                return;
607071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            }
608071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            if (mCallback != null) {
609071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                mCallback.onReturnAudioBuffer(sync, audioBuffer.mByteBuffer,
610071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                        audioBuffer.mBufferIndex);
611071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            }
612071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        }
613071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    }
614071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                });
615071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
616071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
617071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
618071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
619071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private final void returnAudioBuffers() {
620071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        synchronized(mAudioLock) {
621071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            for (AudioBuffer audioBuffer: mAudioBuffers) {
622071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                postReturnByteBuffer(audioBuffer);
623071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
624071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mAudioBuffers.clear();
625071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
626071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
627071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
628071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private void createAudioThread() {
629071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        mAudioThread = new Thread() {
630071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            @Override
631071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            public void run() {
632071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                Looper.prepare();
633071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                synchronized(mAudioLock) {
634071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    mAudioLooper = Looper.myLooper();
635071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    mAudioHandler = new Handler();
636071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    mAudioLock.notify();
637071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                }
638071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                Looper.loop();
639071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
640071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        };
641071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        mAudioThread.start();
642071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
643071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        synchronized(mAudioLock) {
644071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            try {
645071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                mAudioLock.wait();
646071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            } catch(InterruptedException e) {
647071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
648071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
649071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
650071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
651071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    static {
652071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        System.loadLibrary("media_jni");
653071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_init();
654071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
655071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
656071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static native final void native_init();
657071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia}
658