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