MediaSync.java revision 25b802d47249702b9e5d175b3e7144934b67553d
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;
3225b802d47249702b9e5d175b3e7144934b67553dWei Jiaimport java.util.concurrent.TimeUnit;
33071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.util.LinkedList;
34071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiaimport java.util.List;
35071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
36071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia/**
37071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * MediaSync class can be used to synchronously playback audio and video streams.
38071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * It can be used to play audio-only or video-only stream, too.
39071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
40071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p>MediaSync is generally used like this:
41071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <pre>
42071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * MediaSync sync = new MediaSync();
43aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * sync.setSurface(surface);
44071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * Surface inputSurface = sync.createInputSurface();
45071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * ...
46071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // MediaCodec videoDecoder = ...;
47071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * videoDecoder.configure(format, inputSurface, ...);
48071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * ...
49aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * sync.setAudioTrack(audioTrack);
50071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync.setCallback(new MediaSync.Callback() {
51ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia *     {@literal @Override}
52071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     public void onReturnAudioBuffer(MediaSync sync, ByteBuffer audioBuffer, int bufferIndex) {
53071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *         ...
54071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     }
55161537c81664613c79a6397759aa9baa5db6dd64Wei Jia * }, null);
56071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // This needs to be done since sync is paused on creation.
57071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync.setPlaybackRate(1.0f, MediaSync.PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
58071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
59071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * for (;;) {
60071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   ...
61071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   // send video frames to surface for rendering, e.g., call
62071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   // videoDecoder.releaseOutputBuffer(videoOutputBufferIx, videoPresentationTimeNs);
63071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   // More details are available as below.
64071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   ...
65071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   sync.queueAudio(audioByteBuffer, bufferIndex, size, audioPresentationTimeUs); // non-blocking.
66071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   // The audioByteBuffer and bufferIndex will be returned via callback.
67071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   // More details are available as below.
68071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *   ...
69071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     ...
70071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * }
71071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync.setPlaybackRate(0.0f, MediaSync.PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
72071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync.release();
73071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * sync = null;
74071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
75071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // The following code snippet illustrates how video/audio raw frames are created by
76071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // MediaCodec's, how they are fed to MediaSync and how they are returned by MediaSync.
77071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // This is the callback from MediaCodec.
78071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * onOutputBufferAvailable(MediaCodec codec, int bufferIndex, BufferInfo info) {
79071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     // ...
80071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     if (codec == videoDecoder) {
81071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *         // surface timestamp must contain media presentation time in nanoseconds.
82071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *         codec.releaseOutputBuffer(bufferIndex, 1000 * info.presentationTime);
83071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     } else {
84071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *         ByteBuffer audioByteBuffer = codec.getOutputBuffer(bufferIndex);
85071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *         sync.queueByteBuffer(audioByteBuffer, bufferIndex, info.size, info.presentationTime);
86071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     }
87071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     // ...
88071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * }
89071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
90071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * // This is the callback from MediaSync.
91071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * onReturnAudioBuffer(MediaSync sync, ByteBuffer buffer, int bufferIndex) {
92071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     // ...
93071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     audioDecoder.releaseBuffer(bufferIndex, false);
94071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *     // ...
95071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * }
96071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
97071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * </pre>
98071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia *
99aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * The client needs to configure corresponding sink by setting the Surface and/or AudioTrack
100aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar * based on the stream type it will play.
101071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p>
102071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * For video, the client needs to call {@link #createInputSurface} to obtain a surface on
103071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * which it will render video frames.
104071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p>
105071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * For audio, the client needs to set up audio track correctly, e.g., using {@link
106071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * AudioTrack#MODE_STREAM}. The audio buffers are sent to MediaSync directly via {@link
107071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * #queueAudio}, and are returned to the client via {@link Callback#onReturnAudioBuffer}
108071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * asynchronously. The client should not modify an audio buffer till it's returned.
109071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p>
110071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * The client can optionally pre-fill audio/video buffers by setting playback rate to 0.0,
111071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * and then feed audio/video buffers to corresponding components. This can reduce possible
112071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * initial underrun.
113071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia * <p>
114071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia */
115071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jiafinal public class MediaSync {
116071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
117071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * MediaSync callback interface. Used to notify the user asynchronously
118071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * of various MediaSync events.
119071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
120071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public static abstract class Callback {
121071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        /**
122071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia         * Called when returning an audio buffer which has been consumed.
123071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia         *
124071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia         * @param sync The MediaSync object.
125071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia         * @param audioBuffer The returned audio buffer.
12699f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar         * @param bufferIndex The index associated with the audio buffer
127071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia         */
128071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        public abstract void onReturnAudioBuffer(
12999f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar                @NonNull MediaSync sync, @NonNull ByteBuffer audioBuffer, int bufferIndex);
130071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
131071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
132071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static final String TAG = "MediaSync";
133071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
134071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static final int EVENT_CALLBACK = 1;
135071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static final int EVENT_SET_CALLBACK = 2;
136071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
137071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static final int CB_RETURN_AUDIO_BUFFER = 1;
138071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
139071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static class AudioBuffer {
140071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        public ByteBuffer mByteBuffer;
141071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        public int mBufferIndex;
142071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        public int mSizeInBytes;
143071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        long mPresentationTimeUs;
144071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
14599f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar        public AudioBuffer(@NonNull ByteBuffer byteBuffer, int bufferIndex,
146071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                           int sizeInBytes, long presentationTimeUs) {
147071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mByteBuffer = byteBuffer;
148071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mBufferIndex = bufferIndex;
149071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mSizeInBytes = sizeInBytes;
150071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mPresentationTimeUs = presentationTimeUs;
151071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
152071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
153071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
154071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private final Object mCallbackLock = new Object();
155071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private Handler mCallbackHandler = null;
156071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private MediaSync.Callback mCallback = null;
157071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
158071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private Thread mAudioThread = null;
159071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    // Created on mAudioThread when mAudioThread is started. When used on user thread, they should
160071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    // be guarded by checking mAudioThread.
161071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private Handler mAudioHandler = null;
162071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private Looper mAudioLooper = null;
163071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
164071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private final Object mAudioLock = new Object();
165071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private AudioTrack mAudioTrack = null;
166071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private List<AudioBuffer> mAudioBuffers = new LinkedList<AudioBuffer>();
167071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private float mPlaybackRate = 0.0f;
168071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
169071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private long mNativeContext;
170071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
171071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
172071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Class constructor. On creation, MediaSync is paused, i.e., playback rate is 0.0f.
173071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
174071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public MediaSync() {
175071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_setup();
176071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
177071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
178071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_setup();
179071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
180071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    @Override
181071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    protected void finalize() {
182071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_finalize();
183071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
184071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
185071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_finalize();
186071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
187071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
188071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Make sure you call this when you're done to free up any opened
189071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * component instance instead of relying on the garbage collector
190071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * to do this for you at some point in the future.
191071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
192071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public final void release() {
193071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        returnAudioBuffers();
194071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        if (mAudioThread != null) {
195071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            if (mAudioLooper != null) {
196071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                mAudioLooper.quit();
197071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
198071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
199071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        setCallback(null, null);
200071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_release();
201071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
202071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
203071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_release();
204071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
205071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
206071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Sets an asynchronous callback for actionable MediaSync events.
20799f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * <p>
20899f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * This method can be called multiple times to update a previously set callback. If the
20999f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * handler is changed, undelivered notifications scheduled for the old handler may be dropped.
21099f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * <p>
21199f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * <b>Do not call this inside callback.</b>
212071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
21399f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * @param cb The callback that will run. Use {@code null} to stop receiving callbacks.
21499f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar     * @param handler The Handler that will run the callback. Use {@code null} to use MediaSync's
215071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     internal handler if it exists.
216071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
21799f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    public void setCallback(@Nullable /* MediaSync. */ Callback cb, @Nullable Handler handler) {
218071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        synchronized(mCallbackLock) {
219071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            if (handler != null) {
220071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                mCallbackHandler = handler;
221071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            } else {
222071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                Looper looper;
223071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                if ((looper = Looper.myLooper()) == null) {
224071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    looper = Looper.getMainLooper();
225071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                }
226071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                if (looper == null) {
227071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    mCallbackHandler = null;
228071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                } else {
229071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    mCallbackHandler = new Handler(looper);
230071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                }
231071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
232071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
233071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mCallback = cb;
234071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
235071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
236071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
237071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
238aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     * Sets the output surface for MediaSync.
239aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     * <p>
240aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     * Currently, this is only supported in the Initialized state.
241071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
242071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param surface Specify a surface on which to render the video data.
243aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     * @throws IllegalArgumentException if the surface has been released, is invalid,
244071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     or can not be connected.
245aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     * @throws IllegalStateException if setting the surface is not supported, e.g.
246aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     *     not in the Initialized state, or another surface has already been configured.
247071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
248aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar    public void setSurface(@Nullable Surface surface) {
249071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_configureSurface(surface);
250071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
251071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
25299f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    private native final void native_configureSurface(@Nullable Surface surface);
253071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
254071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
255aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     * Sets the audio track for MediaSync.
256aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     * <p>
257aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     * Currently, this is only supported in the Initialized state.
258071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
259071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param audioTrack Specify an AudioTrack through which to render the audio data.
260ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia     * @throws IllegalArgumentException if the audioTrack has been released, or is invalid.
261aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     * @throws IllegalStateException if setting the audio track is not supported, e.g.
262aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     *     not in the Initialized state, or another audio track has already been configured.
263071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
264aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar    public void setAudioTrack(@Nullable AudioTrack audioTrack) {
265ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia        // AudioTrack has sanity check for configured sample rate.
266ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia        int nativeSampleRateInHz = (audioTrack == null ? 0 : audioTrack.getSampleRate());
267ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia
268071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_configureAudioTrack(audioTrack, nativeSampleRateInHz);
269071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        mAudioTrack = audioTrack;
270ba5997e574c2f5cd794652abce40c19d02d5febfWei Jia        if (audioTrack != null && mAudioThread == null) {
271071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            createAudioThread();
272071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
273071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
274071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
275071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_configureAudioTrack(
27699f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar            @Nullable AudioTrack audioTrack, int nativeSampleRateInHz);
277071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
278071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
279071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Requests a Surface to use as the input. This may only be called after
280aba29b77a5742fc920ec62dbc9ddb6f025759d65Lajos Molnar     * {@link #setSurface}.
281071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * <p>
282071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * The application is responsible for calling release() on the Surface when
283071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * done.
284071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @throws IllegalStateException if not configured, or another input surface has
285071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     already been created.
286071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
28799f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    @NonNull
288071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public native final Surface createInputSurface();
289071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
290071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
291b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Resample audio data when changing playback speed.
292b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p>
293b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Resample the waveform based on the requested playback rate to get
294071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * a new waveform, and play back the new waveform at the original sampling
295071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * frequency.
296b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p><ul>
297b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <li>When rate is larger than 1.0, pitch becomes higher.
298b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <li>When rate is smaller than 1.0, pitch becomes lower.
299b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * </ul>
300071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
301b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2;
302071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
303071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
304b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Time stretch audio when changing playback speed.
305b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p>
306071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Time stretching changes the duration of the audio samples without
307b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * affecting their pitch. This is only supported for a limited range
308b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * of playback speeds, e.g. from 1/2x to 2x. If the rate is adjusted
309b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * beyond this limit, the rate change will fail.
310071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
311071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
312071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
313b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    /**
314b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Time stretch audio when changing playback speed, and may mute if
315b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * stretching is no longer supported.
316b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p>
317b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Time stretching changes the duration of the audio samples without
318b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * affecting their pitch. This is only supported for a limited range
319b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * of playback speeds, e.g. from 1/2x to 2x. When it is no longer
320b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * supported, the audio may be muted.  Using this mode will not fail
321b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * for non-negative playback rates.
322b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     */
323b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
324b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar
325071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /** @hide */
326071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    @IntDef(
327071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        value = {
328b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
329b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            PLAYBACK_RATE_AUDIO_MODE_STRETCH,
330071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
331b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        })
332071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    @Retention(RetentionPolicy.SOURCE)
333071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public @interface PlaybackRateAudioMode {}
334071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
335071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
336b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Sets playback rate and audio mode.
337071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
338071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param rate the ratio between desired playback rate and normal one. 1.0 means normal
339b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     playback speed. 0.0 means pause. Value larger than 1.0 means faster playback,
340b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     while value between 0.0 and 1.0 for slower playback. <b>Note:</b> the normal rate
341b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     does not change as a result of this call. To restore the original rate at any time,
342b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     use 1.0.
343b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @param audioMode audio playback mode. Must be one of the supported
344b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     audio modes.
345071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
346071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @throws IllegalStateException if the internal sync engine or the audio track has not
347071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     been initialized.
348b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @throws IllegalArgumentException if audioMode is not supported.
349071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
350b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    public void setPlaybackRate(float rate, @PlaybackRateAudioMode int audioMode) {
351b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        PlaybackSettings rateSettings = new PlaybackSettings();
352b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        rateSettings.allowDefaults();
353b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        switch (audioMode) {
354b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            case PLAYBACK_RATE_AUDIO_MODE_DEFAULT:
355b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                rateSettings.setSpeed(rate).setPitch(1.0f);
356b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                break;
357b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            case PLAYBACK_RATE_AUDIO_MODE_STRETCH:
358b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                rateSettings.setSpeed(rate).setPitch(1.0f)
359b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                        .setAudioFallbackMode(rateSettings.AUDIO_FALLBACK_MODE_FAIL);
360b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                break;
361b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE:
362b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                rateSettings.setSpeed(rate).setPitch(rate);
363b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                break;
364b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            default:
365b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            {
366b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                final String msg = "Audio playback mode " + audioMode + " is not supported";
367b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                throw new IllegalArgumentException(msg);
368b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            }
369b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        }
370b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        setPlaybackSettings(rateSettings);
371071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
372071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
373071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
374b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Sets playback rate using {@link PlaybackSettings}.
375b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p>
376b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * When using MediaSync with {@link AudioTrack}, set playback settings using this
377b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * call instead of calling it directly on the track, so that the sync is aware of
378b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * the settings change.
379b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * <p>
380b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * This call also works if there is no audio track.
381071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
382b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @param settings the playback settings to use. {@link PlaybackSettings#getSpeed
383b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     Speed} is the ratio between desired playback rate and normal one. 1.0 means
384b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     normal playback speed. 0.0 means pause. Value larger than 1.0 means faster playback,
385b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     while value between 0.0 and 1.0 for slower playback. <b>Note:</b> the normal rate
386b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     does not change as a result of this call. To restore the original rate at any time,
387b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     use speed of 1.0.
388071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *
389071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @throws IllegalStateException if the internal sync engine or the audio track has not
390071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     been initialized.
391b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @throws IllegalArgumentException if the settings are not supported.
392071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
393b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    public void setPlaybackSettings(@NonNull PlaybackSettings settings) {
394b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        float rate;
395b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        try {
396b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            rate = settings.getSpeed();
397b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar
398b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            // rate is specified
399b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            if (mAudioTrack != null) {
400b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                try {
401b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                    if (rate == 0.0) {
402b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                        mAudioTrack.pause();
403b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                    } else {
404b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                        mAudioTrack.setPlaybackSettings(settings);
405b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                        mAudioTrack.play();
406b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                    }
407b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                } catch (IllegalStateException e) {
408b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                    throw e;
409071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                }
410071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
411071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
412b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            synchronized(mAudioLock) {
413b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                mPlaybackRate = rate;
414b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            }
415b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            if (mPlaybackRate != 0.0 && mAudioThread != null) {
416b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                postRenderAudio(0);
417b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            }
418b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            native_setPlaybackRate(mPlaybackRate);
419b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        } catch (IllegalStateException e) {
420b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            // rate is not specified; still, propagate settings to audio track
421b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            if (mAudioTrack != null) {
422b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar                mAudioTrack.setPlaybackSettings(settings);
423b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            }
424071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
425b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    }
426071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
427b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    /**
428b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * Gets the playback rate using {@link PlaybackSettings}.
429b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *
430b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @return the playback rate being used.
431b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *
432b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     * @throws IllegalStateException if the internal sync engine or the audio track has not
433b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     *     been initialized.
434b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar     */
435b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    @NonNull
436b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar    public PlaybackSettings getPlaybackSettings() {
437b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        if (mAudioTrack != null) {
438b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            return mAudioTrack.getPlaybackSettings();
439b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar        } else {
440b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            PlaybackSettings settings = new PlaybackSettings();
441b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            settings.allowDefaults();
442b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            settings.setSpeed(mPlaybackRate);
443b3d5fd252851071f2f7e3dd66be84683750379abLajos Molnar            return settings;
444071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
445071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
446071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
447071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_setPlaybackRate(float rate);
448071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
449c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    /**
450c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * Sets A/V sync mode.
451c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     *
452c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @param settings the A/V sync settings to apply
453c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     *
454c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @throws IllegalStateException if the internal player engine has not been
455c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * initialized.
456c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @throws IllegalArgumentException if settings are not supported.
457c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     */
458c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    public native void setSyncSettings(@NonNull SyncSettings settings);
459c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar
460c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    /**
461c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * Gets the A/V sync mode.
462c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     *
463c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @return the A/V sync settings
464c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     *
465c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @throws IllegalStateException if the internal player engine has not been
466c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * initialized.
467c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     */
468c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    @NonNull
469c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    public native SyncSettings getSyncSettings();
470c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar
471c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    /**
472c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * Flushes all buffers from the sync object.
473c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * <p>
474c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * No callbacks are received for the flushed buffers.
475c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     *
476c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * @throws IllegalStateException if the internal player engine has not been
477c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     * initialized.
478c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar     */
479c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    public void flush() {
480c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar        synchronized(mAudioLock) {
481c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar            mAudioBuffers.clear();
482c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar            mCallbackHandler.removeCallbacksAndMessages(null);
483c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar        }
484c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar        // TODO implement this for surface buffers.
485c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar    }
486c98f58efd147c574faa4a4f9956b5ab95e3027a5Lajos Molnar
487217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia   /**
488217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * Get current playback position.
489217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * <p>
4907f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * The MediaTimestamp represents how the media time correlates to the system time in
4917f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * a linear fashion. It contains the media time and system timestamp of an anchor frame
4927f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * ({@link MediaTimestamp#mediaTimeUs} and {@link MediaTimestamp#nanoTime})
4937f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * and the speed of the media clock ({@link MediaTimestamp#clockRate}).
4947f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * <p>
4957f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * During regular playback, the media time moves fairly constantly (though the
4967f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * anchor frame may be rebased to a current system time, the linear correlation stays
4977f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * steady). Therefore, this method does not need to be called often.
498217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * <p>
499217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * To help users to get current playback position, this method always returns the timestamp of
500217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * just-rendered frame, i.e., {@link System#nanoTime} and its corresponding media time. They
501217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    * can be used as current playback position.
502217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    *
5037f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp
5047f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    *         is available, e.g. because the media sync has not been initialized.
505217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    */
5067f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    @Nullable
5077f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar    public MediaTimestamp getTimestamp()
508217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    {
5097f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar        try {
5107f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            // TODO: create the timestamp in native
5117f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            MediaTimestamp timestamp = new MediaTimestamp();
5127f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            if (native_getTimestamp(timestamp)) {
5137f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar                return timestamp;
5147f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            } else {
5157f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar                return null;
5167f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            }
5177f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar        } catch (IllegalStateException e) {
5187f08763f4140822c30d0e18d4d3939488c8d26f8Lajos Molnar            return null;
519217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia        }
520217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia    }
521217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia
52299f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    private native final boolean native_getTimestamp(@NonNull MediaTimestamp timestamp);
523217ec0adfc35302a6cc6b04bc78bf8fd82ffc8a5Wei Jia
524071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    /**
525071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * Queues the audio data asynchronously for playback (AudioTrack must be in streaming mode).
526071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param audioData the buffer that holds the data to play. This buffer will be returned
527071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     to the client via registered callback.
528071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param bufferIndex the buffer index used to identify audioData. It will be returned to
529071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     the client along with audioData. This helps applications to keep track of audioData.
530071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param sizeInBytes number of bytes to queue.
531071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @param presentationTimeUs the presentation timestamp in microseconds for the first frame
532071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     in the buffer.
533071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     * @throws IllegalStateException if audio track is not configured or internal configureation
534071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     *     has not been done correctly.
535071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia     */
536071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    public void queueAudio(
53799f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar            @NonNull ByteBuffer audioData, int bufferIndex, int sizeInBytes,
53899f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar            long presentationTimeUs) {
539071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        if (mAudioTrack == null || mAudioThread == null) {
540071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            throw new IllegalStateException(
541071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    "AudioTrack is NOT configured or audio thread is not created");
542071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
543071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
544071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        synchronized(mAudioLock) {
545071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mAudioBuffers.add(new AudioBuffer(
546071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    audioData, bufferIndex, sizeInBytes, presentationTimeUs));
547071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
548071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
549071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        if (mPlaybackRate != 0.0) {
550071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            postRenderAudio(0);
551071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
552071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
553071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
554071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    // When called on user thread, make sure to check mAudioThread != null.
555071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private void postRenderAudio(long delayMillis) {
556071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        mAudioHandler.postDelayed(new Runnable() {
557071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            public void run() {
558071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                synchronized(mAudioLock) {
559071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    if (mPlaybackRate == 0.0) {
560071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        return;
561071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    }
562071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
563071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    if (mAudioBuffers.isEmpty()) {
564071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        return;
565071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    }
566071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
567071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    AudioBuffer audioBuffer = mAudioBuffers.get(0);
568071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    int sizeWritten = mAudioTrack.write(
569071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            audioBuffer.mByteBuffer,
570071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            audioBuffer.mSizeInBytes,
571071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            AudioTrack.WRITE_NON_BLOCKING);
572071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    if (sizeWritten > 0) {
573071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        if (audioBuffer.mPresentationTimeUs != -1) {
574071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            native_updateQueuedAudioData(
575071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                    audioBuffer.mSizeInBytes, audioBuffer.mPresentationTimeUs);
576071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            audioBuffer.mPresentationTimeUs = -1;
577071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        }
578071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
579071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        if (sizeWritten == audioBuffer.mSizeInBytes) {
580071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            postReturnByteBuffer(audioBuffer);
581071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            mAudioBuffers.remove(0);
582071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            if (!mAudioBuffers.isEmpty()) {
583071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                postRenderAudio(0);
584071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            }
585071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            return;
586071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        }
587071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
588071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        audioBuffer.mSizeInBytes -= sizeWritten;
589071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    }
59025b802d47249702b9e5d175b3e7144934b67553dWei Jia                    long pendingTimeMs = TimeUnit.MICROSECONDS.toMillis(
59125b802d47249702b9e5d175b3e7144934b67553dWei Jia                            native_getPlayTimeForPendingAudioFrames());
59225b802d47249702b9e5d175b3e7144934b67553dWei Jia                    postRenderAudio(pendingTimeMs / 2);
593071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                }
594071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
595071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }, delayMillis);
596071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
597071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
598071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private native final void native_updateQueuedAudioData(
599071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            int sizeInBytes, long presentationTimeUs);
600071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
60125b802d47249702b9e5d175b3e7144934b67553dWei Jia    private native final long native_getPlayTimeForPendingAudioFrames();
60225b802d47249702b9e5d175b3e7144934b67553dWei Jia
60399f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar    private final void postReturnByteBuffer(@NonNull final AudioBuffer audioBuffer) {
604071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        synchronized(mCallbackLock) {
605071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            if (mCallbackHandler != null) {
606071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                final MediaSync sync = this;
607071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                mCallbackHandler.post(new Runnable() {
608071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    public void run() {
609071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        synchronized(mCallbackLock) {
610071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            if (mCallbackHandler == null
611071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                    || mCallbackHandler.getLooper().getThread()
612071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                            != Thread.currentThread()) {
613071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                // callback handler has been changed.
614071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                return;
615071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            }
616071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            if (mCallback != null) {
617071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                mCallback.onReturnAudioBuffer(sync, audioBuffer.mByteBuffer,
618071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                                        audioBuffer.mBufferIndex);
619071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                            }
620071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                        }
621071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    }
622071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                });
623071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
624071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
625071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
626071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
627071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private final void returnAudioBuffers() {
628071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        synchronized(mAudioLock) {
629071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            for (AudioBuffer audioBuffer: mAudioBuffers) {
630071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                postReturnByteBuffer(audioBuffer);
631071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
632071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            mAudioBuffers.clear();
633071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
634071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
635071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
636071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private void createAudioThread() {
637071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        mAudioThread = new Thread() {
638071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            @Override
639071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            public void run() {
640071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                Looper.prepare();
641071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                synchronized(mAudioLock) {
642071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    mAudioLooper = Looper.myLooper();
643071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    mAudioHandler = new Handler();
644071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                    mAudioLock.notify();
645071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                }
646071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                Looper.loop();
647071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
648071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        };
649071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        mAudioThread.start();
650071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
651071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        synchronized(mAudioLock) {
652071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            try {
653071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia                mAudioLock.wait();
654071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            } catch(InterruptedException e) {
655071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia            }
656071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        }
657071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
658071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
659071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    static {
660071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        System.loadLibrary("media_jni");
661071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia        native_init();
662071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    }
663071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia
664071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia    private static native final void native_init();
665071a8b71d1212e218a3ebf7dbb8908a4acf5cf6dWei Jia}
666