summaryrefslogtreecommitdiffstats
path: root/chromium/content/renderer/pepper/pepper_media_stream_audio_track_host.cc
diff options
context:
space:
mode:
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.cc265
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