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