1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.tv.tuner.exoplayer.ac3;
18
19import android.media.MediaFormat;
20
21import com.google.android.exoplayer.audio.AudioTrack;
22
23import java.nio.ByteBuffer;
24
25/**
26 * {@link AudioTrack} wrapper class for trickplay operations including FF/RW.
27 * FF/RW trickplay operations do not need framework {@link AudioTrack}.
28 * This wrapper class will do nothing in disabled status for those operations.
29 */
30public class AudioTrackWrapper {
31    private final AudioTrack mAudioTrack = new AudioTrack();
32    private int mAudioSessionID;
33    private boolean mIsEnabled;
34
35    AudioTrackWrapper() {
36        mIsEnabled = true;
37    }
38
39    public void resetSessionId() {
40        mAudioSessionID = AudioTrack.SESSION_ID_NOT_SET;
41    }
42
43    public boolean isInitialized() {
44        return mIsEnabled && mAudioTrack.isInitialized();
45    }
46
47    public void restart() {
48        if (mAudioTrack.isInitialized()) {
49            mAudioTrack.release();
50        }
51        mIsEnabled = true;
52        resetSessionId();
53    }
54
55    public void release()  {
56        if (mAudioSessionID != AudioTrack.SESSION_ID_NOT_SET) {
57            mAudioTrack.release();
58        }
59    }
60
61    public void initialize() throws AudioTrack.InitializationException {
62        if (!mIsEnabled) {
63            return;
64        }
65        if (mAudioSessionID != AudioTrack.SESSION_ID_NOT_SET) {
66            mAudioTrack.initialize(mAudioSessionID);
67        } else {
68            mAudioSessionID = mAudioTrack.initialize();
69        }
70    }
71
72    public void reset() {
73        if (!mIsEnabled) {
74            return;
75        }
76        mAudioTrack.reset();
77    }
78
79    public boolean isEnded() {
80        return !mIsEnabled || !mAudioTrack.hasPendingData();
81    }
82
83    public boolean isReady() {
84        // In the case of not playing actual audio data, Audio track is always ready.
85        return !mIsEnabled || mAudioTrack.hasPendingData();
86    }
87
88    public void play() {
89        if (!mIsEnabled) {
90            return;
91        }
92        mAudioTrack.play();
93    }
94
95    public void pause() {
96        if (!mIsEnabled) {
97            return;
98        }
99        mAudioTrack.pause();
100    }
101
102    public void setVolume(float volume) {
103        if (!mIsEnabled) {
104            return;
105        }
106        mAudioTrack.setVolume(volume);
107    }
108
109    public void reconfigure(MediaFormat format) {
110        if (!mIsEnabled || format == null) {
111            return;
112        }
113        String mimeType = format.getString(MediaFormat.KEY_MIME);
114        int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
115        int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
116        int pcmEncoding;
117        try {
118            pcmEncoding = format.getInteger(MediaFormat.KEY_PCM_ENCODING);
119        } catch (Exception e) {
120            pcmEncoding = com.google.android.exoplayer.MediaFormat.NO_VALUE;
121        }
122        // TODO: Handle non-AC3 or non-passthrough audio.
123        if (MediaFormat.MIMETYPE_AUDIO_AC3.equalsIgnoreCase(mimeType) && channelCount != 2) {
124            // Workarounds b/25955476.
125            // Since all devices and platforms does not support passthrough for non-stereo AC3,
126            // It is safe to fake non-stereo AC3 as AC3 stereo which is default passthrough mode.
127            // In other words, the channel count should be always 2.
128            channelCount = 2;
129        }
130        mAudioTrack.configure(mimeType, channelCount, sampleRate, pcmEncoding);
131    }
132
133    public void handleDiscontinuity() {
134        if (!mIsEnabled) {
135            return;
136        }
137        mAudioTrack.handleDiscontinuity();
138    }
139
140    public int handleBuffer(ByteBuffer buffer, int offset, int size, long presentationTimeUs)
141            throws AudioTrack.WriteException {
142        if (!mIsEnabled) {
143            return AudioTrack.RESULT_BUFFER_CONSUMED;
144        }
145        return mAudioTrack.handleBuffer(buffer, offset, size, presentationTimeUs);
146    }
147
148    public void setStatus(boolean enable) {
149        if (enable == mIsEnabled) {
150            return;
151        }
152        mAudioTrack.reset();
153        mIsEnabled = enable;
154    }
155
156    public boolean isEnabled() {
157        return mIsEnabled;
158    }
159
160    // This should be used only in case of being enabled.
161    public long getCurrentPositionUs(boolean isEnded) {
162        return mAudioTrack.getCurrentPositionUs(isEnded);
163    }
164}
165