Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / examples / api / media_stream_audio / media_stream_audio.cc
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
35 namespace {
36
37 const uint32_t kColorRed = 0xFFFF0000;
38 const uint32_t kColorGreen = 0xFF00FF00;
39 const uint32_t kColorGrey1 = 0xFF202020;
40 const uint32_t kColorGrey2 = 0xFF404040;
41 const uint32_t kColorGrey3 = 0xFF606060;
42
43 class 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
213 class 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
222 namespace pp {
223
224 // Factory function for your specialization of the Module object.
225 Module* CreateModule() {
226   return new MediaStreamAudioModule();
227 }
228
229 }  // namespace pp