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/renderer/media/audio_renderer_mixer_manager.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "content/renderer/media/audio_device_factory.h"
10#include "media/audio/audio_output_device.h"
11#include "media/base/audio_hardware_config.h"
12#include "media/base/audio_renderer_mixer.h"
13#include "media/base/audio_renderer_mixer_input.h"
14
15namespace content {
16
17AudioRendererMixerManager::AudioRendererMixerManager(
18    media::AudioHardwareConfig* hardware_config)
19    : hardware_config_(hardware_config),
20      sink_for_testing_(NULL) {
21}
22
23AudioRendererMixerManager::~AudioRendererMixerManager() {
24  // References to AudioRendererMixers may be owned by garbage collected
25  // objects.  During process shutdown they may be leaked, so, transitively,
26  // |mixers_| may leak (i.e., may be non-empty at this time) as well.
27}
28
29media::AudioRendererMixerInput* AudioRendererMixerManager::CreateInput(
30    int source_render_view_id, int source_render_frame_id) {
31  return new media::AudioRendererMixerInput(
32      base::Bind(
33          &AudioRendererMixerManager::GetMixer, base::Unretained(this),
34          source_render_view_id,
35          source_render_frame_id),
36      base::Bind(
37          &AudioRendererMixerManager::RemoveMixer, base::Unretained(this),
38          source_render_view_id));
39}
40
41void AudioRendererMixerManager::SetAudioRendererSinkForTesting(
42    media::AudioRendererSink* sink) {
43  sink_for_testing_ = sink;
44}
45
46media::AudioRendererMixer* AudioRendererMixerManager::GetMixer(
47    int source_render_view_id,
48    int source_render_frame_id,
49    const media::AudioParameters& params) {
50  // Effects are not passed through to output creation, so ensure none are set.
51  DCHECK_EQ(params.effects(), media::AudioParameters::NO_EFFECTS);
52
53  const MixerKey key(source_render_view_id, params);
54  base::AutoLock auto_lock(mixers_lock_);
55
56  AudioRendererMixerMap::iterator it = mixers_.find(key);
57  if (it != mixers_.end()) {
58    it->second.ref_count++;
59    return it->second.mixer;
60  }
61
62  // On ChromeOS we can rely on the playback device to handle resampling, so
63  // don't waste cycles on it here.
64#if defined(OS_CHROMEOS)
65  int sample_rate = params.sample_rate();
66#else
67  int sample_rate = hardware_config_->GetOutputSampleRate();
68#endif
69
70  // Create output parameters based on the audio hardware configuration for
71  // passing on to the output sink.  Force to 16-bit output for now since we
72  // know that works everywhere; ChromeOS does not support other bit depths.
73  media::AudioParameters output_params(
74      media::AudioParameters::AUDIO_PCM_LOW_LATENCY, params.channel_layout(),
75      sample_rate, 16, hardware_config_->GetHighLatencyBufferSize());
76
77  // If we've created invalid output parameters, simply pass on the input params
78  // and let the browser side handle automatic fallback.
79  if (!output_params.IsValid())
80    output_params = params;
81
82  media::AudioRendererMixer* mixer;
83  if (sink_for_testing_) {
84    mixer = new media::AudioRendererMixer(
85        params, output_params, sink_for_testing_);
86  } else {
87    mixer = new media::AudioRendererMixer(
88        params, output_params, AudioDeviceFactory::NewOutputDevice(
89            source_render_view_id, source_render_frame_id));
90  }
91
92  AudioRendererMixerReference mixer_reference = { mixer, 1 };
93  mixers_[key] = mixer_reference;
94  return mixer;
95}
96
97void AudioRendererMixerManager::RemoveMixer(
98    int source_render_view_id,
99    const media::AudioParameters& params) {
100  const MixerKey key(source_render_view_id, params);
101  base::AutoLock auto_lock(mixers_lock_);
102
103  AudioRendererMixerMap::iterator it = mixers_.find(key);
104  DCHECK(it != mixers_.end());
105
106  // Only remove the mixer if AudioRendererMixerManager is the last owner.
107  it->second.ref_count--;
108  if (it->second.ref_count == 0) {
109    delete it->second.mixer;
110    mixers_.erase(it);
111  }
112}
113
114}  // namespace content
115