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