| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromecast/media/audio/cast_audio_input_stream.h" |
| |
| #include "base/logging.h" |
| #include "chromecast/media/audio/capture_service/constants.h" |
| #include "chromecast/media/audio/capture_service/message_parsing_utils.h" |
| #include "media/audio/audio_manager_base.h" |
| |
| namespace chromecast { |
| namespace media { |
| |
| CastAudioInputStream::CastAudioInputStream( |
| ::media::AudioManagerBase* audio_manager, |
| const ::media::AudioParameters& audio_params, |
| const std::string& device_id) |
| : audio_manager_(audio_manager), audio_params_(audio_params) { |
| DETACH_FROM_THREAD(audio_thread_checker_); |
| LOG(INFO) << __func__ << " " << this |
| << " created from device_id = " << device_id |
| << " with audio_params = {" << audio_params_.AsHumanReadableString() |
| << "}."; |
| } |
| |
| CastAudioInputStream::~CastAudioInputStream() { |
| DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_); |
| } |
| |
| ::media::AudioInputStream::OpenOutcome CastAudioInputStream::Open() { |
| DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_); |
| DCHECK(!capture_service_receiver_); |
| LOG(INFO) << __func__ << " " << this << "."; |
| |
| // Sanity check the audio parameters. |
| ::media::AudioParameters::Format format = audio_params_.format(); |
| DCHECK((format == ::media::AudioParameters::AUDIO_PCM_LINEAR) || |
| (format == ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY)); |
| ::media::ChannelLayout channel_layout = audio_params_.channel_layout(); |
| if ((channel_layout != ::media::CHANNEL_LAYOUT_MONO) && |
| (channel_layout != ::media::CHANNEL_LAYOUT_STEREO)) { |
| LOG(WARNING) << "Unsupported channel layout: " << channel_layout; |
| return ::media::AudioInputStream::OpenOutcome::kFailed; |
| } |
| DCHECK_GE(audio_params_.channels(), 1); |
| DCHECK_LE(audio_params_.channels(), 2); |
| |
| audio_bus_ = ::media::AudioBus::Create(audio_params_.channels(), |
| audio_params_.frames_per_buffer()); |
| stream_info_ = capture_service::StreamInfo{ |
| capture_service::StreamType::kSoftwareEchoCancelled, |
| capture_service::AudioCodec::kPcm, audio_params_.channels(), |
| // Format doesn't matter in the request. |
| capture_service::SampleFormat::LAST_FORMAT, audio_params_.sample_rate(), |
| audio_params_.frames_per_buffer()}; |
| capture_service_receiver_ = |
| std::make_unique<CaptureServiceReceiver>(stream_info_, this); |
| return ::media::AudioInputStream::OpenOutcome::kSuccess; |
| } |
| |
| void CastAudioInputStream::Start(AudioInputCallback* input_callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_); |
| DCHECK(capture_service_receiver_); |
| DCHECK(!input_callback_); |
| DCHECK(input_callback); |
| LOG(INFO) << __func__ << " " << this << "."; |
| input_callback_ = input_callback; |
| capture_service_receiver_->Start(); |
| } |
| |
| void CastAudioInputStream::Stop() { |
| DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_); |
| DCHECK(capture_service_receiver_); |
| LOG(INFO) << __func__ << " " << this << "."; |
| capture_service_receiver_->Stop(); |
| input_callback_ = nullptr; |
| } |
| |
| void CastAudioInputStream::Close() { |
| DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_); |
| LOG(INFO) << __func__ << " " << this << "."; |
| capture_service_receiver_.reset(); |
| audio_bus_.reset(); |
| if (audio_manager_) { |
| audio_manager_->ReleaseInputStream(this); |
| } |
| } |
| |
| double CastAudioInputStream::GetMaxVolume() { |
| return 1.0; |
| } |
| |
| void CastAudioInputStream::SetVolume(double volume) {} |
| |
| double CastAudioInputStream::GetVolume() { |
| return 1.0; |
| } |
| |
| bool CastAudioInputStream::SetAutomaticGainControl(bool enabled) { |
| return false; |
| } |
| |
| bool CastAudioInputStream::GetAutomaticGainControl() { |
| return false; |
| } |
| |
| bool CastAudioInputStream::IsMuted() { |
| return false; |
| } |
| |
| void CastAudioInputStream::SetOutputDeviceForAec( |
| const std::string& output_device_id) { |
| // Not supported. Do nothing. |
| } |
| |
| bool CastAudioInputStream::OnInitialStreamInfo( |
| const capture_service::StreamInfo& stream_info) { |
| const bool is_params_match = |
| stream_info.stream_type == stream_info_.stream_type && |
| stream_info.audio_codec == stream_info_.audio_codec && |
| stream_info.num_channels == stream_info_.num_channels && |
| stream_info.sample_rate == stream_info_.sample_rate && |
| stream_info.frames_per_buffer == stream_info_.frames_per_buffer; |
| LOG_IF(ERROR, !is_params_match) |
| << "Got different parameters from sender, stream_type: " |
| << static_cast<int>(stream_info_.stream_type) << " -> " |
| << static_cast<int>(stream_info.stream_type) |
| << ", audio_codec: " << static_cast<int>(stream_info_.audio_codec) |
| << " -> " << static_cast<int>(stream_info.audio_codec) |
| << ", sample_rate: " << stream_info_.sample_rate << " Hz -> " |
| << stream_info.sample_rate |
| << " Hz, num_channels: " << stream_info_.num_channels << " -> " |
| << stream_info.num_channels |
| << ", frames_per_buffer: " << stream_info_.frames_per_buffer << " -> " |
| << stream_info.frames_per_buffer << "."; |
| stream_info_.sample_format = stream_info.sample_format; |
| LOG(INFO) << "Set sample_format: " |
| << static_cast<int>(stream_info.sample_format); |
| return is_params_match; |
| } |
| |
| bool CastAudioInputStream::OnCaptureData(const char* data, size_t size) { |
| int64_t timestamp_us; |
| if (!capture_service::ReadPcmAudioMessage(data, size, stream_info_, |
| ×tamp_us, audio_bus_.get())) { |
| return false; |
| } |
| |
| DCHECK(input_callback_); |
| input_callback_->OnData(audio_bus_.get(), |
| base::TimeTicks() + base::Microseconds(timestamp_us), |
| /* volume */ 1.0); |
| return true; |
| } |
| |
| void CastAudioInputStream::OnCaptureError() { |
| DCHECK(input_callback_); |
| input_callback_->OnError(); |
| } |
| |
| void CastAudioInputStream::OnCaptureMetadata(const char* data, size_t size) { |
| // Not implemented! |
| } |
| |
| } // namespace media |
| } // namespace chromecast |