OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 <string> |
| 6 |
| 7 #include "services/media/audio/audio_output.h" |
| 8 #include "services/media/audio/audio_output_manager.h" |
| 9 #include "services/media/audio/audio_server_impl.h" |
| 10 #include "services/media/audio/audio_track_to_output_link.h" |
| 11 #include "services/media/audio/platform/generic/throttle_output.h" |
| 12 |
| 13 namespace mojo { |
| 14 namespace media { |
| 15 namespace audio { |
| 16 |
| 17 static constexpr size_t THREAD_POOL_SZ = 2; |
| 18 static const std::string THREAD_PREFIX("AudioMixer"); |
| 19 |
| 20 AudioOutputManager::AudioOutputManager(AudioServerImpl* server) |
| 21 : server_(server) { |
| 22 } |
| 23 |
| 24 AudioOutputManager::~AudioOutputManager() { |
| 25 Shutdown(); |
| 26 DCHECK_EQ(outputs_.size(), 0u); |
| 27 DCHECK(!thread_pool_); |
| 28 } |
| 29 |
| 30 MediaResult AudioOutputManager::Init() { |
| 31 // Step #1: Initialize the mixing thread pool. |
| 32 // |
| 33 // TODO(johngro): make the thread pool size proportional to the maximum |
| 34 // number of cores available in the system. |
| 35 // |
| 36 // TODO(johngro): make sure that the threads are executed at an elevated |
| 37 // priority, not the default priority. |
| 38 thread_pool_ = new base::SequencedWorkerPool(THREAD_POOL_SZ, THREAD_PREFIX); |
| 39 |
| 40 // Step #2: Instantiate all of the built-in audio output devices. |
| 41 // |
| 42 // TODO(johngro): Come up with a better way of doing this based on our |
| 43 // platform. Right now, we just create some hardcoded default outputs and |
| 44 // leave it at that. |
| 45 outputs_.emplace(audio::ThrottleOutput::New(this)); |
| 46 |
| 47 // Step #3: Being monitoring for plug/unplug events for pluggable audio |
| 48 // output devices. |
| 49 // |
| 50 // TODO(johngro): Implement step #3. Right now, the details are behind |
| 51 // hot-plug monitoring are TBD, so the feature is not implemented. |
| 52 |
| 53 // Step #4: Attempt to initialize each of the audio outputs we have created, |
| 54 // then kick off the callback engine for each of them. |
| 55 for (auto iter = outputs_.begin(); iter != outputs_.end(); ) { |
| 56 const AudioOutputPtr& output = *iter; |
| 57 auto tmp = iter++; |
| 58 DCHECK(output); |
| 59 |
| 60 // Create a sequenced task runner for this output. It will be used by the |
| 61 // output to schedule jobs (such as mixing) on the thread pool. |
| 62 scoped_refptr<base::SequencedTaskRunner> task_runner = |
| 63 thread_pool_->GetSequencedTaskRunnerWithShutdownBehavior( |
| 64 thread_pool_->GetSequenceToken(), |
| 65 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
| 66 |
| 67 MediaResult res = output->Init(output, task_runner); |
| 68 if (res != MediaResult::OK) { |
| 69 // TODO(johngro): Probably should log something about this, assuming that |
| 70 // the output has not already. |
| 71 outputs_.erase(tmp); |
| 72 } |
| 73 } |
| 74 |
| 75 return MediaResult::OK; |
| 76 } |
| 77 |
| 78 void AudioOutputManager::Shutdown() { |
| 79 // Are we already shutdown (or were we never successfully initialized?) |
| 80 if (thread_pool_ == nullptr) { |
| 81 DCHECK_EQ(outputs_.size(), 0u); |
| 82 return; |
| 83 } |
| 84 |
| 85 // Step #1: Stop monitoringing plug/unplug events. We are shutting down and |
| 86 // no longer care about outputs coming and going. |
| 87 // |
| 88 // TODO(johngro): Implement step #1. Right now, the details are behind |
| 89 // hot-plug monitoring are TBD, so the feature is not implemented. |
| 90 |
| 91 // Step #2: Shut down each currently active output in the system. It is |
| 92 // possible for this to take a bit of time as outputs release their hardware, |
| 93 // but it should not take long. |
| 94 for (const auto& output_ptr : outputs_) { |
| 95 output_ptr->Shutdown(); |
| 96 } |
| 97 outputs_.clear(); |
| 98 |
| 99 // Step #3: Shutdown and release our thread pool. Since we have shut down all |
| 100 // of our outputs, any pending tasks left in the task runner are now no-ops, |
| 101 // so it does not matter that the task runner is going to cancel them all |
| 102 // (instead of blocking) when we shut it down. |
| 103 thread_pool_->Shutdown(); |
| 104 thread_pool_ = nullptr; |
| 105 } |
| 106 |
| 107 void AudioOutputManager::ShutdownOutput(AudioOutputPtr output) { |
| 108 // No one should be calling this method if we have been shut down (or never |
| 109 // successfully started). |
| 110 DCHECK(thread_pool_); |
| 111 |
| 112 auto iter = outputs_.find(output); |
| 113 if (iter != outputs_.end()) { |
| 114 output->Shutdown(); |
| 115 outputs_.erase(iter); |
| 116 } |
| 117 } |
| 118 |
| 119 void AudioOutputManager::SelectOutputsForTrack(AudioTrackImplPtr track) { |
| 120 // TODO(johngro): Someday, base this on policy. For now, every track gets |
| 121 // assigned to every output in the system. |
| 122 DCHECK(track); |
| 123 |
| 124 // TODO(johngro): Add some way to assert that we are executing on the main |
| 125 // message loop thread. |
| 126 |
| 127 for (auto output : outputs_) { |
| 128 auto link = AudioTrackToOutputLink::New(track, output); |
| 129 DCHECK(output); |
| 130 DCHECK(link); |
| 131 |
| 132 // If we cannot add this link to the output, it's because the output is in |
| 133 // the process of shutting down (we didn't want to hang out with that guy |
| 134 // anyway) |
| 135 if (output->AddTrackLink(link) == MediaResult::OK) { |
| 136 track->AddOutput(link); |
| 137 } |
| 138 } |
| 139 } |
| 140 |
| 141 void AudioOutputManager::ScheduleMessageLoopTask( |
| 142 const tracked_objects::Location& from_here, |
| 143 const base::Closure& task) { |
| 144 DCHECK(server_); |
| 145 server_->ScheduleMessageLoopTask(from_here, task); |
| 146 } |
| 147 |
| 148 } // namespace audio |
| 149 } // namespace media |
| 150 } // namespace mojo |
OLD | NEW |