summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/renderer_host/audio_renderer_host.cc25
-rw-r--r--chrome/browser/renderer_host/audio_renderer_host.h9
-rw-r--r--chrome/common/render_messages_internal.h6
-rw-r--r--chrome/renderer/audio_message_filter.cc7
-rw-r--r--chrome/renderer/audio_message_filter.h6
-rw-r--r--chrome/renderer/audio_message_filter_unittest.cc406
-rw-r--r--chrome/renderer/media/audio_renderer_impl.cc60
-rw-r--r--chrome/renderer/media/audio_renderer_impl.h34
-rw-r--r--media/filters/audio_renderer_base.cc17
-rw-r--r--media/filters/audio_renderer_base.h14
-rw-r--r--media/filters/audio_renderer_impl.cc3
-rw-r--r--media/filters/null_audio_renderer.cc5
12 files changed, 371 insertions, 221 deletions
diff --git a/chrome/browser/renderer_host/audio_renderer_host.cc b/chrome/browser/renderer_host/audio_renderer_host.cc
index f07d8e5..1d2bf07 100644
--- a/chrome/browser/renderer_host/audio_renderer_host.cc
+++ b/chrome/browser/renderer_host/audio_renderer_host.cc
@@ -216,6 +216,7 @@ size_t AudioRendererHost::IPCAudioSource::OnMoreData(AudioOutputStream* stream,
size_t size = push_source_.OnMoreData(stream, dest, max_size);
{
AutoLock auto_lock(lock_);
+ last_copied_bytes_ = size;
SubmitPacketRequest(&auto_lock);
}
return size;
@@ -239,6 +240,11 @@ void AudioRendererHost::IPCAudioSource::NotifyPacketReady(
{
AutoLock auto_lock(lock_);
outstanding_request_ = false;
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ if (IPC::Logging::current()->Enabled()) {
+ RecordRoundTripLatency(base::Time::Now() - outstanding_request_time_);
+ }
+#endif
// If reported size is greater than capacity of the shared memory, we have
// an error.
if (decoded_packet_size <= decoded_packet_size_) {
@@ -272,7 +278,24 @@ void AudioRendererHost::IPCAudioSource::SubmitPacketRequest_Locked() {
(push_source_.UnProcessedBytes() + decoded_packet_size_ <=
buffer_capacity_)) {
outstanding_request_ = true;
- host_->Send(new ViewMsg_RequestAudioPacket(route_id_, stream_id_));
+ outstanding_request_time_ = base::Time::Now();
+
+ // This variable keeps track of the total amount of bytes buffered for
+ // the associated AudioOutputStream. This value should consist of bytes
+ // buffered in AudioOutputStream and those kept inside |push_source_|.
+ // TODO(hclam): since we have no information about the amount of buffered
+ // bytes in the hardware buffer in AudioOutputStream, we make our best
+ // guess by using the amount of the last copy. This should be a good guess
+ // for Windows since it does double buffering but we shold change this
+ // when AudioOutputStream has the API to query remaining buffer.
+ size_t buffered_bytes = last_copied_bytes_ +
+ push_source_.UnProcessedBytes();
+ host_->Send(
+ new ViewMsg_RequestAudioPacket(
+ route_id_,
+ stream_id_,
+ buffered_bytes,
+ outstanding_request_time_.ToInternalValue()));
}
}
diff --git a/chrome/browser/renderer_host/audio_renderer_host.h b/chrome/browser/renderer_host/audio_renderer_host.h
index f330f86..961917e 100644
--- a/chrome/browser/renderer_host/audio_renderer_host.h
+++ b/chrome/browser/renderer_host/audio_renderer_host.h
@@ -25,7 +25,7 @@
// AudioRendererHost::IPCAudioSource is a container of AudioOutputStream and
// provide audio packets to the associated AudioOutputStream through IPC. It
// performs the logic for buffering and controlling the AudioOutputStream.
-//
+//
// Here is a state diagram for the IPCAudioSource:
//
// .---------> [ Stopped ] <--------.
@@ -310,10 +310,17 @@ class AudioRendererHost : public base::RefCountedThreadSafe<AudioRendererHost> {
AudioOutputStream::State state_;
base::SharedMemory shared_memory_;
PushSource push_source_;
+
+ // Flag that indicates there is an outstanding request.
bool outstanding_request_;
+ base::Time outstanding_request_time_;
+
+ // Number of bytes copied in the last OnMoreData call.
+ size_t last_copied_bytes_;
// Protects:
// - |outstanding_requests_|
+ // - |last_copied_bytes_|
// - |push_source_|
Lock lock_;
};
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index f7ba56c..7de2db7 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -508,8 +508,10 @@ IPC_BEGIN_MESSAGES(View)
bool /* Whether it is visible */)
// Sent by AudioRendererHost to renderer to request an audio packet.
- IPC_MESSAGE_ROUTED1(ViewMsg_RequestAudioPacket,
- int /* stream id */)
+ IPC_MESSAGE_ROUTED3(ViewMsg_RequestAudioPacket,
+ int /* stream id */,
+ size_t /* bytes in buffer */,
+ int64 /* message timestamp */)
// Tell the renderer process that the audio stream has been created, renderer
// process would be given a ShareMemoryHandle that it should write to from
diff --git a/chrome/renderer/audio_message_filter.cc b/chrome/renderer/audio_message_filter.cc
index 944118d..0714b71 100644
--- a/chrome/renderer/audio_message_filter.cc
+++ b/chrome/renderer/audio_message_filter.cc
@@ -74,7 +74,9 @@ void AudioMessageFilter::OnChannelClosing() {
}
void AudioMessageFilter::OnRequestPacket(const IPC::Message& msg,
- int stream_id) {
+ int stream_id,
+ size_t bytes_in_buffer,
+ int64 message_timestamp) {
Delegate* delegate = delegates_.Lookup(stream_id);
if (!delegate) {
DLOG(WARNING) << "Got audio packet request for a non-existent or removed"
@@ -90,7 +92,8 @@ void AudioMessageFilter::OnRequestPacket(const IPC::Message& msg,
}
#endif
- delegate->OnRequestPacket();
+ delegate->OnRequestPacket(bytes_in_buffer,
+ base::Time::FromInternalValue(message_timestamp));
#ifdef IPC_MESSAGE_LOG_ENABLED
if (logger->Enabled()) {
diff --git a/chrome/renderer/audio_message_filter.h b/chrome/renderer/audio_message_filter.h
index 6e90edb..7626b3c 100644
--- a/chrome/renderer/audio_message_filter.h
+++ b/chrome/renderer/audio_message_filter.h
@@ -21,7 +21,8 @@ class AudioMessageFilter : public IPC::ChannelProxy::MessageFilter {
class Delegate {
public:
// Called when an audio packet is requested from the browser process.
- virtual void OnRequestPacket() = 0;
+ virtual void OnRequestPacket(size_t bytes_in_buffer,
+ const base::Time& message_timestamp) = 0;
// Called when state of an audio stream has changed in the browser process.
virtual void OnStateChanged(AudioOutputStream::State state, int info) = 0;
@@ -59,7 +60,8 @@ class AudioMessageFilter : public IPC::ChannelProxy::MessageFilter {
virtual void OnChannelClosing();
// Received when browser process wants more audio packet.
- void OnRequestPacket(const IPC::Message& msg, int stream_id);
+ void OnRequestPacket(const IPC::Message& msg, int stream_id,
+ size_t bytes_in_buffer, int64 message_timestamp);
// Received when browser process has created an audio output stream.
void OnStreamCreated(int stream_id, base::SharedMemoryHandle handle,
diff --git a/chrome/renderer/audio_message_filter_unittest.cc b/chrome/renderer/audio_message_filter_unittest.cc
index bc9fc8e..d09a406 100644
--- a/chrome/renderer/audio_message_filter_unittest.cc
+++ b/chrome/renderer/audio_message_filter_unittest.cc
@@ -1,193 +1,213 @@
-// Copyright (c) 2009 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 "base/logging.h"
-#include "base/message_loop.h"
-#include "chrome/common/render_messages.h"
-#include "chrome/renderer/audio_message_filter.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-class MockAudioDelegate : public AudioMessageFilter::Delegate {
- public:
- MockAudioDelegate() {
- Reset();
- }
-
- virtual void OnRequestPacket() {
- request_packet_received_ = true;
- }
-
- virtual void OnStateChanged(AudioOutputStream::State state, int info) {
- state_changed_received_ = true;
- state_ = state;
- info_ = info;
- }
-
- virtual void OnCreated(base::SharedMemoryHandle handle, size_t length) {
- created_received_ = true;
- handle_ = handle;
- length_ = length;
- }
-
- virtual void OnVolume(double left, double right) {
- volume_received_ = true;
- left_ = left;
- right_ = right;
- }
-
- void Reset() {
- request_packet_received_ = false;
-
- state_changed_received_ = false;
- state_ = AudioOutputStream::STATE_ERROR;
- info_ = 0;
-
- created_received_ = false;
- handle_ = base::SharedMemory::NULLHandle();
- length_ = 0;
-
- volume_received_ = false;
- left_ = 0;
- right_ = 0;
- }
-
- bool request_packet_received() { return request_packet_received_; }
-
- bool state_changed_received() { return state_changed_received_; }
- AudioOutputStream::State state() { return state_; }
- int info() { return info_; }
-
- bool created_received() { return created_received_; }
- base::SharedMemoryHandle handle() { return handle_; }
- size_t length() { return length_; }
-
- bool volume_received() { return volume_received_; }
- double left() { return left_; }
- double right() { return right_; }
-
- private:
- bool request_packet_received_;
-
- bool state_changed_received_;
- AudioOutputStream::State state_;
- int info_;
-
- bool created_received_;
- base::SharedMemoryHandle handle_;
- size_t length_;
-
- bool volume_received_;
- double left_;
- double right_;
-
- DISALLOW_COPY_AND_ASSIGN(MockAudioDelegate);
-};
-
-} // namespace
-
-TEST(AudioMessageFilterTest, Basic) {
- MessageLoop message_loop(MessageLoop::TYPE_IO);
-
- const int kRouteId = 0;
- scoped_refptr<AudioMessageFilter> filter = new AudioMessageFilter(kRouteId);
-
- MockAudioDelegate delegate;
- int stream_id = filter->AddDelegate(&delegate);
-
- // ViewMsg_RequestAudioPacket
- EXPECT_FALSE(delegate.request_packet_received());
- filter->OnMessageReceived(ViewMsg_RequestAudioPacket(kRouteId, stream_id));
- EXPECT_TRUE(delegate.request_packet_received());
- delegate.Reset();
-
- // ViewMsg_NotifyAudioStreamStateChanged
- const AudioOutputStream::State kState = AudioOutputStream::STATE_STARTED;
- const int kStateInfo = 100;
- EXPECT_FALSE(delegate.state_changed_received());
- filter->OnMessageReceived(
- ViewMsg_NotifyAudioStreamStateChanged(kRouteId,
- stream_id,
- kState,
- kStateInfo));
- EXPECT_TRUE(delegate.state_changed_received());
- EXPECT_TRUE(kState == delegate.state());
- EXPECT_EQ(kStateInfo, delegate.info());
- delegate.Reset();
-
- // ViewMsg_NotifyAudioStreamCreated
- const size_t kLength = 1024;
- EXPECT_FALSE(delegate.created_received());
- filter->OnMessageReceived(
- ViewMsg_NotifyAudioStreamCreated(kRouteId,
- stream_id,
- base::SharedMemory::NULLHandle(),
- kLength));
- EXPECT_TRUE(delegate.created_received());
- EXPECT_FALSE(base::SharedMemory::IsHandleValid(delegate.handle()));
- EXPECT_EQ(kLength, delegate.length());
- delegate.Reset();
-
- // ViewMsg_NotifyAudioStreamVolume
- const double kLeftVolume = 1.0;
- const double kRightVolume = 2.0;
- EXPECT_FALSE(delegate.volume_received());
- filter->OnMessageReceived(
- ViewMsg_NotifyAudioStreamVolume(kRouteId, stream_id,
- kLeftVolume, kRightVolume));
- EXPECT_TRUE(delegate.volume_received());
- EXPECT_EQ(kLeftVolume, delegate.left());
- EXPECT_EQ(kRightVolume, delegate.right());
- delegate.Reset();
-
- message_loop.RunAllPending();
-}
-
-TEST(AudioMessageFilterTest, Delegates) {
- MessageLoop message_loop(MessageLoop::TYPE_IO);
-
- const int kRouteId = 0;
- scoped_refptr<AudioMessageFilter> filter = new AudioMessageFilter(kRouteId);
-
- MockAudioDelegate delegate1;
- MockAudioDelegate delegate2;
-
- int stream_id1 = filter->AddDelegate(&delegate1);
- int stream_id2 = filter->AddDelegate(&delegate2);
-
- // Send an IPC message. Make sure the correct delegate gets called.
- EXPECT_FALSE(delegate1.request_packet_received());
- EXPECT_FALSE(delegate2.request_packet_received());
- filter->OnMessageReceived(ViewMsg_RequestAudioPacket(kRouteId, stream_id1));
- EXPECT_TRUE(delegate1.request_packet_received());
- EXPECT_FALSE(delegate2.request_packet_received());
- delegate1.Reset();
-
- EXPECT_FALSE(delegate1.request_packet_received());
- EXPECT_FALSE(delegate2.request_packet_received());
- filter->OnMessageReceived(ViewMsg_RequestAudioPacket(kRouteId, stream_id2));
- EXPECT_FALSE(delegate1.request_packet_received());
- EXPECT_TRUE(delegate2.request_packet_received());
- delegate2.Reset();
-
- // Send a message of a different route id, a message is not received.
- EXPECT_FALSE(delegate1.request_packet_received());
- filter->OnMessageReceived(ViewMsg_RequestAudioPacket(kRouteId + 1,
- stream_id1));
- EXPECT_FALSE(delegate1.request_packet_received());
-
- // Remove the delegates. Make sure they won't get called.
- filter->RemoveDelegate(stream_id1);
- EXPECT_FALSE(delegate1.request_packet_received());
- filter->OnMessageReceived(ViewMsg_RequestAudioPacket(kRouteId, stream_id1));
- EXPECT_FALSE(delegate1.request_packet_received());
-
- filter->RemoveDelegate(stream_id2);
- EXPECT_FALSE(delegate2.request_packet_received());
- filter->OnMessageReceived(ViewMsg_RequestAudioPacket(kRouteId, stream_id2));
- EXPECT_FALSE(delegate2.request_packet_received());
-
- message_loop.RunAllPending();
-}
+// Copyright (c) 2009 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 "base/logging.h"
+#include "base/message_loop.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/renderer/audio_message_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MockAudioDelegate : public AudioMessageFilter::Delegate {
+ public:
+ MockAudioDelegate() {
+ Reset();
+ }
+
+ virtual void OnRequestPacket(size_t bytes_in_buffer,
+ const base::Time& message_timestamp) {
+ request_packet_received_ = true;
+ bytes_in_buffer_ = bytes_in_buffer;
+ message_timestamp_ = message_timestamp;
+ }
+
+ virtual void OnStateChanged(AudioOutputStream::State state, int info) {
+ state_changed_received_ = true;
+ state_ = state;
+ info_ = info;
+ }
+
+ virtual void OnCreated(base::SharedMemoryHandle handle, size_t length) {
+ created_received_ = true;
+ handle_ = handle;
+ length_ = length;
+ }
+
+ virtual void OnVolume(double left, double right) {
+ volume_received_ = true;
+ left_ = left;
+ right_ = right;
+ }
+
+ void Reset() {
+ request_packet_received_ = false;
+ bytes_in_buffer_ = 0;
+ message_timestamp_ = base::Time();
+
+ state_changed_received_ = false;
+ state_ = AudioOutputStream::STATE_ERROR;
+ info_ = 0;
+
+ created_received_ = false;
+ handle_ = base::SharedMemory::NULLHandle();
+ length_ = 0;
+
+ volume_received_ = false;
+ left_ = 0;
+ right_ = 0;
+ }
+
+ bool request_packet_received() { return request_packet_received_; }
+ size_t bytes_in_buffer() { return bytes_in_buffer_; }
+ const base::Time& message_timestamp() { return message_timestamp_; }
+
+ bool state_changed_received() { return state_changed_received_; }
+ AudioOutputStream::State state() { return state_; }
+ int info() { return info_; }
+
+ bool created_received() { return created_received_; }
+ base::SharedMemoryHandle handle() { return handle_; }
+ size_t length() { return length_; }
+
+ bool volume_received() { return volume_received_; }
+ double left() { return left_; }
+ double right() { return right_; }
+
+ private:
+ bool request_packet_received_;
+ size_t bytes_in_buffer_;
+ base::Time message_timestamp_;
+
+ bool state_changed_received_;
+ AudioOutputStream::State state_;
+ int info_;
+
+ bool created_received_;
+ base::SharedMemoryHandle handle_;
+ size_t length_;
+
+ bool volume_received_;
+ double left_;
+ double right_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockAudioDelegate);
+};
+
+} // namespace
+
+TEST(AudioMessageFilterTest, Basic) {
+ MessageLoop message_loop(MessageLoop::TYPE_IO);
+
+ const int kRouteId = 0;
+ scoped_refptr<AudioMessageFilter> filter = new AudioMessageFilter(kRouteId);
+
+ MockAudioDelegate delegate;
+ int stream_id = filter->AddDelegate(&delegate);
+
+ // ViewMsg_RequestAudioPacket
+ const size_t kSizeInBuffer = 1024;
+ const int64 kMessageTimestamp = 99;
+ EXPECT_FALSE(delegate.request_packet_received());
+ filter->OnMessageReceived(ViewMsg_RequestAudioPacket(kRouteId,
+ stream_id,
+ kSizeInBuffer,
+ kMessageTimestamp));
+ EXPECT_TRUE(delegate.request_packet_received());
+ EXPECT_EQ(kSizeInBuffer, delegate.bytes_in_buffer());
+ EXPECT_EQ(kMessageTimestamp, delegate.message_timestamp().ToInternalValue());
+ delegate.Reset();
+
+ // ViewMsg_NotifyAudioStreamStateChanged
+ const AudioOutputStream::State kState = AudioOutputStream::STATE_STARTED;
+ const int kStateInfo = 100;
+ EXPECT_FALSE(delegate.state_changed_received());
+ filter->OnMessageReceived(
+ ViewMsg_NotifyAudioStreamStateChanged(kRouteId,
+ stream_id,
+ kState,
+ kStateInfo));
+ EXPECT_TRUE(delegate.state_changed_received());
+ EXPECT_TRUE(kState == delegate.state());
+ EXPECT_EQ(kStateInfo, delegate.info());
+ delegate.Reset();
+
+ // ViewMsg_NotifyAudioStreamCreated
+ const size_t kLength = 1024;
+ EXPECT_FALSE(delegate.created_received());
+ filter->OnMessageReceived(
+ ViewMsg_NotifyAudioStreamCreated(kRouteId,
+ stream_id,
+ base::SharedMemory::NULLHandle(),
+ kLength));
+ EXPECT_TRUE(delegate.created_received());
+ EXPECT_FALSE(base::SharedMemory::IsHandleValid(delegate.handle()));
+ EXPECT_EQ(kLength, delegate.length());
+ delegate.Reset();
+
+ // ViewMsg_NotifyAudioStreamVolume
+ const double kLeftVolume = 1.0;
+ const double kRightVolume = 2.0;
+ EXPECT_FALSE(delegate.volume_received());
+ filter->OnMessageReceived(
+ ViewMsg_NotifyAudioStreamVolume(kRouteId, stream_id,
+ kLeftVolume, kRightVolume));
+ EXPECT_TRUE(delegate.volume_received());
+ EXPECT_EQ(kLeftVolume, delegate.left());
+ EXPECT_EQ(kRightVolume, delegate.right());
+ delegate.Reset();
+
+ message_loop.RunAllPending();
+}
+
+TEST(AudioMessageFilterTest, Delegates) {
+ MessageLoop message_loop(MessageLoop::TYPE_IO);
+
+ const int kRouteId = 0;
+ scoped_refptr<AudioMessageFilter> filter = new AudioMessageFilter(kRouteId);
+
+ MockAudioDelegate delegate1;
+ MockAudioDelegate delegate2;
+
+ int stream_id1 = filter->AddDelegate(&delegate1);
+ int stream_id2 = filter->AddDelegate(&delegate2);
+
+ // Send an IPC message. Make sure the correct delegate gets called.
+ EXPECT_FALSE(delegate1.request_packet_received());
+ EXPECT_FALSE(delegate2.request_packet_received());
+ filter->OnMessageReceived(
+ ViewMsg_RequestAudioPacket(kRouteId, stream_id1, 0, 0));
+ EXPECT_TRUE(delegate1.request_packet_received());
+ EXPECT_FALSE(delegate2.request_packet_received());
+ delegate1.Reset();
+
+ EXPECT_FALSE(delegate1.request_packet_received());
+ EXPECT_FALSE(delegate2.request_packet_received());
+ filter->OnMessageReceived(
+ ViewMsg_RequestAudioPacket(kRouteId, stream_id2, 0, 0));
+ EXPECT_FALSE(delegate1.request_packet_received());
+ EXPECT_TRUE(delegate2.request_packet_received());
+ delegate2.Reset();
+
+ // Send a message of a different route id, a message is not received.
+ EXPECT_FALSE(delegate1.request_packet_received());
+ filter->OnMessageReceived(
+ ViewMsg_RequestAudioPacket(kRouteId + 1, stream_id1, 0, 0));
+ EXPECT_FALSE(delegate1.request_packet_received());
+
+ // Remove the delegates. Make sure they won't get called.
+ filter->RemoveDelegate(stream_id1);
+ EXPECT_FALSE(delegate1.request_packet_received());
+ filter->OnMessageReceived(
+ ViewMsg_RequestAudioPacket(kRouteId, stream_id1, 0, 0));
+ EXPECT_FALSE(delegate1.request_packet_received());
+
+ filter->RemoveDelegate(stream_id2);
+ EXPECT_FALSE(delegate2.request_packet_received());
+ filter->OnMessageReceived(
+ ViewMsg_RequestAudioPacket(kRouteId, stream_id2, 0, 0));
+ EXPECT_FALSE(delegate2.request_packet_received());
+
+ message_loop.RunAllPending();
+}
diff --git a/chrome/renderer/media/audio_renderer_impl.cc b/chrome/renderer/media/audio_renderer_impl.cc
index 9dd365c..73f2320 100644
--- a/chrome/renderer/media/audio_renderer_impl.cc
+++ b/chrome/renderer/media/audio_renderer_impl.cc
@@ -28,14 +28,18 @@ const int kMillisecondsPreroll = 400;
AudioRendererImpl::AudioRendererImpl(AudioMessageFilter* filter)
: AudioRendererBase(kDefaultMaxQueueSize),
+ channels_(0),
+ sample_rate_(0),
+ sample_bits_(0),
+ bytes_per_second_(0),
filter_(filter),
stream_id_(0),
shared_memory_(NULL),
shared_memory_size_(0),
io_loop_(filter->message_loop()),
stopped_(false),
- pending_request_(false),
playback_rate_(0.0f),
+ pending_request_(false),
prerolling_(true),
preroll_bytes_(0) {
DCHECK(io_loop_);
@@ -44,6 +48,14 @@ AudioRendererImpl::AudioRendererImpl(AudioMessageFilter* filter)
AudioRendererImpl::~AudioRendererImpl() {
}
+base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) {
+ if (bytes_per_second_) {
+ return base::TimeDelta::FromMicroseconds(
+ base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second_);
+ }
+ return base::TimeDelta();
+}
+
bool AudioRendererImpl::IsMediaFormatSupported(
const media::MediaFormat& media_format) {
int channels;
@@ -54,24 +66,24 @@ bool AudioRendererImpl::IsMediaFormatSupported(
bool AudioRendererImpl::OnInitialize(const media::MediaFormat& media_format) {
// Parse integer values in MediaFormat.
- int channels;
- int sample_rate;
- int sample_bits;
- if (!ParseMediaFormat(media_format, &channels, &sample_rate, &sample_bits)) {
+ if (!ParseMediaFormat(media_format,
+ &channels_,
+ &sample_rate_,
+ &sample_bits_)) {
return false;
}
// Create the audio output stream in browser process.
- size_t bytes_per_second = sample_rate * channels * sample_bits / 8;
- size_t packet_size = bytes_per_second * kMillisecondsPerPacket / 1000;
+ bytes_per_second_ = sample_rate_ * channels_ * sample_bits_ / 8;
+ size_t packet_size = bytes_per_second_ * kMillisecondsPerPacket / 1000;
size_t buffer_capacity = packet_size * kPacketsInBuffer;
// Calculate the amount for prerolling.
- preroll_bytes_ = bytes_per_second * kMillisecondsPreroll / 1000;
+ preroll_bytes_ = bytes_per_second_ * kMillisecondsPreroll / 1000;
io_loop_->PostTask(FROM_HERE,
NewRunnableMethod(this, &AudioRendererImpl::OnCreateStream,
- AudioManager::AUDIO_PCM_LINEAR, channels, sample_rate, sample_bits,
+ AudioManager::AUDIO_PCM_LINEAR, channels_, sample_rate_, sample_bits_,
packet_size, buffer_capacity));
return true;
}
@@ -153,13 +165,19 @@ void AudioRendererImpl::OnCreated(base::SharedMemoryHandle handle,
shared_memory_size_ = length;
}
-void AudioRendererImpl::OnRequestPacket() {
+void AudioRendererImpl::OnRequestPacket(size_t bytes_in_buffer,
+ const base::Time& message_timestamp) {
DCHECK(MessageLoop::current() == io_loop_);
{
AutoLock auto_lock(lock_);
DCHECK(!pending_request_);
pending_request_ = true;
+
+ // Use the information provided by the IPC message to adjust the playback
+ // delay.
+ request_timestamp_ = message_timestamp;
+ request_delay_ = ConvertToDuration(bytes_in_buffer);
}
// Try to fill in the fulfil the packet request.
@@ -253,12 +271,32 @@ void AudioRendererImpl::OnNotifyPacketReady() {
return;
if (pending_request_ && playback_rate_ > 0.0f) {
DCHECK(shared_memory_.get());
+
+ // Adjust the playback delay.
+ base::Time current_time = base::Time::Now();
+
+ // Save a local copy of the request delay.
+ base::TimeDelta request_delay = request_delay_;
+ if (current_time > request_timestamp_) {
+ base::TimeDelta receive_latency = current_time - request_timestamp_;
+
+ // If the receive latency is too much it may offset all the delay.
+ if (receive_latency >= request_delay) {
+ request_delay = base::TimeDelta();
+ } else {
+ request_delay -= receive_latency;
+ }
+ }
+
size_t filled = FillBuffer(static_cast<uint8*>(shared_memory_->memory()),
shared_memory_size_,
- playback_rate_);
+ playback_rate_,
+ request_delay);
// TODO(hclam): we should try to fill in the buffer as much as possible.
if (filled > 0) {
pending_request_ = false;
+ request_delay_ = base::TimeDelta();
+ request_timestamp_ = base::Time();
// Then tell browser process we are done filling into the buffer.
filter_->Send(
new ViewHostMsg_NotifyAudioPacketReady(0, stream_id_, filled));
diff --git a/chrome/renderer/media/audio_renderer_impl.h b/chrome/renderer/media/audio_renderer_impl.h
index 6a24dfc..162cf09 100644
--- a/chrome/renderer/media/audio_renderer_impl.h
+++ b/chrome/renderer/media/audio_renderer_impl.h
@@ -116,7 +116,8 @@ class AudioRendererImpl : public media::AudioRendererBase,
// Methods called on IO thread ----------------------------------------------
// AudioMessageFilter::Delegate methods, called by AudioMessageFilter.
- void OnRequestPacket();
+ void OnRequestPacket(size_t bytes_in_buffer,
+ const base::Time& message_timestamp);
void OnStateChanged(AudioOutputStream::State state, int info);
void OnCreated(base::SharedMemoryHandle handle, size_t length);
void OnVolume(double left, double right);
@@ -144,6 +145,11 @@ class AudioRendererImpl : public media::AudioRendererBase,
explicit AudioRendererImpl(AudioMessageFilter* filter);
virtual ~AudioRendererImpl();
+ // Helper methods.
+ // Convert number of bytes to duration of time using information about the
+ // number of channels, sample rate and sample bits.
+ base::TimeDelta ConvertToDuration(int bytes);
+
// Methods call on IO thread ------------------------------------------------
// The following methods are tasks posted on the IO thread that needs to
// be executed on that thread. They interact with AudioMessageFilter and
@@ -157,6 +163,12 @@ class AudioRendererImpl : public media::AudioRendererBase,
void OnNotifyPacketReady();
void OnDestroy();
+ // Information about the audio stream.
+ int channels_;
+ int sample_rate_;
+ int sample_bits_;
+ size_t bytes_per_second_;
+
scoped_refptr<AudioMessageFilter> filter_;
// ID of the stream created in the browser process.
@@ -170,16 +182,32 @@ class AudioRendererImpl : public media::AudioRendererBase,
MessageLoop* io_loop_;
// Protects:
- // - |playback_rate_|
// - |stopped_|
+ // - |playback_rate_|
// - |pending_request_|
+ // - |request_timestamp_|
+ // - |request_delay_|
Lock lock_;
+
+ // A flag that indicates this filter is called to stop.
bool stopped_;
- bool pending_request_;
+
+ // Keeps the current playback rate.
float playback_rate_;
+ // A flag that indicates an outstanding packet request.
+ bool pending_request_;
+
+ // The time when a request is made.
+ base::Time request_timestamp_;
+
+ // The delay for the requested packet to be played.
+ base::TimeDelta request_delay_;
+
// State variables for prerolling.
bool prerolling_;
+
+ // Remaining bytes for prerolling to complete.
size_t preroll_bytes_;
DISALLOW_COPY_AND_ASSIGN(AudioRendererImpl);
diff --git a/media/filters/audio_renderer_base.cc b/media/filters/audio_renderer_base.cc
index 22d275b..681e84b 100644
--- a/media/filters/audio_renderer_base.cc
+++ b/media/filters/audio_renderer_base.cc
@@ -99,10 +99,15 @@ void AudioRendererBase::OnReadComplete(Buffer* buffer_in) {
}
// TODO(scherkus): clean up FillBuffer().. it's overly complex!!
-size_t AudioRendererBase::FillBuffer(uint8* dest, size_t dest_len,
- float rate) {
+size_t AudioRendererBase::FillBuffer(uint8* dest,
+ size_t dest_len,
+ float rate,
+ const base::TimeDelta& playback_delay) {
size_t buffers_released = 0;
size_t dest_written = 0;
+
+ // The timestamp of the last buffer written during the last call to
+ // FillBuffer().
base::TimeDelta last_fill_buffer_time;
{
AutoLock auto_lock(lock_);
@@ -198,6 +203,14 @@ size_t AudioRendererBase::FillBuffer(uint8* dest, size_t dest_len,
// Update the pipeline's time if it was set last time.
if (last_fill_buffer_time.InMicroseconds() > 0) {
+ // Adjust the |last_fill_buffer_time| with the playback delay.
+ // TODO(hclam): If there is a playback delay, the pipeline would not be
+ // updated with a correct timestamp when the stream is played at the very
+ // end since we use decoded packets to trigger time updates. A better
+ // solution is to start a timer when an audio packet is decoded to allow
+ // finer time update events.
+ if (playback_delay < last_fill_buffer_time)
+ last_fill_buffer_time -= playback_delay;
host_->SetTime(last_fill_buffer_time);
}
diff --git a/media/filters/audio_renderer_base.h b/media/filters/audio_renderer_base.h
index 83111aa..2273c7d 100644
--- a/media/filters/audio_renderer_base.h
+++ b/media/filters/audio_renderer_base.h
@@ -58,7 +58,7 @@ class AudioRendererBase : public AudioRenderer {
virtual void OnReadComplete(Buffer* buffer_in);
// Fills the given buffer with audio data by dequeuing buffers and copying the
- // data into the |dest|. FillBuffer also takes care of updating the clock.
+ // data into the |dest|. FillBuffer() also takes care of updating the clock.
// Returns the number of bytes copied into |dest|, which may be less than
// equal to |len|.
//
@@ -67,8 +67,18 @@ class AudioRendererBase : public AudioRenderer {
// enough. In such scenarios, the callee should zero out unused portions
// of their buffer to playback silence.
//
+ // FillBuffer() updates the pipeline's playback timestamp. If FillBuffer() is
+ // not called at the same rate as audio samples are played, then the reported
+ // timestamp in the pipeline will be ahead of the actual audio playback. In
+ // this case |playback_delay| should be used to indicate when in the future
+ // should the filled buffer be played. If FillBuffer() is called as the audio
+ // hardware plays the buffer, then |playback_delay| should be zero.
+ //
// Safe to call on any thread.
- size_t FillBuffer(uint8* dest, size_t len, float rate);
+ size_t FillBuffer(uint8* dest,
+ size_t len,
+ float rate,
+ const base::TimeDelta& playback_delay);
// Helper to parse a media format and return whether we were successful
// retrieving all the information we care about.
diff --git a/media/filters/audio_renderer_impl.cc b/media/filters/audio_renderer_impl.cc
index 95e1401..180aa76 100644
--- a/media/filters/audio_renderer_impl.cc
+++ b/media/filters/audio_renderer_impl.cc
@@ -57,7 +57,8 @@ size_t AudioRendererImpl::OnMoreData(AudioOutputStream* stream, void* dest_void,
// TODO(scherkus): Maybe change OnMoreData to pass in char/uint8 or similar.
// TODO(fbarchard): Waveout_output_win.h should handle zero length buffers
// without clicking.
- return FillBuffer(static_cast<uint8*>(dest_void), len, playback_rate_);
+ return FillBuffer(static_cast<uint8*>(dest_void), len,
+ playback_rate_, base::TimeDelta());
}
void AudioRendererImpl::OnClose(AudioOutputStream* stream) {
diff --git a/media/filters/null_audio_renderer.cc b/media/filters/null_audio_renderer.cc
index b01e939..db72837 100644
--- a/media/filters/null_audio_renderer.cc
+++ b/media/filters/null_audio_renderer.cc
@@ -51,7 +51,10 @@ void NullAudioRenderer::ThreadMain() {
// Only consume buffers when actually playing.
if (playback_rate_ > 0.0f) {
- size_t bytes = FillBuffer(buffer_.get(), buffer_size_, playback_rate_);
+ size_t bytes = FillBuffer(buffer_.get(),
+ buffer_size_,
+ playback_rate_,
+ base::TimeDelta());
// Calculate our sleep duration, taking playback rate into consideration.
sleep_in_milliseconds =