• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
JavaAudioTrack(NativeRegistration * native_reg,rtc::scoped_ptr<GlobalRef> audio_track)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 
~JavaAudioTrack()44 AudioTrackJni::JavaAudioTrack::~JavaAudioTrack() {}
45 
InitPlayout(int sample_rate,int channels)46 void AudioTrackJni::JavaAudioTrack::InitPlayout(int sample_rate, int channels) {
47   audio_track_->CallVoidMethod(init_playout_, sample_rate, channels);
48 }
49 
StartPlayout()50 bool AudioTrackJni::JavaAudioTrack::StartPlayout() {
51   return audio_track_->CallBooleanMethod(start_playout_);
52 }
53 
StopPlayout()54 bool AudioTrackJni::JavaAudioTrack::StopPlayout() {
55   return audio_track_->CallBooleanMethod(stop_playout_);
56 }
57 
SetStreamVolume(int volume)58 bool AudioTrackJni::JavaAudioTrack::SetStreamVolume(int volume) {
59   return audio_track_->CallBooleanMethod(set_stream_volume_, volume);
60 }
61 
GetStreamMaxVolume()62 int AudioTrackJni::JavaAudioTrack::GetStreamMaxVolume() {
63   return audio_track_->CallIntMethod(get_stream_max_volume_);
64 }
65 
GetStreamVolume()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.
AudioTrackJni(AudioManager * audio_manager)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 
~AudioTrackJni()102 AudioTrackJni::~AudioTrackJni() {
103   ALOGD("~dtor%s", GetThreadInfo().c_str());
104   RTC_DCHECK(thread_checker_.CalledOnValidThread());
105   Terminate();
106 }
107 
Init()108 int32_t AudioTrackJni::Init() {
109   ALOGD("Init%s", GetThreadInfo().c_str());
110   RTC_DCHECK(thread_checker_.CalledOnValidThread());
111   return 0;
112 }
113 
Terminate()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 
InitPlayout()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 
StartPlayout()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 
StopPlayout()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 
SpeakerVolumeIsAvailable(bool & available)165 int AudioTrackJni::SpeakerVolumeIsAvailable(bool& available) {
166   available = true;
167   return 0;
168 }
169 
SetSpeakerVolume(uint32_t volume)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 
MaxSpeakerVolume(uint32_t & max_volume) const176 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 
MinSpeakerVolume(uint32_t & min_volume) const183 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 
SpeakerVolume(uint32_t & volume) const190 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.
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)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 
CacheDirectBufferAddress(JNIEnv * env,jobject obj,jobject byte_buffer,jlong nativeAudioTrack)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 
OnCacheDirectBufferAddress(JNIEnv * env,jobject byte_buffer)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 
GetPlayoutData(JNIEnv * env,jobject obj,jint length,jlong nativeAudioTrack)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'.
OnGetPlayoutData(size_t length)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