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.audio; 18 19import android.media.MediaFormat; 20 21import com.google.android.exoplayer.C; 22import com.google.android.exoplayer.audio.AudioTrack; 23 24import java.nio.ByteBuffer; 25 26/** 27 * {@link AudioTrack} wrapper class for trickplay operations including FF/RW. 28 * FF/RW trickplay operations do not need framework {@link AudioTrack}. 29 * This wrapper class will do nothing in disabled status for those operations. 30 */ 31public class AudioTrackWrapper { 32 private static final int PCM16_FRAME_BYTES = 2; 33 private static final int AC3_FRAMES_IN_ONE_SAMPLE = 1536; 34 private static final int BUFFERED_SAMPLES_IN_AUDIOTRACK = 35 MpegTsDefaultAudioTrackRenderer.BUFFERED_SAMPLES_IN_AUDIOTRACK; 36 private final AudioTrack mAudioTrack = new AudioTrack(); 37 private int mAudioSessionID; 38 private boolean mIsEnabled; 39 40 AudioTrackWrapper() { 41 mIsEnabled = true; 42 } 43 44 public void resetSessionId() { 45 mAudioSessionID = AudioTrack.SESSION_ID_NOT_SET; 46 } 47 48 public boolean isInitialized() { 49 return mIsEnabled && mAudioTrack.isInitialized(); 50 } 51 52 public void restart() { 53 if (mAudioTrack.isInitialized()) { 54 mAudioTrack.release(); 55 } 56 mIsEnabled = true; 57 resetSessionId(); 58 } 59 60 public void release() { 61 if (mAudioSessionID != AudioTrack.SESSION_ID_NOT_SET) { 62 mAudioTrack.release(); 63 } 64 } 65 66 public void initialize() throws AudioTrack.InitializationException { 67 if (!mIsEnabled) { 68 return; 69 } 70 if (mAudioSessionID != AudioTrack.SESSION_ID_NOT_SET) { 71 mAudioTrack.initialize(mAudioSessionID); 72 } else { 73 mAudioSessionID = mAudioTrack.initialize(); 74 } 75 } 76 77 public void reset() { 78 if (!mIsEnabled) { 79 return; 80 } 81 mAudioTrack.reset(); 82 } 83 84 public boolean isEnded() { 85 return !mIsEnabled || !mAudioTrack.hasPendingData(); 86 } 87 88 public boolean isReady() { 89 // In the case of not playing actual audio data, Audio track is always ready. 90 return !mIsEnabled || mAudioTrack.hasPendingData(); 91 } 92 93 public void play() { 94 if (!mIsEnabled) { 95 return; 96 } 97 mAudioTrack.play(); 98 } 99 100 public void pause() { 101 if (!mIsEnabled) { 102 return; 103 } 104 mAudioTrack.pause(); 105 } 106 107 public void setVolume(float volume) { 108 if (!mIsEnabled) { 109 return; 110 } 111 mAudioTrack.setVolume(volume); 112 } 113 114 public void reconfigure(MediaFormat format, int audioBufferSize) { 115 if (!mIsEnabled || format == null) { 116 return; 117 } 118 String mimeType = format.getString(MediaFormat.KEY_MIME); 119 int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); 120 int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); 121 int pcmEncoding; 122 try { 123 pcmEncoding = format.getInteger(MediaFormat.KEY_PCM_ENCODING); 124 } catch (Exception e) { 125 pcmEncoding = C.ENCODING_PCM_16BIT; 126 } 127 // TODO: Handle non-AC3. 128 if (MediaFormat.MIMETYPE_AUDIO_AC3.equalsIgnoreCase(mimeType) && channelCount != 2) { 129 // Workarounds b/25955476. 130 // Since all devices and platforms does not support passthrough for non-stereo AC3, 131 // It is safe to fake non-stereo AC3 as AC3 stereo which is default passthrough mode. 132 // In other words, the channel count should be always 2. 133 channelCount = 2; 134 } 135 if (MediaFormat.MIMETYPE_AUDIO_RAW.equalsIgnoreCase(mimeType)) { 136 audioBufferSize = 137 channelCount 138 * PCM16_FRAME_BYTES 139 * AC3_FRAMES_IN_ONE_SAMPLE 140 * BUFFERED_SAMPLES_IN_AUDIOTRACK; 141 } 142 mAudioTrack.configure(mimeType, channelCount, sampleRate, pcmEncoding, audioBufferSize); 143 } 144 145 public void handleDiscontinuity() { 146 if (!mIsEnabled) { 147 return; 148 } 149 mAudioTrack.handleDiscontinuity(); 150 } 151 152 public int handleBuffer(ByteBuffer buffer, int offset, int size, long presentationTimeUs) 153 throws AudioTrack.WriteException { 154 if (!mIsEnabled) { 155 return AudioTrack.RESULT_BUFFER_CONSUMED; 156 } 157 return mAudioTrack.handleBuffer(buffer, offset, size, presentationTimeUs); 158 } 159 160 public void setStatus(boolean enable) { 161 if (enable == mIsEnabled) { 162 return; 163 } 164 mAudioTrack.reset(); 165 mIsEnabled = enable; 166 } 167 168 public boolean isEnabled() { 169 return mIsEnabled; 170 } 171 172 // This should be used only in case of being enabled. 173 public long getCurrentPositionUs(boolean isEnded) { 174 return mAudioTrack.getCurrentPositionUs(isEnded); 175 } 176} 177