media_stream_audio.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
1// Copyright 2014 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 <stdlib.h>
6#include <string.h>
7
8#include <algorithm>
9#include <limits>
10#include <vector>
11
12#include "ppapi/cpp/audio_buffer.h"
13#include "ppapi/cpp/graphics_2d.h"
14#include "ppapi/cpp/image_data.h"
15#include "ppapi/cpp/instance.h"
16#include "ppapi/cpp/logging.h"
17#include "ppapi/cpp/media_stream_audio_track.h"
18#include "ppapi/cpp/module.h"
19#include "ppapi/cpp/rect.h"
20#include "ppapi/cpp/size.h"
21#include "ppapi/cpp/var_dictionary.h"
22#include "ppapi/utility/completion_callback_factory.h"
23
24// When compiling natively on Windows, min, max and PostMessage can be #define-d
25// to something else.
26#ifdef PostMessage
27#undef min
28#undef max
29#undef PostMessage
30#endif
31
32// This example demonstrates receiving audio samples from a
33// MediaStreamAudioTrack and visualizing them.
34
35namespace {
36
37const uint32_t kColorRed = 0xFFFF0000;
38const uint32_t kColorGreen = 0xFF00FF00;
39const uint32_t kColorGrey1 = 0xFF202020;
40const uint32_t kColorGrey2 = 0xFF404040;
41const uint32_t kColorGrey3 = 0xFF606060;
42
43class MediaStreamAudioInstance : public pp::Instance {
44 public:
45  explicit MediaStreamAudioInstance(PP_Instance instance)
46      : pp::Instance(instance),
47        callback_factory_(this),
48        first_buffer_(true),
49        sample_count_(0),
50        channel_count_(0),
51        timer_interval_(0),
52        pending_paint_(false),
53        waiting_for_flush_completion_(false) {
54  }
55
56  virtual ~MediaStreamAudioInstance() {
57  }
58
59  virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
60    if (position.size() == size_)
61      return;
62
63    size_ = position.size();
64    device_context_ = pp::Graphics2D(this, size_, false);
65    if (!BindGraphics(device_context_))
66      return;
67
68    Paint();
69  }
70
71  virtual void HandleMessage(const pp::Var& var_message) {
72    if (!var_message.is_dictionary())
73      return;
74    pp::VarDictionary var_dictionary_message(var_message);
75    pp::Var var_track = var_dictionary_message.Get("track");
76    if (!var_track.is_resource())
77      return;
78
79    pp::Resource resource_track = var_track.AsResource();
80    audio_track_ = pp::MediaStreamAudioTrack(resource_track);
81    audio_track_.GetBuffer(callback_factory_.NewCallbackWithOutput(
82          &MediaStreamAudioInstance::OnGetBuffer));
83  }
84
85 private:
86  // Starts a timer to run Paint() in every |timer_interval_|.
87  void ScheduleNextTimer() {
88    PP_DCHECK(timer_interval_ > 0);
89    pp::Module::Get()->core()->CallOnMainThread(
90        timer_interval_,
91        callback_factory_.NewCallback(&MediaStreamAudioInstance::OnTimer),
92        0);
93  }
94
95  void OnTimer(int32_t) {
96    ScheduleNextTimer();
97    Paint();
98  }
99
100  void DidFlush(int32_t result) {
101    waiting_for_flush_completion_ = false;
102    if (pending_paint_)
103      Paint();
104  }
105
106  void Paint() {
107    if (waiting_for_flush_completion_) {
108      pending_paint_ = true;
109      return;
110    }
111
112    pending_paint_ = false;
113
114    if (size_.IsEmpty())
115      return;  // Nothing to do.
116
117    pp::ImageData image = PaintImage(size_);
118    if (!image.is_null()) {
119      device_context_.ReplaceContents(&image);
120      waiting_for_flush_completion_ = true;
121      device_context_.Flush(
122          callback_factory_.NewCallback(&MediaStreamAudioInstance::DidFlush));
123    }
124  }
125
126  pp::ImageData PaintImage(const pp::Size& size) {
127    pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, size, false);
128    if (image.is_null())
129      return image;
130
131    // Clear to dark grey.
132    for (int y = 0; y < size.height(); y++) {
133      for (int x = 0; x < size.width(); x++)
134        *image.GetAddr32(pp::Point(x, y)) = kColorGrey1;
135    }
136
137    int mid_height = size.height() / 2;
138    int max_amplitude = size.height() * 4 / 10;
139
140    // Draw some lines.
141    for (int x = 0; x < size.width(); x++) {
142      *image.GetAddr32(pp::Point(x, mid_height)) = kColorGrey3;
143      *image.GetAddr32(pp::Point(x, mid_height + max_amplitude)) = kColorGrey2;
144      *image.GetAddr32(pp::Point(x, mid_height - max_amplitude)) = kColorGrey2;
145    }
146
147
148    // Draw our samples.
149    for (int x = 0, i = 0;
150         x < std::min(size.width(), static_cast<int>(sample_count_));
151         x++, i += channel_count_) {
152      for (uint32_t ch = 0; ch < std::min(channel_count_, 2U); ++ch) {
153        int y = samples_[i + ch] * max_amplitude /
154                (std::numeric_limits<int16_t>::max() + 1) + mid_height;
155        *image.GetAddr32(pp::Point(x, y)) = (ch == 0 ? kColorRed : kColorGreen);
156      }
157    }
158
159    return image;
160  }
161
162  // Callback that is invoked when new buffers are received.
163  void OnGetBuffer(int32_t result, pp::AudioBuffer buffer) {
164    if (result != PP_OK)
165      return;
166
167    assert(buffer.GetSampleSize() == PP_AUDIOBUFFER_SAMPLESIZE_16_BITS);
168    const char* data = static_cast<const char*>(buffer.GetDataBuffer());
169    uint32_t channels = buffer.GetNumberOfChannels();
170    uint32_t samples = buffer.GetNumberOfSamples() / channels;
171
172    if (channel_count_ != channels || sample_count_ != samples) {
173      channel_count_ = channels;
174      sample_count_ = samples;
175
176      samples_.resize(sample_count_ * channel_count_);
177      // Try (+ 5) to ensure that we pick up a new set of samples between each
178      // timer-generated repaint.
179      timer_interval_ = (sample_count_ * 1000) / buffer.GetSampleRate() + 5;
180      // Start the timer for the first buffer.
181      if (first_buffer_) {
182        first_buffer_ = false;
183        ScheduleNextTimer();
184      }
185    }
186
187    memcpy(samples_.data(), data,
188        sample_count_ * channel_count_ * sizeof(int16_t));
189
190    audio_track_.RecycleBuffer(buffer);
191    audio_track_.GetBuffer(callback_factory_.NewCallbackWithOutput(
192        &MediaStreamAudioInstance::OnGetBuffer));
193
194  }
195
196  pp::MediaStreamAudioTrack audio_track_;
197  pp::CompletionCallbackFactory<MediaStreamAudioInstance> callback_factory_;
198
199  bool first_buffer_;
200  uint32_t sample_count_;
201  uint32_t channel_count_;
202  std::vector<int16_t> samples_;
203
204  int32_t timer_interval_;
205
206  // Painting stuff.
207  pp::Size size_;
208  pp::Graphics2D device_context_;
209  bool pending_paint_;
210  bool waiting_for_flush_completion_;
211};
212
213class MediaStreamAudioModule : public pp::Module {
214 public:
215  virtual pp::Instance* CreateInstance(PP_Instance instance) {
216    return new MediaStreamAudioInstance(instance);
217  }
218};
219
220}  // namespace
221
222namespace pp {
223
224// Factory function for your specialization of the Module object.
225Module* CreateModule() {
226  return new MediaStreamAudioModule();
227}
228
229}  // namespace pp
230