text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "content/browser/renderer_host/media/audio_sync_reader.h"
  
  #include <algorithm>
  
  #include "base/command_line.h"
  #include "base/memory/shared_memory.h"
  #include "base/metrics/histogram.h"
  #include "content/public/common/content_switches.h"
  #include "media/audio/audio_buffers_state.h"
  #include "media/audio/audio_parameters.h"
  
  using media::AudioBus;
  
  namespace content {
  
  AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory,
                                   const media::AudioParameters& params)
      : shared_memory_(shared_memory),
        mute_audio_(CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kMuteAudio)),
        packet_size_(shared_memory_->requested_size()),
        renderer_callback_count_(0),
        renderer_missed_callback_count_(0),
  #if defined(OS_MACOSX)
        maximum_wait_time_(params.GetBufferDuration() / 2),
  #else
        // TODO(dalecurtis): Investigate if we can reduce this on all platforms.
        maximum_wait_time_(base::TimeDelta::FromMilliseconds(20)),
  #endif
        buffer_index_(0) {
    DCHECK_EQ(packet_size_, AudioBus::CalculateMemorySize(params));
    output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory());
    output_bus_->Zero();
  }
  
  AudioSyncReader::~AudioSyncReader() {
    if (!renderer_callback_count_)
      return;
  
    // Recording the percentage of deadline misses gives us a rough overview of
    // how many users might be running into audio glitches.
    int percentage_missed =
        100.0 * renderer_missed_callback_count_ / renderer_callback_count_;
    UMA_HISTOGRAM_PERCENTAGE(
        "Media.AudioRendererMissedDeadline", percentage_missed);
  }
  
  // media::AudioOutputController::SyncReader implementations.
  void AudioSyncReader::UpdatePendingBytes(uint32 bytes) {
    // Zero out the entire output buffer to avoid stuttering/repeating-buffers
    // in the anomalous case if the renderer is unable to keep up with real-time.
    output_bus_->Zero();
    socket_->Send(&bytes, sizeof(bytes));
    ++buffer_index_;
  }
  
  void AudioSyncReader::Read(const AudioBus* source, AudioBus* dest) {
    ++renderer_callback_count_;
    if (!WaitUntilDataIsReady()) {
      ++renderer_missed_callback_count_;
      dest->Zero();
      return;
    }
  
    if (mute_audio_)
      dest->Zero();
    else
      output_bus_->CopyTo(dest);
  }
  
  void AudioSyncReader::Close() {
    socket_->Close();
  }
  
  bool AudioSyncReader::Init() {
    socket_.reset(new base::CancelableSyncSocket());
    foreign_socket_.reset(new base::CancelableSyncSocket());
    return base::CancelableSyncSocket::CreatePair(socket_.get(),
                                                  foreign_socket_.get());
  }
  
  #if defined(OS_WIN)
  bool AudioSyncReader::PrepareForeignSocketHandle(
      base::ProcessHandle process_handle,
      base::SyncSocket::Handle* foreign_handle) {
    ::DuplicateHandle(GetCurrentProcess(), foreign_socket_->handle(),
                      process_handle, foreign_handle,
                      0, FALSE, DUPLICATE_SAME_ACCESS);
    return (*foreign_handle != 0);
  }
  #else
  bool AudioSyncReader::PrepareForeignSocketHandle(
      base::ProcessHandle process_handle,
      base::FileDescriptor* foreign_handle) {
    foreign_handle->fd = foreign_socket_->handle();
    foreign_handle->auto_close = false;
    return (foreign_handle->fd != -1);
  }
  #endif
  
  bool AudioSyncReader::WaitUntilDataIsReady() {
    base::TimeDelta timeout = maximum_wait_time_;
    const base::TimeTicks start_time = base::TimeTicks::Now();
    const base::TimeTicks finish_time = start_time + timeout;
  
    // Check if data is ready and if not, wait a reasonable amount of time for it.
    //
    // Data readiness is achieved via parallel counters, one on the renderer side
    // and one here.  Every time a buffer is requested via UpdatePendingBytes(),
    // |buffer_index_| is incremented.  Subsequently every time the renderer has a
    // buffer ready it increments its counter and sends the counter value over the
    // SyncSocket.  Data is ready when |buffer_index_| matches the counter value
    // received from the renderer.
    //
    // The counter values may temporarily become out of sync if the renderer is
    // unable to deliver audio fast enough.  It's assumed that the renderer will
    // catch up at some point, which means discarding counter values read from the
    // SyncSocket which don't match our current buffer index.
    size_t bytes_received = 0;
    uint32 renderer_buffer_index = 0;
    while (timeout.InMicroseconds() > 0) {
      bytes_received = socket_->ReceiveWithTimeout(
          &renderer_buffer_index, sizeof(renderer_buffer_index), timeout);
      if (!bytes_received)
        break;
  
      DCHECK_EQ(bytes_received, sizeof(renderer_buffer_index));
      if (renderer_buffer_index == buffer_index_)
        break;
  
      // Reduce the timeout value as receives succeed, but aren't the right index.
      timeout = finish_time - base::TimeTicks::Now();
    }
  
    // Receive timed out or another error occurred.  Receive can timeout if the
    // renderer is unable to deliver audio data within the allotted time.
    if (!bytes_received || renderer_buffer_index != buffer_index_) {
      DVLOG(2) << "AudioSyncReader::WaitUntilDataIsReady() timed out.";
  
      base::TimeDelta time_since_start = base::TimeTicks::Now() - start_time;
      UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady",
                                 time_since_start,
                                 base::TimeDelta::FromMilliseconds(1),
                                 base::TimeDelta::FromMilliseconds(1000),
                                 50);
      return false;
    }
  
    return true;
  }
  
  }  // namespace content
  