|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
|
3 /* |
|
4 * Copyright (c) 2014 The Linux Foundation. All rights reserved. |
|
5 * Copyright (C) 2008 The Android Open Source Project |
|
6 * |
|
7 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
8 * you may not use this file except in compliance with the License. |
|
9 * You may obtain a copy of the License at |
|
10 * |
|
11 * http://www.apache.org/licenses/LICENSE-2.0 |
|
12 * |
|
13 * Unless required by applicable law or agreed to in writing, software |
|
14 * distributed under the License is distributed on an "AS IS" BASIS, |
|
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
16 * See the License for the specific language governing permissions and |
|
17 * limitations under the License. |
|
18 */ |
|
19 |
|
20 #include <stagefright/foundation/ADebug.h> |
|
21 #include "AudioOutput.h" |
|
22 |
|
23 namespace mozilla { |
|
24 |
|
25 #ifdef PR_LOGGING |
|
26 extern PRLogModuleInfo* gAudioOffloadPlayerLog; |
|
27 #define AUDIO_OFFLOAD_LOG(type, msg) \ |
|
28 PR_LOG(gAudioOffloadPlayerLog, type, msg) |
|
29 #else |
|
30 #define AUDIO_OFFLOAD_LOG(type, msg) |
|
31 #endif |
|
32 |
|
33 using namespace android; |
|
34 |
|
35 AudioOutput::AudioOutput(int aSessionId, int aUid) : |
|
36 mCallback(nullptr), |
|
37 mCallbackCookie(nullptr), |
|
38 mCallbackData(nullptr), |
|
39 mSessionId(aSessionId), |
|
40 mUid(aUid) |
|
41 { |
|
42 #ifdef PR_LOGGING |
|
43 if (!gAudioOffloadPlayerLog) { |
|
44 gAudioOffloadPlayerLog = PR_NewLogModule("AudioOffloadPlayer"); |
|
45 } |
|
46 #endif |
|
47 } |
|
48 |
|
49 AudioOutput::~AudioOutput() |
|
50 { |
|
51 Close(); |
|
52 } |
|
53 |
|
54 ssize_t AudioOutput::FrameSize() const |
|
55 { |
|
56 if (!mTrack.get()) { |
|
57 return NO_INIT; |
|
58 } |
|
59 return mTrack->frameSize(); |
|
60 } |
|
61 |
|
62 status_t AudioOutput::GetPosition(uint32_t *aPosition) const |
|
63 { |
|
64 if (!mTrack.get()) { |
|
65 return NO_INIT; |
|
66 } |
|
67 return mTrack->getPosition(aPosition); |
|
68 } |
|
69 |
|
70 status_t AudioOutput::SetVolume(float aVolume) const |
|
71 { |
|
72 if (!mTrack.get()) { |
|
73 return NO_INIT; |
|
74 } |
|
75 return mTrack->setVolume(aVolume); |
|
76 } |
|
77 |
|
78 status_t AudioOutput::SetParameters(const String8& aKeyValuePairs) |
|
79 { |
|
80 if (!mTrack.get()) { |
|
81 return NO_INIT; |
|
82 } |
|
83 return mTrack->setParameters(aKeyValuePairs); |
|
84 } |
|
85 |
|
86 status_t AudioOutput::Open(uint32_t aSampleRate, |
|
87 int aChannelCount, |
|
88 audio_channel_mask_t aChannelMask, |
|
89 audio_format_t aFormat, |
|
90 AudioCallback aCb, |
|
91 void* aCookie, |
|
92 audio_output_flags_t aFlags, |
|
93 const audio_offload_info_t *aOffloadInfo) |
|
94 { |
|
95 mCallback = aCb; |
|
96 mCallbackCookie = aCookie; |
|
97 |
|
98 if (((aFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) || !aCb || |
|
99 !aOffloadInfo) { |
|
100 return BAD_VALUE; |
|
101 } |
|
102 |
|
103 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("open(%u, %d, 0x%x, 0x%x, %d 0x%x)", |
|
104 aSampleRate, aChannelCount, aChannelMask, aFormat, mSessionId, aFlags)); |
|
105 |
|
106 if (aChannelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) { |
|
107 aChannelMask = audio_channel_out_mask_from_count(aChannelCount); |
|
108 if (0 == aChannelMask) { |
|
109 AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("open() error, can\'t derive mask for" |
|
110 " %d audio channels", aChannelCount)); |
|
111 return NO_INIT; |
|
112 } |
|
113 } |
|
114 |
|
115 sp<AudioTrack> t; |
|
116 CallbackData* newcbd = new CallbackData(this); |
|
117 |
|
118 t = new AudioTrack( |
|
119 AUDIO_STREAM_MUSIC, |
|
120 aSampleRate, |
|
121 aFormat, |
|
122 aChannelMask, |
|
123 0, // Offloaded tracks will get frame count from AudioFlinger |
|
124 aFlags, |
|
125 CallbackWrapper, |
|
126 newcbd, |
|
127 0, // notification frames |
|
128 mSessionId, |
|
129 AudioTrack::TRANSFER_CALLBACK, |
|
130 aOffloadInfo, |
|
131 mUid); |
|
132 |
|
133 if ((!t.get()) || (t->initCheck() != NO_ERROR)) { |
|
134 AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Unable to create audio track")); |
|
135 delete newcbd; |
|
136 return NO_INIT; |
|
137 } |
|
138 |
|
139 mCallbackData = newcbd; |
|
140 t->setVolume(1.0); |
|
141 |
|
142 mTrack = t; |
|
143 return NO_ERROR; |
|
144 } |
|
145 |
|
146 status_t AudioOutput::Start() |
|
147 { |
|
148 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__)); |
|
149 if (!mTrack.get()) { |
|
150 return NO_INIT; |
|
151 } |
|
152 mTrack->setVolume(1.0); |
|
153 return mTrack->start(); |
|
154 } |
|
155 |
|
156 void AudioOutput::Stop() |
|
157 { |
|
158 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__)); |
|
159 if (mTrack.get()) { |
|
160 mTrack->stop(); |
|
161 } |
|
162 } |
|
163 |
|
164 void AudioOutput::Flush() |
|
165 { |
|
166 if (mTrack.get()) { |
|
167 mTrack->flush(); |
|
168 } |
|
169 } |
|
170 |
|
171 void AudioOutput::Pause() |
|
172 { |
|
173 if (mTrack.get()) { |
|
174 mTrack->pause(); |
|
175 } |
|
176 } |
|
177 |
|
178 void AudioOutput::Close() |
|
179 { |
|
180 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__)); |
|
181 mTrack.clear(); |
|
182 |
|
183 delete mCallbackData; |
|
184 mCallbackData = nullptr; |
|
185 } |
|
186 |
|
187 // static |
|
188 void AudioOutput::CallbackWrapper(int aEvent, void* aCookie, void* aInfo) |
|
189 { |
|
190 CallbackData* data = (CallbackData*) aCookie; |
|
191 data->Lock(); |
|
192 AudioOutput* me = data->GetOutput(); |
|
193 AudioTrack::Buffer* buffer = (AudioTrack::Buffer*) aInfo; |
|
194 if (!me) { |
|
195 // no output set, likely because the track was scheduled to be reused |
|
196 // by another player, but the format turned out to be incompatible. |
|
197 data->Unlock(); |
|
198 if (buffer) { |
|
199 buffer->size = 0; |
|
200 } |
|
201 return; |
|
202 } |
|
203 |
|
204 switch(aEvent) { |
|
205 |
|
206 case AudioTrack::EVENT_MORE_DATA: { |
|
207 |
|
208 size_t actualSize = (*me->mCallback)(me, buffer->raw, buffer->size, |
|
209 me->mCallbackCookie, CB_EVENT_FILL_BUFFER); |
|
210 |
|
211 if (actualSize == 0 && buffer->size > 0) { |
|
212 // We've reached EOS but the audio track is not stopped yet, |
|
213 // keep playing silence. |
|
214 memset(buffer->raw, 0, buffer->size); |
|
215 actualSize = buffer->size; |
|
216 } |
|
217 |
|
218 buffer->size = actualSize; |
|
219 } break; |
|
220 |
|
221 case AudioTrack::EVENT_STREAM_END: |
|
222 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Callback wrapper: EVENT_STREAM_END")); |
|
223 (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */, |
|
224 me->mCallbackCookie, CB_EVENT_STREAM_END); |
|
225 break; |
|
226 |
|
227 case AudioTrack::EVENT_NEW_IAUDIOTRACK : |
|
228 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Callback wrapper: EVENT_TEAR_DOWN")); |
|
229 (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */, |
|
230 me->mCallbackCookie, CB_EVENT_TEAR_DOWN); |
|
231 break; |
|
232 |
|
233 default: |
|
234 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("received unknown event type: %d in" |
|
235 " Callback wrapper!", aEvent)); |
|
236 break; |
|
237 } |
|
238 |
|
239 data->Unlock(); |
|
240 } |
|
241 |
|
242 } // namespace mozilla |