forked from sanyaade-mobiledev/chromium.src
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PPAPI] Pepper MediaStream API audio track implementation and example.
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
Showing
26 changed files
with
1,101 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
190 changes: 190 additions & 0 deletions
190
content/renderer/pepper/pepper_media_stream_audio_track_host.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
125
content/renderer/pepper/pepper_media_stream_audio_track_host.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.