1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/renderer_host/media/audio_sync_reader.h"
6
7 #include <algorithm>
8
9 #include "base/command_line.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/metrics/histogram.h"
12 #include "content/public/common/content_switches.h"
13 #include "media/audio/audio_buffers_state.h"
14 #include "media/audio/audio_parameters.h"
15
16 using media::AudioBus;
17
18 namespace content {
19
AudioSyncReader(base::SharedMemory * shared_memory,const media::AudioParameters & params)20 AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory,
21 const media::AudioParameters& params)
22 : shared_memory_(shared_memory),
23 mute_audio_(CommandLine::ForCurrentProcess()->HasSwitch(
24 switches::kMuteAudio)),
25 packet_size_(shared_memory_->requested_size()),
26 renderer_callback_count_(0),
27 renderer_missed_callback_count_(0),
28 #if defined(OS_MACOSX)
29 maximum_wait_time_(params.GetBufferDuration() / 2),
30 #else
31 // TODO(dalecurtis): Investigate if we can reduce this on all platforms.
32 maximum_wait_time_(base::TimeDelta::FromMilliseconds(20)),
33 #endif
34 buffer_index_(0) {
35 DCHECK_EQ(packet_size_, AudioBus::CalculateMemorySize(params));
36 output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory());
37 output_bus_->Zero();
38 }
39
~AudioSyncReader()40 AudioSyncReader::~AudioSyncReader() {
41 if (!renderer_callback_count_)
42 return;
43
44 // Recording the percentage of deadline misses gives us a rough overview of
45 // how many users might be running into audio glitches.
46 int percentage_missed =
47 100.0 * renderer_missed_callback_count_ / renderer_callback_count_;
48 UMA_HISTOGRAM_PERCENTAGE(
49 "Media.AudioRendererMissedDeadline", percentage_missed);
50 }
51
52 // media::AudioOutputController::SyncReader implementations.
UpdatePendingBytes(uint32 bytes)53 void AudioSyncReader::UpdatePendingBytes(uint32 bytes) {
54 // Zero out the entire output buffer to avoid stuttering/repeating-buffers
55 // in the anomalous case if the renderer is unable to keep up with real-time.
56 output_bus_->Zero();
57 socket_->Send(&bytes, sizeof(bytes));
58 ++buffer_index_;
59 }
60
Read(AudioBus * dest)61 void AudioSyncReader::Read(AudioBus* dest) {
62 ++renderer_callback_count_;
63 if (!WaitUntilDataIsReady()) {
64 ++renderer_missed_callback_count_;
65 dest->Zero();
66 return;
67 }
68
69 if (mute_audio_)
70 dest->Zero();
71 else
72 output_bus_->CopyTo(dest);
73 }
74
Close()75 void AudioSyncReader::Close() {
76 socket_->Close();
77 }
78
Init()79 bool AudioSyncReader::Init() {
80 socket_.reset(new base::CancelableSyncSocket());
81 foreign_socket_.reset(new base::CancelableSyncSocket());
82 return base::CancelableSyncSocket::CreatePair(socket_.get(),
83 foreign_socket_.get());
84 }
85
86 #if defined(OS_WIN)
PrepareForeignSocketHandle(base::ProcessHandle process_handle,base::SyncSocket::Handle * foreign_handle)87 bool AudioSyncReader::PrepareForeignSocketHandle(
88 base::ProcessHandle process_handle,
89 base::SyncSocket::Handle* foreign_handle) {
90 ::DuplicateHandle(GetCurrentProcess(), foreign_socket_->handle(),
91 process_handle, foreign_handle,
92 0, FALSE, DUPLICATE_SAME_ACCESS);
93 return (*foreign_handle != 0);
94 }
95 #else
PrepareForeignSocketHandle(base::ProcessHandle process_handle,base::FileDescriptor * foreign_handle)96 bool AudioSyncReader::PrepareForeignSocketHandle(
97 base::ProcessHandle process_handle,
98 base::FileDescriptor* foreign_handle) {
99 foreign_handle->fd = foreign_socket_->handle();
100 foreign_handle->auto_close = false;
101 return (foreign_handle->fd != -1);
102 }
103 #endif
104
WaitUntilDataIsReady()105 bool AudioSyncReader::WaitUntilDataIsReady() {
106 base::TimeDelta timeout = maximum_wait_time_;
107 const base::TimeTicks start_time = base::TimeTicks::Now();
108 const base::TimeTicks finish_time = start_time + timeout;
109
110 // Check if data is ready and if not, wait a reasonable amount of time for it.
111 //
112 // Data readiness is achieved via parallel counters, one on the renderer side
113 // and one here. Every time a buffer is requested via UpdatePendingBytes(),
114 // |buffer_index_| is incremented. Subsequently every time the renderer has a
115 // buffer ready it increments its counter and sends the counter value over the
116 // SyncSocket. Data is ready when |buffer_index_| matches the counter value
117 // received from the renderer.
118 //
119 // The counter values may temporarily become out of sync if the renderer is
120 // unable to deliver audio fast enough. It's assumed that the renderer will
121 // catch up at some point, which means discarding counter values read from the
122 // SyncSocket which don't match our current buffer index.
123 size_t bytes_received = 0;
124 uint32 renderer_buffer_index = 0;
125 while (timeout.InMicroseconds() > 0) {
126 bytes_received = socket_->ReceiveWithTimeout(
127 &renderer_buffer_index, sizeof(renderer_buffer_index), timeout);
128 if (!bytes_received)
129 break;
130
131 DCHECK_EQ(bytes_received, sizeof(renderer_buffer_index));
132 if (renderer_buffer_index == buffer_index_)
133 break;
134
135 // Reduce the timeout value as receives succeed, but aren't the right index.
136 timeout = finish_time - base::TimeTicks::Now();
137 }
138
139 // Receive timed out or another error occurred. Receive can timeout if the
140 // renderer is unable to deliver audio data within the allotted time.
141 if (!bytes_received || renderer_buffer_index != buffer_index_) {
142 DVLOG(2) << "AudioSyncReader::WaitUntilDataIsReady() timed out.";
143
144 base::TimeDelta time_since_start = base::TimeTicks::Now() - start_time;
145 UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady",
146 time_since_start,
147 base::TimeDelta::FromMilliseconds(1),
148 base::TimeDelta::FromMilliseconds(1000),
149 50);
150 return false;
151 }
152
153 return true;
154 }
155
156 } // namespace content
157