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