dom/plugins/base/android/ANPAudio.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "base/basictypes.h"
michael@0 7 #include "AndroidBridge.h"
michael@0 8
michael@0 9 #include <android/log.h>
michael@0 10 #include <stdlib.h>
michael@0 11 #include <time.h>
michael@0 12
michael@0 13 #include "assert.h"
michael@0 14 #include "ANPBase.h"
michael@0 15 #include "nsIThread.h"
michael@0 16 #include "nsThreadUtils.h"
michael@0 17 #include "mozilla/Mutex.h"
michael@0 18
michael@0 19 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPluginsAudio" , ## args)
michael@0 20 #define ASSIGN(obj, name) (obj)->name = anp_audio_##name
michael@0 21
michael@0 22 /* android.media.AudioTrack */
michael@0 23 struct AudioTrack {
michael@0 24 jclass at_class;
michael@0 25 jmethodID constructor;
michael@0 26 jmethodID flush;
michael@0 27 jmethodID pause;
michael@0 28 jmethodID play;
michael@0 29 jmethodID setvol;
michael@0 30 jmethodID stop;
michael@0 31 jmethodID write;
michael@0 32 jmethodID getpos;
michael@0 33 jmethodID getstate;
michael@0 34 jmethodID release;
michael@0 35 };
michael@0 36
michael@0 37 enum AudioTrackMode {
michael@0 38 MODE_STATIC = 0,
michael@0 39 MODE_STREAM = 1
michael@0 40 };
michael@0 41
michael@0 42 /* android.media.AudioManager */
michael@0 43 enum AudioManagerStream {
michael@0 44 STREAM_VOICE_CALL = 0,
michael@0 45 STREAM_SYSTEM = 1,
michael@0 46 STREAM_RING = 2,
michael@0 47 STREAM_MUSIC = 3,
michael@0 48 STREAM_ALARM = 4,
michael@0 49 STREAM_NOTIFICATION = 5,
michael@0 50 STREAM_DTMF = 8
michael@0 51 };
michael@0 52
michael@0 53 /* android.media.AudioFormat */
michael@0 54 enum AudioFormatChannel {
michael@0 55 CHANNEL_OUT_MONO = 4,
michael@0 56 CHANNEL_OUT_STEREO = 12
michael@0 57 };
michael@0 58
michael@0 59 enum AudioFormatEncoding {
michael@0 60 ENCODING_PCM_16BIT = 2,
michael@0 61 ENCODING_PCM_8BIT = 3
michael@0 62 };
michael@0 63
michael@0 64 enum AudioFormatState {
michael@0 65 STATE_UNINITIALIZED = 0,
michael@0 66 STATE_INITIALIZED = 1,
michael@0 67 STATE_NO_STATIC_DATA = 2
michael@0 68 };
michael@0 69
michael@0 70 static struct AudioTrack at;
michael@0 71
michael@0 72 static jclass
michael@0 73 init_jni_bindings(JNIEnv *jenv) {
michael@0 74 jclass jc =
michael@0 75 (jclass)jenv->NewGlobalRef(jenv->FindClass("android/media/AudioTrack"));
michael@0 76
michael@0 77 at.constructor = jenv->GetMethodID(jc, "<init>", "(IIIIII)V");
michael@0 78 at.flush = jenv->GetMethodID(jc, "flush", "()V");
michael@0 79 at.pause = jenv->GetMethodID(jc, "pause", "()V");
michael@0 80 at.play = jenv->GetMethodID(jc, "play", "()V");
michael@0 81 at.setvol = jenv->GetMethodID(jc, "setStereoVolume", "(FF)I");
michael@0 82 at.stop = jenv->GetMethodID(jc, "stop", "()V");
michael@0 83 at.write = jenv->GetMethodID(jc, "write", "([BII)I");
michael@0 84 at.getpos = jenv->GetMethodID(jc, "getPlaybackHeadPosition", "()I");
michael@0 85 at.getstate = jenv->GetMethodID(jc, "getState", "()I");
michael@0 86 at.release = jenv->GetMethodID(jc, "release", "()V");
michael@0 87
michael@0 88 return jc;
michael@0 89 }
michael@0 90
michael@0 91 struct ANPAudioTrack {
michael@0 92 jobject output_unit;
michael@0 93 jclass at_class;
michael@0 94
michael@0 95 unsigned int rate;
michael@0 96 unsigned int channels;
michael@0 97 unsigned int bufferSize;
michael@0 98 unsigned int isStopped;
michael@0 99 unsigned int keepGoing;
michael@0 100
michael@0 101 mozilla::Mutex lock;
michael@0 102
michael@0 103 void* user;
michael@0 104 ANPAudioCallbackProc proc;
michael@0 105 ANPSampleFormat format;
michael@0 106
michael@0 107 ANPAudioTrack() : lock("ANPAudioTrack") { }
michael@0 108 };
michael@0 109
michael@0 110 class AudioRunnable : public nsRunnable
michael@0 111 {
michael@0 112 public:
michael@0 113 NS_DECL_NSIRUNNABLE
michael@0 114
michael@0 115 AudioRunnable(ANPAudioTrack* aAudioTrack) {
michael@0 116 mTrack = aAudioTrack;
michael@0 117 }
michael@0 118
michael@0 119 ANPAudioTrack* mTrack;
michael@0 120 };
michael@0 121
michael@0 122 NS_IMETHODIMP
michael@0 123 AudioRunnable::Run()
michael@0 124 {
michael@0 125 PR_SetCurrentThreadName("Android Audio");
michael@0 126
michael@0 127 JNIEnv* jenv = GetJNIForThread();
michael@0 128
michael@0 129 mozilla::AutoLocalJNIFrame autoFrame(jenv, 2);
michael@0 130
michael@0 131 jbyteArray bytearray = jenv->NewByteArray(mTrack->bufferSize);
michael@0 132 if (!bytearray) {
michael@0 133 LOG("AudioRunnable:: Run. Could not create bytearray");
michael@0 134 return NS_ERROR_FAILURE;
michael@0 135 }
michael@0 136
michael@0 137 jbyte *byte = jenv->GetByteArrayElements(bytearray, nullptr);
michael@0 138 if (!byte) {
michael@0 139 LOG("AudioRunnable:: Run. Could not create bytearray");
michael@0 140 return NS_ERROR_FAILURE;
michael@0 141 }
michael@0 142
michael@0 143 ANPAudioBuffer buffer;
michael@0 144 buffer.channelCount = mTrack->channels;
michael@0 145 buffer.format = mTrack->format;
michael@0 146 buffer.bufferData = (void*) byte;
michael@0 147
michael@0 148 while (true)
michael@0 149 {
michael@0 150 // reset the buffer size
michael@0 151 buffer.size = mTrack->bufferSize;
michael@0 152
michael@0 153 {
michael@0 154 mozilla::MutexAutoLock lock(mTrack->lock);
michael@0 155
michael@0 156 if (!mTrack->keepGoing)
michael@0 157 break;
michael@0 158
michael@0 159 // Get data from the plugin
michael@0 160 mTrack->proc(kMoreData_ANPAudioEvent, mTrack->user, &buffer);
michael@0 161 }
michael@0 162
michael@0 163 if (buffer.size == 0) {
michael@0 164 LOG("%p - kMoreData_ANPAudioEvent", mTrack);
michael@0 165 continue;
michael@0 166 }
michael@0 167
michael@0 168 size_t wroteSoFar = 0;
michael@0 169 jint retval;
michael@0 170 do {
michael@0 171 retval = jenv->CallIntMethod(mTrack->output_unit,
michael@0 172 at.write,
michael@0 173 bytearray,
michael@0 174 wroteSoFar,
michael@0 175 buffer.size - wroteSoFar);
michael@0 176 if (retval < 0) {
michael@0 177 LOG("%p - Write failed %d", mTrack, retval);
michael@0 178 break;
michael@0 179 }
michael@0 180
michael@0 181 wroteSoFar += retval;
michael@0 182
michael@0 183 } while(wroteSoFar < buffer.size);
michael@0 184 }
michael@0 185
michael@0 186 jenv->CallVoidMethod(mTrack->output_unit, at.release);
michael@0 187
michael@0 188 jenv->DeleteGlobalRef(mTrack->output_unit);
michael@0 189 jenv->DeleteGlobalRef(mTrack->at_class);
michael@0 190
michael@0 191 delete mTrack;
michael@0 192
michael@0 193 jenv->ReleaseByteArrayElements(bytearray, byte, 0);
michael@0 194
michael@0 195 return NS_OK;
michael@0 196 }
michael@0 197
michael@0 198 ANPAudioTrack*
michael@0 199 anp_audio_newTrack(uint32_t sampleRate, // sampling rate in Hz
michael@0 200 ANPSampleFormat format,
michael@0 201 int channelCount, // MONO=1, STEREO=2
michael@0 202 ANPAudioCallbackProc proc,
michael@0 203 void* user)
michael@0 204 {
michael@0 205 ANPAudioTrack *s = new ANPAudioTrack();
michael@0 206 if (s == nullptr) {
michael@0 207 return nullptr;
michael@0 208 }
michael@0 209
michael@0 210 JNIEnv *jenv = GetJNIForThread();
michael@0 211
michael@0 212 s->at_class = init_jni_bindings(jenv);
michael@0 213 s->rate = sampleRate;
michael@0 214 s->channels = channelCount;
michael@0 215 s->bufferSize = s->rate * s->channels;
michael@0 216 s->isStopped = true;
michael@0 217 s->keepGoing = false;
michael@0 218 s->user = user;
michael@0 219 s->proc = proc;
michael@0 220 s->format = format;
michael@0 221
michael@0 222 int jformat;
michael@0 223 switch (format) {
michael@0 224 case kPCM16Bit_ANPSampleFormat:
michael@0 225 jformat = ENCODING_PCM_16BIT;
michael@0 226 break;
michael@0 227 case kPCM8Bit_ANPSampleFormat:
michael@0 228 jformat = ENCODING_PCM_8BIT;
michael@0 229 break;
michael@0 230 default:
michael@0 231 LOG("Unknown audio format. defaulting to 16bit.");
michael@0 232 jformat = ENCODING_PCM_16BIT;
michael@0 233 break;
michael@0 234 }
michael@0 235
michael@0 236 int jChannels;
michael@0 237 switch (channelCount) {
michael@0 238 case 1:
michael@0 239 jChannels = CHANNEL_OUT_MONO;
michael@0 240 break;
michael@0 241 case 2:
michael@0 242 jChannels = CHANNEL_OUT_STEREO;
michael@0 243 break;
michael@0 244 default:
michael@0 245 LOG("Unknown channel count. defaulting to mono.");
michael@0 246 jChannels = CHANNEL_OUT_MONO;
michael@0 247 break;
michael@0 248 }
michael@0 249
michael@0 250 mozilla::AutoLocalJNIFrame autoFrame(jenv);
michael@0 251
michael@0 252 jobject obj = jenv->NewObject(s->at_class,
michael@0 253 at.constructor,
michael@0 254 STREAM_MUSIC,
michael@0 255 s->rate,
michael@0 256 jChannels,
michael@0 257 jformat,
michael@0 258 s->bufferSize,
michael@0 259 MODE_STREAM);
michael@0 260
michael@0 261 if (autoFrame.CheckForException() || obj == nullptr) {
michael@0 262 jenv->DeleteGlobalRef(s->at_class);
michael@0 263 free(s);
michael@0 264 return nullptr;
michael@0 265 }
michael@0 266
michael@0 267 jint state = jenv->CallIntMethod(obj, at.getstate);
michael@0 268
michael@0 269 if (autoFrame.CheckForException() || state == STATE_UNINITIALIZED) {
michael@0 270 jenv->DeleteGlobalRef(s->at_class);
michael@0 271 free(s);
michael@0 272 return nullptr;
michael@0 273 }
michael@0 274
michael@0 275 s->output_unit = jenv->NewGlobalRef(obj);
michael@0 276 return s;
michael@0 277 }
michael@0 278
michael@0 279 void
michael@0 280 anp_audio_deleteTrack(ANPAudioTrack* s)
michael@0 281 {
michael@0 282 if (s == nullptr) {
michael@0 283 return;
michael@0 284 }
michael@0 285
michael@0 286 mozilla::MutexAutoLock lock(s->lock);
michael@0 287 s->keepGoing = false;
michael@0 288
michael@0 289 // deallocation happens in the AudioThread. There is a
michael@0 290 // potential leak if anp_audio_start is never called, but
michael@0 291 // we do not see that from flash.
michael@0 292 }
michael@0 293
michael@0 294 void
michael@0 295 anp_audio_start(ANPAudioTrack* s)
michael@0 296 {
michael@0 297 if (s == nullptr || s->output_unit == nullptr) {
michael@0 298 return;
michael@0 299 }
michael@0 300
michael@0 301 if (s->keepGoing) {
michael@0 302 // we are already playing. Ignore.
michael@0 303 return;
michael@0 304 }
michael@0 305
michael@0 306 JNIEnv *jenv = GetJNIForThread();
michael@0 307
michael@0 308 mozilla::AutoLocalJNIFrame autoFrame(jenv, 0);
michael@0 309 jenv->CallVoidMethod(s->output_unit, at.play);
michael@0 310
michael@0 311 if (autoFrame.CheckForException()) {
michael@0 312 jenv->DeleteGlobalRef(s->at_class);
michael@0 313 free(s);
michael@0 314 return;
michael@0 315 }
michael@0 316
michael@0 317 s->isStopped = false;
michael@0 318 s->keepGoing = true;
michael@0 319
michael@0 320 // AudioRunnable now owns the ANPAudioTrack
michael@0 321 nsRefPtr<AudioRunnable> runnable = new AudioRunnable(s);
michael@0 322
michael@0 323 nsCOMPtr<nsIThread> thread;
michael@0 324 NS_NewThread(getter_AddRefs(thread), runnable);
michael@0 325 }
michael@0 326
michael@0 327 void
michael@0 328 anp_audio_pause(ANPAudioTrack* s)
michael@0 329 {
michael@0 330 if (s == nullptr || s->output_unit == nullptr) {
michael@0 331 return;
michael@0 332 }
michael@0 333
michael@0 334 JNIEnv *jenv = GetJNIForThread();
michael@0 335
michael@0 336 mozilla::AutoLocalJNIFrame autoFrame(jenv, 0);
michael@0 337 jenv->CallVoidMethod(s->output_unit, at.pause);
michael@0 338 }
michael@0 339
michael@0 340 void
michael@0 341 anp_audio_stop(ANPAudioTrack* s)
michael@0 342 {
michael@0 343 if (s == nullptr || s->output_unit == nullptr) {
michael@0 344 return;
michael@0 345 }
michael@0 346
michael@0 347 s->isStopped = true;
michael@0 348 JNIEnv *jenv = GetJNIForThread();
michael@0 349
michael@0 350 mozilla::AutoLocalJNIFrame autoFrame(jenv, 0);
michael@0 351 jenv->CallVoidMethod(s->output_unit, at.stop);
michael@0 352 }
michael@0 353
michael@0 354 bool
michael@0 355 anp_audio_isStopped(ANPAudioTrack* s)
michael@0 356 {
michael@0 357 return s->isStopped;
michael@0 358 }
michael@0 359
michael@0 360 uint32_t
michael@0 361 anp_audio_trackLatency(ANPAudioTrack* s) {
michael@0 362 // Hardcode an estimate of the system's audio latency. Flash hardcodes
michael@0 363 // similar latency estimates for pre-Honeycomb devices that do not support
michael@0 364 // ANPAudioTrackInterfaceV1's trackLatency(). The Android stock browser
michael@0 365 // calls android::AudioTrack::latency(), an internal Android API that is
michael@0 366 // not available in the public NDK:
michael@0 367 // https://github.com/android/platform_external_webkit/commit/49bf866973cb3b2a6c74c0eab864e9562e4cbab1
michael@0 368 return 100; // milliseconds
michael@0 369 }
michael@0 370
michael@0 371 void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i) {
michael@0 372 _assert(i->inSize == sizeof(*i));
michael@0 373 ASSIGN(i, newTrack);
michael@0 374 ASSIGN(i, deleteTrack);
michael@0 375 ASSIGN(i, start);
michael@0 376 ASSIGN(i, pause);
michael@0 377 ASSIGN(i, stop);
michael@0 378 ASSIGN(i, isStopped);
michael@0 379 }
michael@0 380
michael@0 381 void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1 *i) {
michael@0 382 _assert(i->inSize == sizeof(*i));
michael@0 383 ASSIGN(i, newTrack);
michael@0 384 ASSIGN(i, deleteTrack);
michael@0 385 ASSIGN(i, start);
michael@0 386 ASSIGN(i, pause);
michael@0 387 ASSIGN(i, stop);
michael@0 388 ASSIGN(i, isStopped);
michael@0 389 ASSIGN(i, trackLatency);
michael@0 390 }

mercurial