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