Skip to content

Commit

Permalink
[PPAPI] Pepper MediaStream API audio track implementation and example.
Browse files Browse the repository at this point in the history
TBR=jamesr@chromium.org

BUG=330851

Review URL: https://codereview.chromium.org/140783004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@249245 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
penghuang@chromium.org committed Feb 6, 2014
1 parent 8fa42fb commit 2b7a6d3
Show file tree
Hide file tree
Showing 26 changed files with 1,101 additions and 21 deletions.
2 changes: 2 additions & 0 deletions content/content_renderer.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,8 @@
'renderer/p2p/socket_client_impl.h',
'renderer/p2p/socket_dispatcher.cc',
'renderer/p2p/socket_dispatcher.h',
'renderer/pepper/pepper_media_stream_audio_track_host.cc',
'renderer/pepper/pepper_media_stream_audio_track_host.h',
'renderer/pepper/pepper_media_stream_track_host_base.cc',
'renderer/pepper/pepper_media_stream_track_host_base.h',
'renderer/pepper/pepper_media_stream_video_track_host.cc',
Expand Down
190 changes: 190 additions & 0 deletions content/renderer/pepper/pepper_media_stream_audio_track_host.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// 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 "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop_proxy.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/ppb_audio_frame.h"
#include "ppapi/shared_impl/media_stream_frame.h"

using media::AudioParameters;

namespace {

// Max audio buffer duration in milliseconds.
const uint32_t kMaxDuration = 10;

// TODO(penghuang): make this configurable.
const int32_t kNumberOfFrames = 4;

} // namespace

namespace content {

PepperMediaStreamAudioTrackHost::AudioSink::AudioSink(
PepperMediaStreamAudioTrackHost* host)
: host_(host),
frame_data_size_(0),
main_message_loop_proxy_(base::MessageLoopProxy::current()),
weak_factory_(this) {
}

PepperMediaStreamAudioTrackHost::AudioSink::~AudioSink() {
DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
}

void PepperMediaStreamAudioTrackHost::AudioSink::EnqueueFrame(int32_t index) {
DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
DCHECK_GE(index, 0);
DCHECK_LT(index, host_->frame_buffer()->number_of_frames());
base::AutoLock lock(lock_);
frames_.push_back(index);
}

void PepperMediaStreamAudioTrackHost::AudioSink::InitFramesOnMainThread(
int32_t number_of_frames, int32_t frame_size) {
DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
bool result = host_->InitFrames(number_of_frames, frame_size);
// TODO(penghuang): Send PP_ERROR_NOMEMORY to plugin.
CHECK(result);
base::AutoLock lock(lock_);
for (int32_t i = 0; i < number_of_frames; ++i) {
int32_t index = host_->frame_buffer()->DequeueFrame();
DCHECK_GE(index, 0);
frames_.push_back(index);
}
}

void
PepperMediaStreamAudioTrackHost::AudioSink::SendEnqueueFrameMessageOnMainThread(
int32_t index) {
DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
host_->SendEnqueueFrameMessageToPlugin(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 (!frames_.empty()) {
index = frames_.front();
frames_.pop_front();
}
}

if (index != -1) {
// TODO(penghuang): support re-sampling, etc.
ppapi::MediaStreamFrame::Audio* frame =
&(host_->frame_buffer()->GetFramePointer(index)->audio);
frame->header.size = host_->frame_buffer()->frame_size();
frame->header.type = ppapi::MediaStreamFrame::TYPE_AUDIO;
frame->timestamp = timestamp_.InMillisecondsF();
frame->sample_rate = static_cast<PP_AudioFrame_SampleRate>(sample_rate);
frame->number_of_channels = number_of_channels;
frame->number_of_samples = number_of_channels * number_of_frames;
frame->data_size = frame_data_size_;
memcpy(frame->data, audio_data, frame_data_size_);

main_message_loop_proxy_->PostTask(
FROM_HERE,
base::Bind(&AudioSink::SendEnqueueFrameMessageOnMainThread,
weak_factory_.GetWeakPtr(), index));
}
timestamp_ += frame_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((params.sample_rate() == AudioParameters::kTelephoneSampleRate) ||
(params.sample_rate() == AudioParameters::kAudioCDSampleRate));

COMPILE_ASSERT(AudioParameters::kTelephoneSampleRate ==
static_cast<int32_t>(PP_AUDIOFRAME_SAMPLERATE_8000),
audio_sample_rate_does_not_match);
COMPILE_ASSERT(AudioParameters::kAudioCDSampleRate ==
static_cast<int32_t>(PP_AUDIOFRAME_SAMPLERATE_44100),
audio_sample_rate_does_not_match);
audio_params_ = params;

// TODO(penghuang): support setting format more than once.
frame_duration_ = params.GetBufferDuration();
frame_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;
// The size is slightly bigger than necessary, because 8 extra bytes are
// added into the struct. Also see |MediaStreamFrame|.
size_t max_frame_size =
params.sample_rate() * params.bits_per_sample() / 8 *
params.channels() * kMaxDuration / 1000;
size_t size = sizeof(ppapi::MediaStreamFrame::Audio) + max_frame_size;

main_message_loop_proxy_->PostTask(
FROM_HERE,
base::Bind(&AudioSink::InitFramesOnMainThread,
weak_factory_.GetWeakPtr(),
kNumberOfFrames,
static_cast<int32_t>(size)));
}
}

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();
}

void PepperMediaStreamAudioTrackHost::OnClose() {
if (connected_) {
MediaStreamAudioSink::RemoveFromAudioTrack(&audio_sink_, track_);
connected_ = false;
}
}

void PepperMediaStreamAudioTrackHost::OnNewFrameEnqueued() {
int32_t index = frame_buffer()->DequeueFrame();
DCHECK_GE(index, 0);
audio_sink_.EnqueueFrame(index);
}

void PepperMediaStreamAudioTrackHost::DidConnectPendingHostToResource() {
if (!connected_) {
MediaStreamAudioSink::AddToAudioTrack(&audio_sink_, track_);
connected_ = true;
}
}

} // namespace content
125 changes: 125 additions & 0 deletions content/renderer/pepper/pepper_media_stream_audio_track_host.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// 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.

#ifndef CONTENT_RENDERER_PEPPER_PEPPER_MEDIA_STREAM_AUDIO_TRACK_HOST_H_
#define CONTENT_RENDERER_PEPPER_PEPPER_MEDIA_STREAM_AUDIO_TRACK_HOST_H_

#include <deque>

#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "content/public/renderer/media_stream_audio_sink.h"
#include "content/renderer/pepper/pepper_media_stream_track_host_base.h"
#include "media/audio/audio_parameters.h"
#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"

namespace base {
class MessageLoopProxy;
} // namespace base

namespace content {

class PepperMediaStreamAudioTrackHost : public PepperMediaStreamTrackHostBase {
public:
PepperMediaStreamAudioTrackHost(RendererPpapiHost* host,
PP_Instance instance,
PP_Resource resource,
const blink::WebMediaStreamTrack& track);

private:
// A helper class for receiving audio samples in the audio thread.
// This class is created and destroyed on the renderer main thread.
class AudioSink : public MediaStreamAudioSink {
public:
explicit AudioSink(PepperMediaStreamAudioTrackHost* host);
virtual ~AudioSink();

// Enqueues a free frame index into |frames_| which will be used for
// sending audio samples to plugin.
// This function is called on the renderer main thread.
void EnqueueFrame(int32_t index);

private:
void InitFramesOnMainThread(int32_t number_of_frames, int32_t frame_size);
void SendEnqueueFrameMessageOnMainThread(int32_t index);

// MediaStreamAudioSink overrides:
// These two functions should be called on the audio thread.
virtual void OnData(const int16* audio_data,
int sample_rate,
int number_of_channels,
int number_of_frames) OVERRIDE;
virtual void OnSetFormat(const media::AudioParameters& params) OVERRIDE;

// Unowned host which is available during the AudioSink's lifespan.
// It is mainly used in the main thread. But the audio thread will use
// host_->frame_buffer() to read some buffer properties. It is safe
// because the frame_buffer()'s properties will not be changed after
// initialization.
PepperMediaStreamAudioTrackHost* host_;

// Timestamp of the next received audio frame.
// Access only on the audio thread.
base::TimeDelta timestamp_;

// Duration of one audio frame.
// Access only on the audio thread.
base::TimeDelta frame_duration_;

// The current audio parameters.
// Access only on the audio thread.
media::AudioParameters audio_params_;

// The original audio parameters which is set in the first time of
// OnSetFormat being called.
// Access only on the audio thread.
media::AudioParameters original_audio_params_;

// The size of a frame in bytes.
// Access only on the audio thread.
uint32_t frame_data_size_;

// A lock to protect the index queue |frames_|.
base::Lock lock_;

// A queue for free frame indices.
std::deque<int32_t> frames_;

scoped_refptr<base::MessageLoopProxy> main_message_loop_proxy_;

base::ThreadChecker audio_thread_checker_;

base::WeakPtrFactory<AudioSink> weak_factory_;

DISALLOW_COPY_AND_ASSIGN(AudioSink);
};

virtual ~PepperMediaStreamAudioTrackHost();

// PepperMediaStreamTrackHostBase overrides:
virtual void OnClose() OVERRIDE;

// MediaStreamFrameBuffer::Delegate overrides:
virtual void OnNewFrameEnqueued() OVERRIDE;

// ResourceHost overrides:
virtual void DidConnectPendingHostToResource() OVERRIDE;

blink::WebMediaStreamTrack track_;

// True if |audio_sink_| has been added to |blink::WebMediaStreamTrack|
// as a sink.
bool connected_;

AudioSink audio_sink_;

DISALLOW_COPY_AND_ASSIGN(PepperMediaStreamAudioTrackHost);
};

} // namespace content

#endif // CONTENT_RENDERER_PEPPER_PEPPER_MEDIA_STREAM_AUDIO_TRACK_HOST_H_
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ bool PepperMediaStreamTrackHostBase::InitFrames(int32_t number_of_frames,
// Make each frame 4 byte aligned.
frame_size = (frame_size + 3) & ~0x3;

// TODO(penghuang): |HostAllocateSharedMemoryBuffer| uses sync IPC. We should
// avoid it.
int32_t size = number_of_frames * frame_size;
content::RenderThread* render_thread = content::RenderThread::Get();
scoped_ptr<base::SharedMemory> shm(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ void PepperMediaStreamVideoTrackHost::OnVideoFrame(
DCHECK(!frame_data_size_);
frame_data_size_ = VideoFrame::AllocationSize(frame_format_, frame_size_);
int32_t size = sizeof(ppapi::MediaStreamFrame::Video) + frame_data_size_;
InitFrames(kNumberOfFrames, size);
bool result = InitFrames(kNumberOfFrames, size);
// TODO(penghuang): Send PP_ERROR_NOMEMORY to plugin.
CHECK(result);
}

int32_t index = frame_buffer()->DequeueFrame();
Expand Down
12 changes: 10 additions & 2 deletions content/renderer/pepper/resource_converter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "base/message_loop/message_loop.h"
#include "content/public/renderer/renderer_ppapi_host.h"
#include "content/renderer/pepper/pepper_file_system_host.h"
#include "content/renderer/pepper/pepper_media_stream_audio_track_host.h"
#include "content/renderer/pepper/pepper_media_stream_video_track_host.h"
#include "ipc/ipc_message.h"
#include "ppapi/host/ppapi_host.h"
Expand Down Expand Up @@ -112,8 +113,15 @@ bool DOMMediaStreamTrackToResource(
new PpapiPluginMsg_MediaStreamVideoTrack_CreateFromPendingHost(id));
return true;
} else if (track.source().type() == blink::WebMediaStreamSource::TypeAudio) {
// TODO(penghuang): support audio track.
return false;
*pending_renderer_id = host->GetPpapiHost()->AddPendingResourceHost(
scoped_ptr<ppapi::host::ResourceHost>(
new PepperMediaStreamAudioTrackHost(host, instance, 0, track)));
if (*pending_renderer_id == 0)
return false;

create_message->reset(
new PpapiPluginMsg_MediaStreamAudioTrack_CreateFromPendingHost(id));
return true;
}

return false;
Expand Down
12 changes: 12 additions & 0 deletions ppapi/api/ppb_audio_frame.idl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ label Chrome {
*/
enum PP_AudioFrame_SampleRate {
PP_AUDIOFRAME_SAMPLERATE_UNKNOWN = 0,
PP_AUDIOFRAME_SAMPLERATE_8000 = 8000,
PP_AUDIOFRAME_SAMPLERATE_44100 = 44100
};

Expand Down Expand Up @@ -65,6 +66,17 @@ interface PPB_AudioFrame {
*/
void SetTimestamp([in] PP_Resource frame, [in] PP_TimeDelta timestamp);

/**
* Gets the sample rate of the audio frame.
*
* @param[in] frame A <code>PP_Resource</code> corresponding to an audio frame
* resource.
*
* @return The sample rate of the audio frame.
*/
[on_failure=PP_AUDIOFRAME_SAMPLERATE_UNKNOWN]
PP_AudioFrame_SampleRate GetSampleRate([in] PP_Resource frame);

/**
* Gets the sample size of the audio frame.
*
Expand Down
Loading

0 comments on commit 2b7a6d3

Please sign in to comment.