null_audio_sink.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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 "media/audio/null_audio_sink.h" 6 7#include "base/bind.h" 8#include "base/stringprintf.h" 9#include "base/sys_byteorder.h" 10#include "base/threading/platform_thread.h" 11 12namespace media { 13 14NullAudioSink::NullAudioSink() 15 : initialized_(false), 16 playing_(false), 17 callback_(NULL), 18 thread_("NullAudioThread"), 19 hash_audio_for_testing_(false) { 20} 21 22void NullAudioSink::Initialize(const AudioParameters& params, 23 RenderCallback* callback) { 24 DCHECK(!initialized_); 25 params_ = params; 26 27 audio_bus_ = AudioBus::Create(params_); 28 29 if (hash_audio_for_testing_) { 30 md5_channel_contexts_.reset(new base::MD5Context[params_.channels()]); 31 for (int i = 0; i < params_.channels(); i++) 32 base::MD5Init(&md5_channel_contexts_[i]); 33 } 34 35 callback_ = callback; 36 initialized_ = true; 37} 38 39void NullAudioSink::Start() { 40 if (!thread_.Start()) 41 return; 42 43 thread_.message_loop()->PostTask(FROM_HERE, base::Bind( 44 &NullAudioSink::FillBufferTask, this)); 45} 46 47void NullAudioSink::Stop() { 48 SetPlaying(false); 49 thread_.Stop(); 50} 51 52void NullAudioSink::Play() { 53 SetPlaying(true); 54} 55 56void NullAudioSink::Pause(bool /* flush */) { 57 SetPlaying(false); 58} 59 60bool NullAudioSink::SetVolume(double volume) { 61 // Audio is always muted. 62 return volume == 0.0; 63} 64 65void NullAudioSink::SetPlaying(bool is_playing) { 66 base::AutoLock auto_lock(lock_); 67 playing_ = is_playing; 68} 69 70NullAudioSink::~NullAudioSink() { 71 DCHECK(!thread_.IsRunning()); 72} 73 74void NullAudioSink::FillBufferTask() { 75 base::AutoLock auto_lock(lock_); 76 77 base::TimeDelta delay; 78 // Only consume buffers when actually playing. 79 if (playing_) { 80 int frames_received = callback_->Render(audio_bus_.get(), 0); 81 int frames_per_millisecond = 82 params_.sample_rate() / base::Time::kMillisecondsPerSecond; 83 84 if (hash_audio_for_testing_ && frames_received > 0) { 85 DCHECK_EQ(sizeof(float), sizeof(uint32)); 86 int channels = audio_bus_->channels(); 87 for (int channel_idx = 0; channel_idx < channels; ++channel_idx) { 88 float* channel = audio_bus_->channel(channel_idx); 89 for (int frame_idx = 0; frame_idx < frames_received; frame_idx++) { 90 // Convert float to uint32 w/o conversion loss. 91 uint32 frame = base::ByteSwapToLE32( 92 bit_cast<uint32>(channel[frame_idx])); 93 base::MD5Update( 94 &md5_channel_contexts_[channel_idx], base::StringPiece( 95 reinterpret_cast<char*>(&frame), sizeof(frame))); 96 } 97 } 98 } 99 100 // Calculate our sleep duration. 101 delay = base::TimeDelta::FromMilliseconds( 102 frames_received / frames_per_millisecond); 103 } else { 104 // If paused, sleep for 10 milliseconds before polling again. 105 delay = base::TimeDelta::FromMilliseconds(10); 106 } 107 108 // Sleep for at least one millisecond so we don't spin the CPU. 109 MessageLoop::current()->PostDelayedTask( 110 FROM_HERE, 111 base::Bind(&NullAudioSink::FillBufferTask, this), 112 std::max(delay, base::TimeDelta::FromMilliseconds(1))); 113} 114 115void NullAudioSink::StartAudioHashForTesting() { 116 DCHECK(!initialized_); 117 hash_audio_for_testing_ = true; 118} 119 120std::string NullAudioSink::GetAudioHashForTesting() { 121 DCHECK(hash_audio_for_testing_); 122 123 // If initialize failed or was never called, ensure we return an empty hash. 124 int channels = 1; 125 if (!initialized_) { 126 md5_channel_contexts_.reset(new base::MD5Context[1]); 127 base::MD5Init(&md5_channel_contexts_[0]); 128 } else { 129 channels = audio_bus_->channels(); 130 } 131 132 // Hash all channels into the first channel. 133 base::MD5Digest digest; 134 for (int i = 1; i < channels; i++) { 135 base::MD5Final(&digest, &md5_channel_contexts_[i]); 136 base::MD5Update(&md5_channel_contexts_[0], base::StringPiece( 137 reinterpret_cast<char*>(&digest), sizeof(base::MD5Digest))); 138 } 139 140 base::MD5Final(&digest, &md5_channel_contexts_[0]); 141 return base::MD5DigestToBase16(digest); 142} 143 144} // namespace media 145