15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/android/opensles_output.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/debug/trace_event.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/android/audio_manager_android.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#define LOG_ON_FAILURE_AND_RETURN(op, ...)      \
12d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  do {                                          \
13d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    SLresult err = (op);                        \
14d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (err != SL_RESULT_SUCCESS) {             \
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      DLOG(ERROR) << #op << " failed: " << err; \
16d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return __VA_ARGS__;                       \
17d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }                                           \
18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  } while (0)
19eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace media {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                           const AudioParameters& params,
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                           SLint32 stream_type)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : audio_manager_(manager),
26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      stream_type_(stream_type),
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      callback_(NULL),
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      player_(NULL),
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      simple_buffer_queue_(NULL),
30d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      active_buffer_index_(0),
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      buffer_size_bytes_(0),
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      started_(false),
33a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      muted_(false),
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      volume_(1.0) {
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream("
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)           << "stream_type=" << stream_type << ")";
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.formatType = SL_DATAFORMAT_PCM;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.numChannels = static_cast<SLuint32>(params.channels());
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Provides sampling rate in milliHertz to OpenSLES.
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.samplesPerSec = static_cast<SLuint32>(params.sample_rate() * 1000);
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.bitsPerSample = params.bits_per_sample();
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.containerSize = params.bits_per_sample();
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.endianness = SL_BYTEORDER_LITTLEENDIAN;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (format_.numChannels == 1)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    format_.channelMask = SL_SPEAKER_FRONT_CENTER;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else if (format_.numChannels == 2)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    format_.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "Unsupported number of channels: " << format_.numChannels;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  buffer_size_bytes_ = params.GetBytesPerBuffer();
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  audio_bus_ = AudioBus::Create(params);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memset(&audio_data_, 0, sizeof(audio_data_));
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OpenSLESOutputStream::~OpenSLESOutputStream() {
58d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DVLOG(2) << "OpenSLESOutputStream::~OpenSLESOutputStream()";
59d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!engine_object_.Get());
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!player_object_.Get());
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!output_mixer_.Get());
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!player_);
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!simple_buffer_queue_);
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!audio_data_[0]);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool OpenSLESOutputStream::Open() {
69d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DVLOG(2) << "OpenSLESOutputStream::Open()";
70d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (engine_object_.Get())
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!CreatePlayer())
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetupAudioBuffer();
78d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  active_buffer_index_ = 0;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OpenSLESOutputStream::Start(AudioSourceCallback* callback) {
84d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DVLOG(2) << "OpenSLESOutputStream::Start()";
85d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(callback);
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(player_);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(simple_buffer_queue_);
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (started_)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
92d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  base::AutoLock lock(lock_);
93d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(callback_ == NULL || callback_ == callback);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  callback_ = callback;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Avoid start-up glitches by filling up one buffer queue before starting
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the stream.
98d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  FillBufferQueueNoLock();
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
100d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Start streaming data by setting the play state to SL_PLAYSTATE_PLAYING.
101d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // For a player object, when the object is in the SL_PLAYSTATE_PLAYING
102d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // state, adding buffers will implicitly start playback.
103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
104eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      (*player_)->SetPlayState(player_, SL_PLAYSTATE_PLAYING));
105d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
106d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  started_ = true;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OpenSLESOutputStream::Stop() {
110d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DVLOG(2) << "OpenSLESOutputStream::Stop()";
111d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!started_)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  base::AutoLock lock(lock_);
116d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
117d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Stop playing by setting the play state to SL_PLAYSTATE_STOPPED.
118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
119eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      (*player_)->SetPlayState(player_, SL_PLAYSTATE_STOPPED));
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Clear the buffer queue so that the old data won't be played when
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // resuming playing.
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      (*simple_buffer_queue_)->Clear(simple_buffer_queue_));
125d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
126d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#ifndef NDEBUG
127d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Verify that the buffer queue is in fact cleared as it should.
128d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  SLAndroidSimpleBufferQueueState buffer_queue_state;
129d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  LOG_ON_FAILURE_AND_RETURN((*simple_buffer_queue_)->GetState(
130d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      simple_buffer_queue_, &buffer_queue_state));
131d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK_EQ(0u, buffer_queue_state.count);
132d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK_EQ(0u, buffer_queue_state.index);
133d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#endif
134d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  callback_ = NULL;
136d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  started_ = false;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OpenSLESOutputStream::Close() {
140d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DVLOG(2) << "OpenSLESOutputStream::Close()";
141d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
142d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Stop the stream if it is still playing.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Stop();
145d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  {
146d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Destroy the buffer queue player object and invalidate all associated
147d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // interfaces.
148d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    player_object_.Reset();
149d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    simple_buffer_queue_ = NULL;
150d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    player_ = NULL;
151d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
152d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Destroy the mixer object. We don't store any associated interface for
153d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // this object.
154d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    output_mixer_.Reset();
155d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
156d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Destroy the engine object. We don't store any associated interface for
157d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // this object.
158d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    engine_object_.Reset();
159d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    ReleaseAudioBuffer();
160d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  audio_manager_->ReleaseOutputStream(this);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OpenSLESOutputStream::SetVolume(double volume) {
166d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DVLOG(2) << "OpenSLESOutputStream::SetVolume(" << volume << ")";
167d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float volume_float = static_cast<float>(volume);
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (volume_float < 0.0f || volume_float > 1.0f) {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  volume_ = volume_float;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OpenSLESOutputStream::GetVolume(double* volume) {
176d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *volume = static_cast<double>(volume_);
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
180a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void OpenSLESOutputStream::SetMute(bool muted) {
181a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  DVLOG(2) << "OpenSLESOutputStream::SetMute(" << muted << ")";
182a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
183a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  muted_ = muted;
184a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
185a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool OpenSLESOutputStream::CreatePlayer() {
187d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
188d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(!engine_object_.Get());
189d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(!player_object_.Get());
190d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(!output_mixer_.Get());
191d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(!player_);
192d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(!simple_buffer_queue_);
193d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Initializes the engine object with specific option. After working with the
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // object, we need to free the object and its resources.
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SLEngineOption option[] = {
197d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      {SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE)}};
198eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      slCreateEngine(engine_object_.Receive(), 1, option, 0, NULL, NULL),
200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      false);
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Realize the SL engine object in synchronous mode.
203eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
204d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE), false);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the SL engine interface which is implicit.
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SLEngineItf engine;
208d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  LOG_ON_FAILURE_AND_RETURN(engine_object_->GetInterface(
209d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                engine_object_.Get(), SL_IID_ENGINE, &engine),
210d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                            false);
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create ouput mixer object to be used by the player.
213d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  LOG_ON_FAILURE_AND_RETURN((*engine)->CreateOutputMix(
214d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                engine, output_mixer_.Receive(), 0, NULL, NULL),
215d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                            false);
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Realizing the output mix object in synchronous mode.
218eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
219d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      output_mixer_->Realize(output_mixer_.Get(), SL_BOOLEAN_FALSE), false);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Audio source configuration.
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SLDataLocator_AndroidSimpleBufferQueue simple_buffer_queue = {
223d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
224d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      static_cast<SLuint32>(kMaxNumOfBuffersInQueue)};
225d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  SLDataSource audio_source = {&simple_buffer_queue, &format_};
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Audio sink configuration.
228d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  SLDataLocator_OutputMix locator_output_mix = {SL_DATALOCATOR_OUTPUTMIX,
229d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                                output_mixer_.Get()};
230d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  SLDataSink audio_sink = {&locator_output_mix, NULL};
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create an audio player.
233d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const SLInterfaceID interface_id[] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME,
234d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                        SL_IID_ANDROIDCONFIGURATION};
235d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const SLboolean interface_required[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,
236d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                          SL_BOOLEAN_TRUE};
237eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
238eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      (*engine)->CreateAudioPlayer(engine,
239eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                   player_object_.Receive(),
240eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                   &audio_source,
241eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                   &audio_sink,
242eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                   arraysize(interface_id),
243eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                   interface_id,
244eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                   interface_required),
245eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      false);
246eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
247eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Create AudioPlayer and specify SL_IID_ANDROIDCONFIGURATION.
248eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SLAndroidConfigurationItf player_config;
249eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
250d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      player_object_->GetInterface(
251d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          player_object_.Get(), SL_IID_ANDROIDCONFIGURATION, &player_config),
252eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      false);
253eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
254a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Set configuration using the stream type provided at construction.
255eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
256eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      (*player_config)->SetConfiguration(player_config,
257eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                         SL_ANDROID_KEY_STREAM_TYPE,
258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                         &stream_type_,
259d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                         sizeof(SLint32)),
260eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      false);
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Realize the player object in synchronous mode.
263eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
264d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      player_object_->Realize(player_object_.Get(), SL_BOOLEAN_FALSE), false);
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get an implicit player interface.
267eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
268eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      player_object_->GetInterface(player_object_.Get(), SL_IID_PLAY, &player_),
269eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      false);
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the simple buffer queue interface.
272eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
273d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      player_object_->GetInterface(
274d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          player_object_.Get(), SL_IID_BUFFERQUEUE, &simple_buffer_queue_),
275eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      false);
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Register the input callback for the simple buffer queue.
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This callback will be called when the soundcard needs data.
279eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LOG_ON_FAILURE_AND_RETURN(
280d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      (*simple_buffer_queue_)->RegisterCallback(
281d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          simple_buffer_queue_, SimpleBufferQueueCallback, this),
282eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      false);
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
284eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return true;
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OpenSLESOutputStream::SimpleBufferQueueCallback(
288d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    SLAndroidSimpleBufferQueueItf buffer_queue,
289d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    void* instance) {
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OpenSLESOutputStream* stream =
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<OpenSLESOutputStream*>(instance);
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  stream->FillBufferQueue();
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OpenSLESOutputStream::FillBufferQueue() {
296d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  base::AutoLock lock(lock_);
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!started_)
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
300eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  TRACE_EVENT0("audio", "OpenSLESOutputStream::FillBufferQueue");
301d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
302d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Verify that we are in a playing state.
303d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  SLuint32 state;
304d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  SLresult err = (*player_)->GetPlayState(player_, &state);
305d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (SL_RESULT_SUCCESS != err) {
306d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    HandleError(err);
307d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return;
308d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
309d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (state != SL_PLAYSTATE_PLAYING) {
310d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    DLOG(WARNING) << "Received callback in non-playing state";
311d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return;
312d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
313d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
314d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Fill up one buffer in the queue by asking the registered source for
315d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // data using the OnMoreData() callback.
316d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  FillBufferQueueNoLock();
317d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
318d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
319d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void OpenSLESOutputStream::FillBufferQueueNoLock() {
320d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Ensure that the calling thread has acquired the lock since it is not
321d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // done in this method.
322d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  lock_.AssertAcquired();
323d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Read data from the registered client source.
325d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // TODO(henrika): Investigate if it is possible to get a more accurate
326d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // delay estimation.
327d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const uint32 hardware_delay = buffer_size_bytes_;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int frames_filled = callback_->OnMoreData(
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      audio_bus_.get(), AudioBuffersState(0, hardware_delay));
330d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (frames_filled <= 0) {
331d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Audio source is shutting down, or halted on error.
332d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return;
333d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
334d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
335d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Note: If the internal representation ever changes from 16-bit PCM to
336d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // raw float, the data must be clipped and sanitized since it may come
337d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // from an untrusted source such as NaCl.
338a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  audio_bus_->Scale(muted_ ? 0.0f : volume_);
339d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  audio_bus_->ToInterleaved(frames_filled,
340d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                            format_.bitsPerSample / 8,
341d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                            audio_data_[active_buffer_index_]);
342d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
343d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const int num_filled_bytes =
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      frames_filled * audio_bus_->channels() * format_.bitsPerSample / 8;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_LE(static_cast<size_t>(num_filled_bytes), buffer_size_bytes_);
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Enqueue the buffer for playback.
348d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  SLresult err =
349d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      (*simple_buffer_queue_)->Enqueue(simple_buffer_queue_,
350d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                       audio_data_[active_buffer_index_],
351d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                       num_filled_bytes);
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (SL_RESULT_SUCCESS != err)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleError(err);
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  active_buffer_index_ = (active_buffer_index_ + 1) % kMaxNumOfBuffersInQueue;
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OpenSLESOutputStream::SetupAudioBuffer() {
359d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!audio_data_[0]);
361d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    audio_data_[i] = new uint8[buffer_size_bytes_];
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OpenSLESOutputStream::ReleaseAudioBuffer() {
367d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (audio_data_[0]) {
369d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
370d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      delete[] audio_data_[i];
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      audio_data_[i] = NULL;
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OpenSLESOutputStream::HandleError(SLresult error) {
377eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DLOG(ERROR) << "OpenSLES Output error " << error;
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (callback_)
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    callback_->OnError(this);
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace media
383