summaryrefslogtreecommitdiffstats
path: root/media/filters/audio_renderer_base.cc
blob: 9d7783f6d912b5653ebfe6b43a2c3f0eb3c9a19c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/filters/audio_renderer_base.h"

#include <algorithm>
#include <string>

#include "base/callback.h"
#include "base/logging.h"
#include "media/base/filter_host.h"
#include "media/filters/audio_renderer_algorithm_ola.h"

namespace media {

AudioRendererBase::AudioRendererBase()
    : state_(kUninitialized),
      recieved_end_of_stream_(false),
      rendered_end_of_stream_(false),
      pending_reads_(0) {
}

AudioRendererBase::~AudioRendererBase() {
  // Stop() should have been called and |algorithm_| should have been destroyed.
  DCHECK(state_ == kUninitialized || state_ == kStopped);
  DCHECK(!algorithm_.get());
}

void AudioRendererBase::Play(FilterCallback* callback) {
  base::AutoLock auto_lock(lock_);
  DCHECK_EQ(kPaused, state_);
  scoped_ptr<FilterCallback> c(callback);
  state_ = kPlaying;
  callback->Run();
}

void AudioRendererBase::Pause(FilterCallback* callback) {
  base::AutoLock auto_lock(lock_);
  DCHECK_EQ(kPlaying, state_);
  pause_callback_.reset(callback);
  state_ = kPaused;

  // We'll only pause when we've finished all pending reads.
  if (pending_reads_ == 0) {
    pause_callback_->Run();
    pause_callback_.reset();
  } else {
    state_ = kPaused;
  }
}

void AudioRendererBase::Stop(FilterCallback* callback) {
  OnStop();
  {
    base::AutoLock auto_lock(lock_);
    state_ = kStopped;
    algorithm_.reset(NULL);
  }
  if (callback) {
    callback->Run();
    delete callback;
  }
}

void AudioRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) {
  base::AutoLock auto_lock(lock_);
  DCHECK_EQ(kPaused, state_);
  DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed";
  state_ = kSeeking;
  seek_callback_.reset(callback);
  seek_timestamp_ = time;

  // Throw away everything and schedule our reads.
  last_fill_buffer_time_ = base::TimeDelta();
  recieved_end_of_stream_ = false;
  rendered_end_of_stream_ = false;

  // |algorithm_| will request more reads.
  algorithm_->FlushBuffers();
}

void AudioRendererBase::Initialize(AudioDecoder* decoder,
                                   FilterCallback* callback) {
  DCHECK(decoder);
  DCHECK(callback);
  DCHECK_EQ(kUninitialized, state_);
  scoped_ptr<FilterCallback> c(callback);
  decoder_ = decoder;

  decoder_->set_consume_audio_samples_callback(
      NewCallback(this, &AudioRendererBase::ConsumeAudioSamples));
  // Get the media properties to initialize our algorithms.
  int channels = 0;
  int sample_rate = 0;
  int sample_bits = 0;
  if (!ParseMediaFormat(decoder_->media_format(), &channels, &sample_rate,
                        &sample_bits)) {
    host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED);
    callback->Run();
    return;
  }

  // Create a callback so our algorithm can request more reads.
  AudioRendererAlgorithmBase::RequestReadCallback* cb =
      NewCallback(this, &AudioRendererBase::ScheduleRead_Locked);

  // Construct the algorithm.
  algorithm_.reset(new AudioRendererAlgorithmOLA());

  // Initialize our algorithm with media properties, initial playback rate,
  // and a callback to request more reads from the data source.
  algorithm_->Initialize(channels,
                         sample_rate,
                         sample_bits,
                         0.0f,
                         cb);

  // Give the subclass an opportunity to initialize itself.
  if (!OnInitialize(decoder_->media_format())) {
    host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED);
    callback->Run();
    return;
  }

  // Finally, execute the start callback.
  state_ = kPaused;
  callback->Run();
}

bool AudioRendererBase::HasEnded() {
  base::AutoLock auto_lock(lock_);
  if (rendered_end_of_stream_) {
    DCHECK(algorithm_->IsQueueEmpty())
        << "Audio queue should be empty if we have rendered end of stream";
  }
  return recieved_end_of_stream_ && rendered_end_of_stream_;
}

void AudioRendererBase::ConsumeAudioSamples(scoped_refptr<Buffer> buffer_in) {
  base::AutoLock auto_lock(lock_);
  DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying);
  DCHECK_GT(pending_reads_, 0u);
  --pending_reads_;

  // TODO(scherkus): this happens due to a race, primarily because Stop() is a
  // synchronous call when it should be asynchronous and accept a callback.
  // Refer to http://crbug.com/16059
  if (state_ == kStopped) {
    return;
  }

  // Don't enqueue an end-of-stream buffer because it has no data, otherwise
  // discard decoded audio data until we reach our desired seek timestamp.
  if (buffer_in->IsEndOfStream()) {
    recieved_end_of_stream_ = true;
  } else if (state_ == kSeeking && !buffer_in->IsEndOfStream() &&
             (buffer_in->GetTimestamp() + buffer_in->GetDuration()) <
                 seek_timestamp_) {
    ScheduleRead_Locked();
  } else {
    // Note: Calling this may schedule more reads.
    algorithm_->EnqueueBuffer(buffer_in);
  }

  // Check for our preroll complete condition.
  if (state_ == kSeeking) {
    DCHECK(seek_callback_.get());
    if (algorithm_->IsQueueFull() || recieved_end_of_stream_) {
      // Transition into paused whether we have data in |algorithm_| or not.
      // FillBuffer() will play silence if there's nothing to fill.
      state_ = kPaused;
      seek_callback_->Run();
      seek_callback_.reset();
    }
  } else if (state_ == kPaused && pending_reads_ == 0) {
    // No more pending reads!  We're now officially "paused".
    if (pause_callback_.get()) {
      pause_callback_->Run();
      pause_callback_.reset();
    }
  }
}

uint32 AudioRendererBase::FillBuffer(uint8* dest,
                                     uint32 dest_len,
                                     const base::TimeDelta& playback_delay,
                                     bool buffers_empty) {
  // The timestamp of the last buffer written during the last call to
  // FillBuffer().
  base::TimeDelta last_fill_buffer_time;
  size_t dest_written = 0;
  {
    base::AutoLock auto_lock(lock_);

    // Mute audio by returning 0 when not playing.
    if (state_ != kPlaying) {
      // TODO(scherkus): To keep the audio hardware busy we write at most 8k of
      // zeros.  This gets around the tricky situation of pausing and resuming
      // the audio IPC layer in Chrome.  Ideally, we should return zero and then
      // the subclass can restart the conversation.
      const uint32 kZeroLength = 8192;
      dest_written = std::min(kZeroLength, dest_len);
      memset(dest, 0, dest_written);
      return dest_written;
    }

    // Save a local copy of last fill buffer time and reset the member.
    last_fill_buffer_time = last_fill_buffer_time_;
    last_fill_buffer_time_ = base::TimeDelta();

    // Use two conditions to determine the end of playback:
    // 1. Algorithm has no audio data.
    // 2. Browser process has no audio data.
    if (algorithm_->IsQueueEmpty() && buffers_empty) {
      if (recieved_end_of_stream_ && !rendered_end_of_stream_) {
        rendered_end_of_stream_ = true;
        host()->NotifyEnded();
      }
    } else {
      // Otherwise fill the buffer.
      dest_written = algorithm_->FillBuffer(dest, dest_len);
    }

    // Get the current time.
    last_fill_buffer_time_ = algorithm_->GetTime();
  }

  // Update the pipeline's time if it was set last time.
  if (last_fill_buffer_time.InMicroseconds() > 0 &&
      (last_fill_buffer_time != last_fill_buffer_time_ ||
       (last_fill_buffer_time - playback_delay) > host()->GetTime())) {
    // Adjust the |last_fill_buffer_time| with the playback delay.
    // TODO(hclam): If there is a playback delay, the pipeline would not be
    // updated with a correct timestamp when the stream is played at the very
    // end since we use decoded packets to trigger time updates. A better
    // solution is to start a timer when an audio packet is decoded to allow
    // finer time update events.
    last_fill_buffer_time -= playback_delay;
    host()->SetTime(last_fill_buffer_time);
  }

  return dest_written;
}

void AudioRendererBase::ScheduleRead_Locked() {
  lock_.AssertAcquired();
  ++pending_reads_;
  // TODO(jiesun): We use dummy buffer to feed decoder to let decoder to
  // provide buffer pools. In the future, we may want to implement real
  // buffer pool to recycle buffers.
  scoped_refptr<Buffer> buffer;
  decoder_->ProduceAudioSamples(buffer);
}

// static
bool AudioRendererBase::ParseMediaFormat(const MediaFormat& media_format,
                                         int* channels_out,
                                         int* sample_rate_out,
                                         int* sample_bits_out) {
  // TODO(scherkus): might be handy to support NULL parameters.
  std::string mime_type;
  return media_format.GetAsString(MediaFormat::kMimeType, &mime_type) &&
      media_format.GetAsInteger(MediaFormat::kChannels, channels_out) &&
      media_format.GetAsInteger(MediaFormat::kSampleRate, sample_rate_out) &&
      media_format.GetAsInteger(MediaFormat::kSampleBits, sample_bits_out) &&
      mime_type.compare(mime_type::kUncompressedAudio) == 0;
}

void AudioRendererBase::SetPlaybackRate(float playback_rate) {
  algorithm_->set_playback_rate(playback_rate);
}

float AudioRendererBase::GetPlaybackRate() {
  return algorithm_->playback_rate();
}

}  // namespace media