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.
5 #include "content/browser/renderer_host/media/audio_sync_reader.h"
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"
16 using media::AudioBus
;
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),
31 // TODO(dalecurtis): Investigate if we can reduce this on all platforms.
32 maximum_wait_time_(base::TimeDelta::FromMilliseconds(20)),
35 DCHECK_EQ(packet_size_
, AudioBus::CalculateMemorySize(params
));
36 output_bus_
= AudioBus::WrapMemory(params
, shared_memory
->memory());
40 AudioSyncReader::~AudioSyncReader() {
41 if (!renderer_callback_count_
)
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
);
52 // media::AudioOutputController::SyncReader implementations.
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.
57 socket_
->Send(&bytes
, sizeof(bytes
));
61 void AudioSyncReader::Read(AudioBus
* dest
) {
62 ++renderer_callback_count_
;
63 if (!WaitUntilDataIsReady()) {
64 ++renderer_missed_callback_count_
;
72 output_bus_
->CopyTo(dest
);
75 void AudioSyncReader::Close() {
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());
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);
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);
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
;
110 // Check if data is ready and if not, wait a reasonable amount of time for it.
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.
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
);
131 DCHECK_EQ(bytes_received
, sizeof(renderer_buffer_index
));
132 if (renderer_buffer_index
== buffer_index_
)
135 // Reduce the timeout value as receives succeed, but aren't the right index.
136 timeout
= finish_time
- base::TimeTicks::Now();
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.";
144 base::TimeDelta time_since_start
= base::TimeTicks::Now() - start_time
;
145 UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady",
147 base::TimeDelta::FromMilliseconds(1),
148 base::TimeDelta::FromMilliseconds(1000),
156 } // namespace content