Home | History | Annotate | Download | only in android
      1 /*
      2  *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/modules/audio_device/android/audio_manager.h"
     12 #include "webrtc/modules/audio_device/android/audio_track_jni.h"
     13 
     14 #include <utility>
     15 
     16 #include <android/log.h>
     17 
     18 #include "webrtc/base/arraysize.h"
     19 #include "webrtc/base/checks.h"
     20 #include "webrtc/base/format_macros.h"
     21 
     22 #define TAG "AudioTrackJni"
     23 #define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
     24 #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
     25 #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
     26 #define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
     27 #define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
     28 
     29 namespace webrtc {
     30 
     31 // AudioTrackJni::JavaAudioTrack implementation.
     32 AudioTrackJni::JavaAudioTrack::JavaAudioTrack(
     33     NativeRegistration* native_reg,
     34     rtc::scoped_ptr<GlobalRef> audio_track)
     35     : audio_track_(std::move(audio_track)),
     36       init_playout_(native_reg->GetMethodId("initPlayout", "(II)V")),
     37       start_playout_(native_reg->GetMethodId("startPlayout", "()Z")),
     38       stop_playout_(native_reg->GetMethodId("stopPlayout", "()Z")),
     39       set_stream_volume_(native_reg->GetMethodId("setStreamVolume", "(I)Z")),
     40       get_stream_max_volume_(
     41           native_reg->GetMethodId("getStreamMaxVolume", "()I")),
     42       get_stream_volume_(native_reg->GetMethodId("getStreamVolume", "()I")) {}
     43 
     44 AudioTrackJni::JavaAudioTrack::~JavaAudioTrack() {}
     45 
     46 void AudioTrackJni::JavaAudioTrack::InitPlayout(int sample_rate, int channels) {
     47   audio_track_->CallVoidMethod(init_playout_, sample_rate, channels);
     48 }
     49 
     50 bool AudioTrackJni::JavaAudioTrack::StartPlayout() {
     51   return audio_track_->CallBooleanMethod(start_playout_);
     52 }
     53 
     54 bool AudioTrackJni::JavaAudioTrack::StopPlayout() {
     55   return audio_track_->CallBooleanMethod(stop_playout_);
     56 }
     57 
     58 bool AudioTrackJni::JavaAudioTrack::SetStreamVolume(int volume) {
     59   return audio_track_->CallBooleanMethod(set_stream_volume_, volume);
     60 }
     61 
     62 int AudioTrackJni::JavaAudioTrack::GetStreamMaxVolume() {
     63   return audio_track_->CallIntMethod(get_stream_max_volume_);
     64 }
     65 
     66 int AudioTrackJni::JavaAudioTrack::GetStreamVolume() {
     67   return audio_track_->CallIntMethod(get_stream_volume_);
     68 }
     69 
     70 // TODO(henrika): possible extend usage of AudioManager and add it as member.
     71 AudioTrackJni::AudioTrackJni(AudioManager* audio_manager)
     72     : j_environment_(JVM::GetInstance()->environment()),
     73       audio_parameters_(audio_manager->GetPlayoutAudioParameters()),
     74       direct_buffer_address_(nullptr),
     75       direct_buffer_capacity_in_bytes_(0),
     76       frames_per_buffer_(0),
     77       initialized_(false),
     78       playing_(false),
     79       audio_device_buffer_(nullptr) {
     80   ALOGD("ctor%s", GetThreadInfo().c_str());
     81   RTC_DCHECK(audio_parameters_.is_valid());
     82   RTC_CHECK(j_environment_);
     83   JNINativeMethod native_methods[] = {
     84       {"nativeCacheDirectBufferAddress", "(Ljava/nio/ByteBuffer;J)V",
     85       reinterpret_cast<void*>(
     86           &webrtc::AudioTrackJni::CacheDirectBufferAddress)},
     87       {"nativeGetPlayoutData", "(IJ)V",
     88       reinterpret_cast<void*>(&webrtc::AudioTrackJni::GetPlayoutData)}};
     89   j_native_registration_ = j_environment_->RegisterNatives(
     90       "org/webrtc/voiceengine/WebRtcAudioTrack",
     91       native_methods, arraysize(native_methods));
     92   j_audio_track_.reset(new JavaAudioTrack(
     93       j_native_registration_.get(),
     94       j_native_registration_->NewObject(
     95           "<init>", "(Landroid/content/Context;J)V",
     96           JVM::GetInstance()->context(), PointerTojlong(this))));
     97   // Detach from this thread since we want to use the checker to verify calls
     98   // from the Java based audio thread.
     99   thread_checker_java_.DetachFromThread();
    100 }
    101 
    102 AudioTrackJni::~AudioTrackJni() {
    103   ALOGD("~dtor%s", GetThreadInfo().c_str());
    104   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    105   Terminate();
    106 }
    107 
    108 int32_t AudioTrackJni::Init() {
    109   ALOGD("Init%s", GetThreadInfo().c_str());
    110   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    111   return 0;
    112 }
    113 
    114 int32_t AudioTrackJni::Terminate() {
    115   ALOGD("Terminate%s", GetThreadInfo().c_str());
    116   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    117   StopPlayout();
    118   return 0;
    119 }
    120 
    121 int32_t AudioTrackJni::InitPlayout() {
    122   ALOGD("InitPlayout%s", GetThreadInfo().c_str());
    123   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    124   RTC_DCHECK(!initialized_);
    125   RTC_DCHECK(!playing_);
    126   j_audio_track_->InitPlayout(
    127       audio_parameters_.sample_rate(), audio_parameters_.channels());
    128   initialized_ = true;
    129   return 0;
    130 }
    131 
    132 int32_t AudioTrackJni::StartPlayout() {
    133   ALOGD("StartPlayout%s", GetThreadInfo().c_str());
    134   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    135   RTC_DCHECK(initialized_);
    136   RTC_DCHECK(!playing_);
    137   if (!j_audio_track_->StartPlayout()) {
    138     ALOGE("StartPlayout failed!");
    139     return -1;
    140   }
    141   playing_ = true;
    142   return 0;
    143 }
    144 
    145 int32_t AudioTrackJni::StopPlayout() {
    146   ALOGD("StopPlayout%s", GetThreadInfo().c_str());
    147   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    148   if (!initialized_ || !playing_) {
    149     return 0;
    150   }
    151   if (!j_audio_track_->StopPlayout()) {
    152     ALOGE("StopPlayout failed!");
    153     return -1;
    154   }
    155   // If we don't detach here, we will hit a RTC_DCHECK in OnDataIsRecorded()
    156   // next time StartRecording() is called since it will create a new Java
    157   // thread.
    158   thread_checker_java_.DetachFromThread();
    159   initialized_ = false;
    160   playing_ = false;
    161   direct_buffer_address_ = nullptr;
    162   return 0;
    163 }
    164 
    165 int AudioTrackJni::SpeakerVolumeIsAvailable(bool& available) {
    166   available = true;
    167   return 0;
    168 }
    169 
    170 int AudioTrackJni::SetSpeakerVolume(uint32_t volume) {
    171   ALOGD("SetSpeakerVolume(%d)%s", volume, GetThreadInfo().c_str());
    172   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    173   return j_audio_track_->SetStreamVolume(volume) ? 0 : -1;
    174 }
    175 
    176 int AudioTrackJni::MaxSpeakerVolume(uint32_t& max_volume) const {
    177   ALOGD("MaxSpeakerVolume%s", GetThreadInfo().c_str());
    178   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    179   max_volume = j_audio_track_->GetStreamMaxVolume();
    180   return 0;
    181 }
    182 
    183 int AudioTrackJni::MinSpeakerVolume(uint32_t& min_volume) const {
    184   ALOGD("MaxSpeakerVolume%s", GetThreadInfo().c_str());
    185   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    186   min_volume = 0;
    187   return 0;
    188 }
    189 
    190 int AudioTrackJni::SpeakerVolume(uint32_t& volume) const {
    191   ALOGD("SpeakerVolume%s", GetThreadInfo().c_str());
    192   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    193   volume = j_audio_track_->GetStreamVolume();
    194   return 0;
    195 }
    196 
    197 // TODO(henrika): possibly add stereo support.
    198 void AudioTrackJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
    199   ALOGD("AttachAudioBuffer%s", GetThreadInfo().c_str());
    200   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    201   audio_device_buffer_ = audioBuffer;
    202   const int sample_rate_hz = audio_parameters_.sample_rate();
    203   ALOGD("SetPlayoutSampleRate(%d)", sample_rate_hz);
    204   audio_device_buffer_->SetPlayoutSampleRate(sample_rate_hz);
    205   const size_t channels = audio_parameters_.channels();
    206   ALOGD("SetPlayoutChannels(%" PRIuS ")", channels);
    207   audio_device_buffer_->SetPlayoutChannels(channels);
    208 }
    209 
    210 void JNICALL AudioTrackJni::CacheDirectBufferAddress(
    211     JNIEnv* env, jobject obj, jobject byte_buffer, jlong nativeAudioTrack) {
    212   webrtc::AudioTrackJni* this_object =
    213       reinterpret_cast<webrtc::AudioTrackJni*> (nativeAudioTrack);
    214   this_object->OnCacheDirectBufferAddress(env, byte_buffer);
    215 }
    216 
    217 void AudioTrackJni::OnCacheDirectBufferAddress(
    218     JNIEnv* env, jobject byte_buffer) {
    219   ALOGD("OnCacheDirectBufferAddress");
    220   RTC_DCHECK(thread_checker_.CalledOnValidThread());
    221   RTC_DCHECK(!direct_buffer_address_);
    222   direct_buffer_address_ =
    223       env->GetDirectBufferAddress(byte_buffer);
    224   jlong capacity = env->GetDirectBufferCapacity(byte_buffer);
    225   ALOGD("direct buffer capacity: %lld", capacity);
    226   direct_buffer_capacity_in_bytes_ = static_cast<size_t>(capacity);
    227   frames_per_buffer_ = direct_buffer_capacity_in_bytes_ / kBytesPerFrame;
    228   ALOGD("frames_per_buffer: %" PRIuS, frames_per_buffer_);
    229 }
    230 
    231 void JNICALL AudioTrackJni::GetPlayoutData(
    232   JNIEnv* env, jobject obj, jint length, jlong nativeAudioTrack) {
    233   webrtc::AudioTrackJni* this_object =
    234       reinterpret_cast<webrtc::AudioTrackJni*> (nativeAudioTrack);
    235   this_object->OnGetPlayoutData(static_cast<size_t>(length));
    236 }
    237 
    238 // This method is called on a high-priority thread from Java. The name of
    239 // the thread is 'AudioRecordTrack'.
    240 void AudioTrackJni::OnGetPlayoutData(size_t length) {
    241   RTC_DCHECK(thread_checker_java_.CalledOnValidThread());
    242   RTC_DCHECK_EQ(frames_per_buffer_, length / kBytesPerFrame);
    243   if (!audio_device_buffer_) {
    244     ALOGE("AttachAudioBuffer has not been called!");
    245     return;
    246   }
    247   // Pull decoded data (in 16-bit PCM format) from jitter buffer.
    248   int samples = audio_device_buffer_->RequestPlayoutData(frames_per_buffer_);
    249   if (samples <= 0) {
    250     ALOGE("AudioDeviceBuffer::RequestPlayoutData failed!");
    251     return;
    252   }
    253   RTC_DCHECK_EQ(static_cast<size_t>(samples), frames_per_buffer_);
    254   // Copy decoded data into common byte buffer to ensure that it can be
    255   // written to the Java based audio track.
    256   samples = audio_device_buffer_->GetPlayoutData(direct_buffer_address_);
    257   RTC_DCHECK_EQ(length, kBytesPerFrame * samples);
    258 }
    259 
    260 }  // namespace webrtc
    261