Home | History | Annotate | Download | only in media
      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 
     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 
     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 
     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.
     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 
     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 
    107 void AudioSyncReader::Close() {
    108   socket_->Close();
    109 }
    110 
    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 
    118 bool AudioSyncReader::PrepareForeignSocket(
    119     base::ProcessHandle process_handle,
    120     base::SyncSocket::TransitDescriptor* descriptor) {
    121   return foreign_socket_->PrepareTransitDescriptor(process_handle, descriptor);
    122 }
    123 
    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