blob: d972c2c061e2882eb1257d92fc9981e18a296ba5 [file] [log] [blame]
// 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