audio_sync_reader.cc revision 3240926e260ce088908e02ac07a6cf7b0c0cbf44
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#include "media/audio/shared_memory_util.h"
16
17using media::AudioBus;
18
19namespace content {
20
21AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory,
22                                 const media::AudioParameters& params,
23                                 int input_channels)
24    : shared_memory_(shared_memory),
25      input_channels_(input_channels),
26      mute_audio_(CommandLine::ForCurrentProcess()->HasSwitch(
27          switches::kMuteAudio)),
28      renderer_callback_count_(0),
29      renderer_missed_callback_count_(0) {
30  packet_size_ = media::PacketSizeInBytes(shared_memory_->requested_size());
31  int input_memory_size = 0;
32  int output_memory_size = AudioBus::CalculateMemorySize(params);
33  if (input_channels_ > 0) {
34    // The input storage is after the output storage.
35    int frames = params.frames_per_buffer();
36    input_memory_size = AudioBus::CalculateMemorySize(input_channels_, frames);
37    char* input_data =
38        static_cast<char*>(shared_memory_->memory()) + output_memory_size;
39    input_bus_ = AudioBus::WrapMemory(input_channels_, frames, input_data);
40  }
41  DCHECK_EQ(packet_size_, output_memory_size + input_memory_size);
42  output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory());
43}
44
45AudioSyncReader::~AudioSyncReader() {
46  if (!renderer_callback_count_)
47    return;
48
49  // Recording the percentage of deadline misses gives us a rough overview of
50  // how many users might be running into audio glitches.
51  int percentage_missed =
52      100.0 * renderer_missed_callback_count_ / renderer_callback_count_;
53  UMA_HISTOGRAM_PERCENTAGE(
54      "Media.AudioRendererMissedDeadline", percentage_missed);
55}
56
57bool AudioSyncReader::DataReady() {
58  return !media::IsUnknownDataSize(shared_memory_, packet_size_);
59}
60
61// media::AudioOutputController::SyncReader implementations.
62void AudioSyncReader::UpdatePendingBytes(uint32 bytes) {
63  if (bytes != static_cast<uint32>(media::kPauseMark)) {
64    // Store unknown length of data into buffer, so we later
65    // can find out if data became available.
66    media::SetUnknownDataSize(shared_memory_, packet_size_);
67  }
68
69  if (socket_) {
70    socket_->Send(&bytes, sizeof(bytes));
71  }
72}
73
74int AudioSyncReader::Read(bool block, const AudioBus* source, AudioBus* dest) {
75  ++renderer_callback_count_;
76  if (!DataReady()) {
77    ++renderer_missed_callback_count_;
78
79    if (block)
80      WaitTillDataReady();
81  }
82
83  // Copy optional synchronized live audio input for consumption by renderer
84  // process.
85  if (source && input_bus_) {
86    DCHECK_EQ(source->channels(), input_bus_->channels());
87    // TODO(crogers): In some cases with device and sample-rate changes
88    // it's possible for an AOR to insert a resampler in the path.
89    // Because this is used with the Web Audio API, it'd be better
90    // to bypass the device change handling in AOR and instead let
91    // the renderer-side Web Audio code deal with this.
92    if (source->frames() == input_bus_->frames() &&
93        source->channels() == input_bus_->channels())
94      source->CopyTo(input_bus_.get());
95    else
96      input_bus_->Zero();
97  }
98
99  // Retrieve the actual number of bytes available from the shared memory.  If
100  // the renderer has not completed rendering this value will be invalid (still
101  // the marker stored in UpdatePendingBytes() above) and must be sanitized.
102  // TODO(dalecurtis): Technically this is not the exact size.  Due to channel
103  // padding for alignment, there may be more data available than this; AudioBus
104  // will automatically do the right thing during CopyTo().  Rename this method
105  // to GetActualFrameCount().
106  uint32 size = media::GetActualDataSizeInBytes(shared_memory_, packet_size_);
107
108  // Compute the actual number of frames read.  It's important to sanitize this
109  // value for a couple reasons.  One, it might still be the unknown data size
110  // marker.  Two, shared memory comes from a potentially untrusted source.
111  int frames =
112      size / (sizeof(*output_bus_->channel(0)) * output_bus_->channels());
113  if (frames < 0)
114    frames = 0;
115  else if (frames > output_bus_->frames())
116    frames = output_bus_->frames();
117
118  if (mute_audio_) {
119    dest->Zero();
120  } else {
121    // Copy data from the shared memory into the caller's AudioBus.
122    output_bus_->CopyTo(dest);
123
124    // Zero out any unfilled frames in the destination bus.
125    dest->ZeroFramesPartial(frames, dest->frames() - frames);
126  }
127
128  // Zero out the entire output buffer to avoid stuttering/repeating-buffers
129  // in the anomalous case if the renderer is unable to keep up with real-time.
130  output_bus_->Zero();
131
132  // Store unknown length of data into buffer, in case renderer does not store
133  // the length itself. It also helps in decision if we need to yield.
134  media::SetUnknownDataSize(shared_memory_, packet_size_);
135
136  // Return the actual number of frames read.
137  return frames;
138}
139
140void AudioSyncReader::Close() {
141  if (socket_) {
142    socket_->Close();
143  }
144}
145
146bool AudioSyncReader::Init() {
147  socket_.reset(new base::CancelableSyncSocket());
148  foreign_socket_.reset(new base::CancelableSyncSocket());
149  return base::CancelableSyncSocket::CreatePair(socket_.get(),
150                                                foreign_socket_.get());
151}
152
153#if defined(OS_WIN)
154bool AudioSyncReader::PrepareForeignSocketHandle(
155    base::ProcessHandle process_handle,
156    base::SyncSocket::Handle* foreign_handle) {
157  ::DuplicateHandle(GetCurrentProcess(), foreign_socket_->handle(),
158                    process_handle, foreign_handle,
159                    0, FALSE, DUPLICATE_SAME_ACCESS);
160  if (*foreign_handle != 0)
161    return true;
162  return false;
163}
164#else
165bool AudioSyncReader::PrepareForeignSocketHandle(
166    base::ProcessHandle process_handle,
167    base::FileDescriptor* foreign_handle) {
168  foreign_handle->fd = foreign_socket_->handle();
169  foreign_handle->auto_close = false;
170  if (foreign_handle->fd != -1)
171    return true;
172  return false;
173}
174#endif
175
176void AudioSyncReader::WaitTillDataReady() {
177  base::TimeTicks start = base::TimeTicks::Now();
178  const base::TimeDelta kMaxWait = base::TimeDelta::FromMilliseconds(20);
179#if defined(OS_WIN)
180  // Sleep(0) on Windows lets the other threads run.
181  const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(0);
182#else
183  // We want to sleep for a bit here, as otherwise a backgrounded renderer won't
184  // get enough cpu to send the data and the high priority thread in the browser
185  // will use up a core causing even more skips.
186  const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(2);
187#endif
188  base::TimeDelta time_since_start;
189  do {
190    base::PlatformThread::Sleep(kSleep);
191    time_since_start = base::TimeTicks::Now() - start;
192  } while (!DataReady() && time_since_start < kMaxWait);
193  UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady",
194                             time_since_start,
195                             base::TimeDelta::FromMilliseconds(1),
196                             base::TimeDelta::FromMilliseconds(1000),
197                             50);
198}
199
200}  // namespace content
201