1// Copyright 2013 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/clockless_audio_sink.h"
6
7#include "base/threading/simple_thread.h"
8#include "base/time/time.h"
9#include "media/base/audio_renderer_sink.h"
10
11namespace media {
12
13// Internal to ClocklessAudioSink. Class is used to call Render() on a seperate
14// thread, running as fast as it can read the data.
15class ClocklessAudioSinkThread : public base::DelegateSimpleThread::Delegate {
16 public:
17  explicit ClocklessAudioSinkThread(const AudioParameters& params,
18                                    AudioRendererSink::RenderCallback* callback)
19      : callback_(callback),
20        audio_bus_(AudioBus::Create(params)),
21        stop_event_(new base::WaitableEvent(false, false)) {}
22
23  void Start() {
24    stop_event_->Reset();
25    thread_.reset(new base::DelegateSimpleThread(this, "ClocklessAudioSink"));
26    thread_->Start();
27  }
28
29  // Generate a signal to stop calling Render().
30  base::TimeDelta Stop() {
31    stop_event_->Signal();
32    thread_->Join();
33    return playback_time_;
34  }
35
36 private:
37   // Call Render() repeatedly, keeping track of the rendering time.
38   virtual void Run() OVERRIDE {
39     base::TimeTicks start;
40     while (!stop_event_->IsSignaled()) {
41       int frames_received = callback_->Render(audio_bus_.get(), 0);
42       if (frames_received <= 0) {
43         // No data received, so let other threads run to provide data.
44         base::PlatformThread::YieldCurrentThread();
45       } else if (start.is_null()) {
46         // First time we processed some audio, so record the starting time.
47         start = base::TimeTicks::HighResNow();
48       } else {
49         // Keep track of the last time data was rendered.
50         playback_time_ = base::TimeTicks::HighResNow() - start;
51       }
52     }
53   }
54
55  AudioRendererSink::RenderCallback* callback_;
56  scoped_ptr<AudioBus> audio_bus_;
57  scoped_ptr<base::WaitableEvent> stop_event_;
58  scoped_ptr<base::DelegateSimpleThread> thread_;
59  base::TimeDelta playback_time_;
60};
61
62ClocklessAudioSink::ClocklessAudioSink()
63    : initialized_(false),
64      playing_(false) {}
65
66ClocklessAudioSink::~ClocklessAudioSink() {}
67
68void ClocklessAudioSink::Initialize(const AudioParameters& params,
69                                    RenderCallback* callback) {
70  DCHECK(!initialized_);
71  thread_.reset(new ClocklessAudioSinkThread(params, callback));
72  initialized_ = true;
73}
74
75void ClocklessAudioSink::Start() {
76  DCHECK(initialized_);
77  DCHECK(!playing_);
78}
79
80void ClocklessAudioSink::Stop() {
81  if (initialized_)
82    Pause();
83}
84
85void ClocklessAudioSink::Play() {
86  DCHECK(initialized_);
87
88  if (playing_)
89    return;
90
91  playing_ = true;
92  thread_->Start();
93}
94
95void ClocklessAudioSink::Pause() {
96  DCHECK(initialized_);
97
98  if (!playing_)
99    return;
100
101  playing_ = false;
102  playback_time_ = thread_->Stop();
103}
104
105bool ClocklessAudioSink::SetVolume(double volume) {
106  // Audio is always muted.
107  return volume == 0.0;
108}
109
110}  // namespace media
111