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