• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 using media::AudioBus;
19 
20 namespace {
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.
24 enum 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 
LogAudioGlitchResult(AudioGlitchResult result)30 void LogAudioGlitchResult(AudioGlitchResult result) {
31   UMA_HISTOGRAM_ENUMERATION("Media.AudioRendererAudioGlitches",
32                             result,
33                             AUDIO_RENDERER_AUDIO_GLITCHES_MAX + 1);
34 }
35 
36 }  // namespace
37 
38 namespace content {
39 
AudioSyncReader(base::SharedMemory * shared_memory,const media::AudioParameters & params)40 AudioSyncReader::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 
~AudioSyncReader()60 AudioSyncReader::~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.
UpdatePendingBytes(uint32 bytes)85 void 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 
Read(AudioBus * dest)93 void 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 
Close()107 void AudioSyncReader::Close() {
108   socket_->Close();
109 }
110 
Init()111 bool 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 
PrepareForeignSocket(base::ProcessHandle process_handle,base::SyncSocket::TransitDescriptor * descriptor)118 bool AudioSyncReader::PrepareForeignSocket(
119     base::ProcessHandle process_handle,
120     base::SyncSocket::TransitDescriptor* descriptor) {
121   return foreign_socket_->PrepareTransitDescriptor(process_handle, descriptor);
122 }
123 
WaitUntilDataIsReady()124 bool 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