Chromium Code Reviews| Index: media/audio/android/audio_track_output_stream.cc | 
| diff --git a/media/audio/android/audio_track_output_stream.cc b/media/audio/android/audio_track_output_stream.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..06c4acef0e37d34006f0e94cf18a7051b9987e48 | 
| --- /dev/null | 
| +++ b/media/audio/android/audio_track_output_stream.cc | 
| @@ -0,0 +1,150 @@ | 
| +// Copyright 2017 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "media/audio/android/audio_track_output_stream.h" | 
| + | 
| +#include "base/bind.h" | 
| +#include "base/bind_helpers.h" | 
| +#include "base/logging.h" | 
| +#include "base/single_thread_task_runner.h" | 
| +#include "base/time/default_tick_clock.h" | 
| +#include "jni/AudioTrackOutputStream_jni.h" | 
| +#include "media/audio/audio_manager_base.h" | 
| + | 
| +using base::android::AttachCurrentThread; | 
| + | 
| +namespace media { | 
| + | 
| +// Android audio format. For more information, please see: | 
| +// https://developer.android.com/reference/android/media/AudioFormat.html | 
| +enum { | 
| + kEncodingPcmFloat = 4, // ENCODING_PCM_FLOAT | 
| + kEncodingAc3 = 5, // ENCODING_AC3 | 
| + kEncodingEac3 = 6, // ENCODING_E_AC3 | 
| +}; | 
| + | 
| +AudioTrackOutputStream::AudioTrackOutputStream(AudioManagerBase* manager, | 
| + const AudioParameters& params) | 
| + : params_(params), | 
| + audio_manager_(manager), | 
| + callback_(nullptr), | 
| 
 
DaleCurtis
2017/06/15 21:46:31
Inline initialize these parameters in the header f
 
AndyWu
2017/08/02 01:43:38
Done.
 
 | 
| + audio_bus_(AudioBus::Create(params_)), | 
| + muted_(false), | 
| + volume_(1.0), | 
| + total_read_frames_(0), | 
| + tick_clock_(new base::DefaultTickClock()) { | 
| 
 
DaleCurtis
2017/06/15 21:46:31
No need for this unless you're going to add a test
 
AndyWu
2017/08/02 01:43:38
Done.
 
 | 
| + audio_bus_->Zero(); | 
| +} | 
| + | 
| +AudioTrackOutputStream::~AudioTrackOutputStream() { | 
| + DCHECK(!callback_); | 
| +} | 
| + | 
| +bool AudioTrackOutputStream::Open() { | 
| + DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 
| + JNIEnv* env = AttachCurrentThread(); | 
| + j_audio_output_stream_.Reset(Java_AudioTrackOutputStream_create(env)); | 
| + | 
| + int format = kEncodingPcmFloat; | 
| + if (params_.IsBitstreamFormat()) { | 
| + if (params_.format() == AudioParameters::AUDIO_BITSTREAM_AC3) { | 
| + format = kEncodingAc3; | 
| + } else if (params_.format() == AudioParameters::AUDIO_BITSTREAM_EAC3) { | 
| + format = kEncodingEac3; | 
| + } else { | 
| + NOTREACHED(); | 
| + } | 
| + } | 
| + | 
| + return Java_AudioTrackOutputStream_open(env, j_audio_output_stream_.obj(), | 
| + params_.channels(), | 
| + params_.sample_rate(), format); | 
| +} | 
| + | 
| +void AudioTrackOutputStream::Start(AudioSourceCallback* callback) { | 
| + DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 
| + callback_ = callback; | 
| + total_read_frames_ = 0; | 
| + Java_AudioTrackOutputStream_start(AttachCurrentThread(), | 
| + j_audio_output_stream_.obj(), | 
| + reinterpret_cast<intptr_t>(this)); | 
| +} | 
| + | 
| +void AudioTrackOutputStream::Stop() { | 
| + DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 
| + Java_AudioTrackOutputStream_stop(AttachCurrentThread(), | 
| + j_audio_output_stream_.obj()); | 
| + callback_ = nullptr; | 
| +} | 
| + | 
| +void AudioTrackOutputStream::Close() { | 
| + DCHECK(!callback_); | 
| + DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 
| + | 
| + Java_AudioTrackOutputStream_close(AttachCurrentThread(), | 
| + j_audio_output_stream_.obj()); | 
| + audio_manager_->ReleaseOutputStream(this); | 
| +} | 
| + | 
| +void AudioTrackOutputStream::SetMute(bool muted) { | 
| + if (muted_ == muted) | 
| + return; | 
| + | 
| + muted_ = muted; | 
| + Java_AudioTrackOutputStream_setVolume(AttachCurrentThread(), | 
| + j_audio_output_stream_.obj(), | 
| + muted_ ? 0.0 : volume_); | 
| +} | 
| + | 
| +void AudioTrackOutputStream::SetVolume(double volume) { | 
| + // Track |volume_| since AudioTrack uses a scaled value. | 
| + volume_ = volume; | 
| + if (muted_) | 
| + return; | 
| + | 
| + Java_AudioTrackOutputStream_setVolume(AttachCurrentThread(), | 
| + j_audio_output_stream_.obj(), volume); | 
| +}; | 
| + | 
| +void AudioTrackOutputStream::GetVolume(double* volume) { | 
| + *volume = volume_; | 
| +}; | 
| + | 
| +// AudioOutputStream::SourceCallback implementation methods called from Java. | 
| +int AudioTrackOutputStream::OnMoreData(JNIEnv* env, | 
| + jobject obj, | 
| + jobject audio_data, | 
| + int total_played_frames) { | 
| 
 
DaleCurtis
2017/06/15 21:46:31
As Chris notes, this should be an int64_t.
 
AndyWu
2017/08/02 01:43:38
AudioTrack.getPlaybackHeadPosition()[1] returns a
 
DaleCurtis
2017/08/03 01:38:10
Overflow isn't defined for signed types, so if you
 
AndyWu
2017/08/03 17:11:17
Well, I consider 0x7FFFFFFF + 1 = 0x80000000 is ov
 
DaleCurtis
2017/08/03 17:22:55
Thanks for changing it. For future reference, what
 
AndyWu
2017/08/03 21:58:45
Thanks for the great information. :) It does make
 
 | 
| + if (!callback_) | 
| + return 0; | 
| + | 
| + int delay_in_frame = total_read_frames_ - total_played_frames; | 
| 
 
DaleCurtis
2017/06/15 21:46:31
Ditto; int64_t.
 
AndyWu
2017/08/02 01:43:38
Done.
 
 | 
| + if (delay_in_frame < 0) | 
| 
 
DaleCurtis
2017/06/15 21:46:31
Should be impossible?, DCHECK instead?
 
AndyWu
2017/08/02 01:43:38
Normally, it should not happen. However, it could
 
 | 
| + delay_in_frame = 0; | 
| + base::TimeDelta delay = base::TimeDelta::FromSecondsD( | 
| 
 
DaleCurtis
2017/06/15 21:46:31
Use AudioTimestampHelper::FramesToTime().
 
AndyWu
2017/08/02 01:43:38
Done.
 
 | 
| + static_cast<double>(delay_in_frame) / params_.sample_rate()); | 
| + | 
| + callback_->OnMoreData(delay, tick_clock_->NowTicks(), 0, audio_bus_.get()); | 
| + | 
| + if (audio_bus_->data_size() <= 0) | 
| + return 0; | 
| + | 
| + total_read_frames_ += audio_bus_->frames(); | 
| + | 
| + void* native_bus = env->GetDirectBufferAddress(audio_data); | 
| 
 
DaleCurtis
2017/06/15 21:46:31
Get this pointer ahead of time and use AudioBus::W
 
AndyWu
2017/08/02 01:43:38
TBD
 
 | 
| + memcpy(native_bus, audio_bus_->channel(0), audio_bus_->data_size()); | 
| + return audio_bus_->data_size(); | 
| +} | 
| + | 
| +void AudioTrackOutputStream::OnError(JNIEnv* env, jobject obj) { | 
| + DCHECK(callback_); | 
| + callback_->OnError(); | 
| +} | 
| + | 
| +// static | 
| +bool AudioTrackOutputStream::RegisterAudioTrackOutputStream(JNIEnv* env) { | 
| + return RegisterNativesImpl(env); | 
| +} | 
| + | 
| +} // namespace media |