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