diff options
Diffstat (limited to 'chromium/content/renderer/pepper/pepper_media_stream_audio_track_host.cc')
-rw-r--r-- | chromium/content/renderer/pepper/pepper_media_stream_audio_track_host.cc | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/chromium/content/renderer/pepper/pepper_media_stream_audio_track_host.cc b/chromium/content/renderer/pepper/pepper_media_stream_audio_track_host.cc new file mode 100644 index 00000000000..d972c2c061e --- /dev/null +++ b/chromium/content/renderer/pepper/pepper_media_stream_audio_track_host.cc @@ -0,0 +1,265 @@ +// Copyright 2014 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 "content/renderer/pepper/pepper_media_stream_audio_track_host.h" + +#include <algorithm> + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/numerics/safe_math.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/c/ppb_audio_buffer.h" +#include "ppapi/host/dispatch_host_message.h" +#include "ppapi/host/host_message_context.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/shared_impl/media_stream_audio_track_shared.h" +#include "ppapi/shared_impl/media_stream_buffer.h" + +using media::AudioParameters; +using ppapi::host::HostMessageContext; +using ppapi::MediaStreamAudioTrackShared; + +namespace { + +// Max audio buffer duration in milliseconds. +const uint32_t kMaxDuration = 10; + +const int32_t kDefaultNumberOfBuffers = 4; +const int32_t kMaxNumberOfBuffers = 1000; // 10 sec + +// Returns true if the |sample_rate| is supported in +// |PP_AudioBuffer_SampleRate|, otherwise false. +PP_AudioBuffer_SampleRate GetPPSampleRate(int sample_rate) { + switch (sample_rate) { + case 8000: + return PP_AUDIOBUFFER_SAMPLERATE_8000; + case 16000: + return PP_AUDIOBUFFER_SAMPLERATE_16000; + case 22050: + return PP_AUDIOBUFFER_SAMPLERATE_22050; + case 32000: + return PP_AUDIOBUFFER_SAMPLERATE_32000; + case 44100: + return PP_AUDIOBUFFER_SAMPLERATE_44100; + case 48000: + return PP_AUDIOBUFFER_SAMPLERATE_48000; + case 96000: + return PP_AUDIOBUFFER_SAMPLERATE_96000; + case 192000: + return PP_AUDIOBUFFER_SAMPLERATE_192000; + default: + return PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN; + } +} + +} // namespace + +namespace content { + +PepperMediaStreamAudioTrackHost::AudioSink::AudioSink( + PepperMediaStreamAudioTrackHost* host) + : host_(host), + buffer_data_size_(0), + main_message_loop_proxy_(base::MessageLoopProxy::current()), + weak_factory_(this), + number_of_buffers_(kDefaultNumberOfBuffers), + bytes_per_second_(0) {} + +PepperMediaStreamAudioTrackHost::AudioSink::~AudioSink() { + DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current()); +} + +void PepperMediaStreamAudioTrackHost::AudioSink::EnqueueBuffer(int32_t index) { + DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current()); + DCHECK_GE(index, 0); + DCHECK_LT(index, host_->buffer_manager()->number_of_buffers()); + base::AutoLock lock(lock_); + buffers_.push_back(index); +} + +void PepperMediaStreamAudioTrackHost::AudioSink::Configure( + int32_t number_of_buffers) { + DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current()); + bool changed = false; + if (number_of_buffers != number_of_buffers_) + changed = true; + number_of_buffers_ = number_of_buffers; + + // Initialize later in OnSetFormat if bytes_per_second_ is not know yet. + if (changed && bytes_per_second_ > 0) + InitBuffers(); +} + +void PepperMediaStreamAudioTrackHost::AudioSink::SetFormatOnMainThread( + int bytes_per_second) { + bytes_per_second_ = bytes_per_second; + InitBuffers(); +} + +void PepperMediaStreamAudioTrackHost::AudioSink::InitBuffers() { + DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current()); + // The size is slightly bigger than necessary, because 8 extra bytes are + // added into the struct. Also see |MediaStreamBuffer|. + base::CheckedNumeric<int32_t> buffer_size = bytes_per_second_; + buffer_size *= kMaxDuration; + buffer_size /= base::Time::kMillisecondsPerSecond; + buffer_size += sizeof(ppapi::MediaStreamBuffer::Audio); + bool result = host_->InitBuffers(number_of_buffers_, + buffer_size.ValueOrDie(), + kRead); + // TODO(penghuang): Send PP_ERROR_NOMEMORY to plugin. + CHECK(result); + base::AutoLock lock(lock_); + buffers_.clear(); + for (int32_t i = 0; i < number_of_buffers_; ++i) { + int32_t index = host_->buffer_manager()->DequeueBuffer(); + DCHECK_GE(index, 0); + buffers_.push_back(index); + } +} + +void PepperMediaStreamAudioTrackHost::AudioSink:: + SendEnqueueBufferMessageOnMainThread(int32_t index) { + DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current()); + host_->SendEnqueueBufferMessageToPlugin(index); +} + +void PepperMediaStreamAudioTrackHost::AudioSink::OnData(const int16* audio_data, + int sample_rate, + int number_of_channels, + int number_of_frames) { + DCHECK(audio_thread_checker_.CalledOnValidThread()); + DCHECK(audio_data); + DCHECK_EQ(sample_rate, audio_params_.sample_rate()); + DCHECK_EQ(number_of_channels, audio_params_.channels()); + DCHECK_EQ(number_of_frames, audio_params_.frames_per_buffer()); + int32_t index = -1; + { + base::AutoLock lock(lock_); + if (!buffers_.empty()) { + index = buffers_.front(); + buffers_.pop_front(); + } + } + + if (index != -1) { + // TODO(penghuang): support re-sampling, etc. + ppapi::MediaStreamBuffer::Audio* buffer = + &(host_->buffer_manager()->GetBufferPointer(index)->audio); + buffer->header.size = host_->buffer_manager()->buffer_size(); + buffer->header.type = ppapi::MediaStreamBuffer::TYPE_AUDIO; + buffer->timestamp = timestamp_.InMillisecondsF(); + buffer->sample_rate = static_cast<PP_AudioBuffer_SampleRate>(sample_rate); + buffer->number_of_channels = number_of_channels; + buffer->number_of_samples = number_of_channels * number_of_frames; + buffer->data_size = buffer_data_size_; + memcpy(buffer->data, audio_data, buffer_data_size_); + + main_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&AudioSink::SendEnqueueBufferMessageOnMainThread, + weak_factory_.GetWeakPtr(), + index)); + } + timestamp_ += buffer_duration_; +} + +void PepperMediaStreamAudioTrackHost::AudioSink::OnSetFormat( + const AudioParameters& params) { + DCHECK(params.IsValid()); + DCHECK_LE(params.GetBufferDuration().InMilliseconds(), kMaxDuration); + DCHECK_EQ(params.bits_per_sample(), 16); + DCHECK_NE(GetPPSampleRate(params.sample_rate()), + PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN); + + audio_params_ = params; + + // TODO(penghuang): support setting format more than once. + buffer_duration_ = params.GetBufferDuration(); + buffer_data_size_ = params.GetBytesPerBuffer(); + + if (original_audio_params_.IsValid()) { + DCHECK_EQ(params.sample_rate(), original_audio_params_.sample_rate()); + DCHECK_EQ(params.bits_per_sample(), + original_audio_params_.bits_per_sample()); + DCHECK_EQ(params.channels(), original_audio_params_.channels()); + } else { + audio_thread_checker_.DetachFromThread(); + original_audio_params_ = params; + + main_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&AudioSink::SetFormatOnMainThread, + weak_factory_.GetWeakPtr(), + params.GetBytesPerSecond())); + } +} + +PepperMediaStreamAudioTrackHost::PepperMediaStreamAudioTrackHost( + RendererPpapiHost* host, + PP_Instance instance, + PP_Resource resource, + const blink::WebMediaStreamTrack& track) + : PepperMediaStreamTrackHostBase(host, instance, resource), + track_(track), + connected_(false), + audio_sink_(this) { + DCHECK(!track_.isNull()); +} + +PepperMediaStreamAudioTrackHost::~PepperMediaStreamAudioTrackHost() { + OnClose(); +} + +int32_t PepperMediaStreamAudioTrackHost::OnResourceMessageReceived( + const IPC::Message& msg, + HostMessageContext* context) { + PPAPI_BEGIN_MESSAGE_MAP(PepperMediaStreamAudioTrackHost, msg) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_MediaStreamAudioTrack_Configure, OnHostMsgConfigure) + PPAPI_END_MESSAGE_MAP() + return PepperMediaStreamTrackHostBase::OnResourceMessageReceived(msg, + context); +} + +int32_t PepperMediaStreamAudioTrackHost::OnHostMsgConfigure( + HostMessageContext* context, + const MediaStreamAudioTrackShared::Attributes& attributes) { + if (!MediaStreamAudioTrackShared::VerifyAttributes(attributes)) + return PP_ERROR_BADARGUMENT; + + int32_t buffers = attributes.buffers + ? std::min(kMaxNumberOfBuffers, attributes.buffers) + : kDefaultNumberOfBuffers; + audio_sink_.Configure(buffers); + + context->reply_msg = PpapiPluginMsg_MediaStreamAudioTrack_ConfigureReply(); + return PP_OK; +} + +void PepperMediaStreamAudioTrackHost::OnClose() { + if (connected_) { + MediaStreamAudioSink::RemoveFromAudioTrack(&audio_sink_, track_); + connected_ = false; + } +} + +void PepperMediaStreamAudioTrackHost::OnNewBufferEnqueued() { + int32_t index = buffer_manager()->DequeueBuffer(); + DCHECK_GE(index, 0); + audio_sink_.EnqueueBuffer(index); +} + +void PepperMediaStreamAudioTrackHost::DidConnectPendingHostToResource() { + if (!connected_) { + MediaStreamAudioSink::AddToAudioTrack(&audio_sink_, track_); + connected_ = true; + } +} + +} // namespace content |