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/win/waveout_output_win.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windows.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <mmsystem.h> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma comment(lib, "winmm.lib") 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/atomicops.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/debug/trace_event.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/audio_io.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/win/audio_manager_win.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace media { 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Some general thoughts about the waveOut API which is badly documented : 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - We use CALLBACK_EVENT mode in which XP signals events such as buffer 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// releases. 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - We use RegisterWaitForSingleObject() so one of threads in thread pool 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// automatically calls our callback that feeds more data to Windows. 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - Windows does not provide a way to query if the device is playing or paused 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// thus it forces you to maintain state, which naturally is not exactly 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// synchronized to the actual device state. 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Sixty four MB is the maximum buffer size per AudioOutputStream. 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const uint32 kMaxOpenBufferSize = 1024 * 1024 * 64; 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// See Also 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// http://www.thx.com/consumer/home-entertainment/home-theater/surround-sound-speaker-set-up/ 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// http://en.wikipedia.org/wiki/Surround_sound 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMaxChannelsToMask = 8; 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = { 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 0, 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 1 = Mono 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_FRONT_CENTER, 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 2 = Stereo 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 3 = Stereo + Center 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER, 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 4 = Quad 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 5 = 5.0 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 6 = 5.1 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 7 = 6.1 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_BACK_CENTER, 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 8 = 7.1 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(fbarchard): Add additional masks for 7.2 and beyond. 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)inline size_t PCMWaveOutAudioOutputStream::BufferSize() const { 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Round size of buffer up to the nearest 16 bytes. 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (sizeof(WAVEHDR) + buffer_size_ + 15u) & static_cast<size_t>(~15); 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)inline WAVEHDR* PCMWaveOutAudioOutputStream::GetBuffer(int n) const { 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_GE(n, 0); 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_LT(n, num_buffers_); 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return reinterpret_cast<WAVEHDR*>(&buffers_[n * BufferSize()]); 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AudioManagerWin* manager, const AudioParameters& params, int num_buffers, 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UINT device_id) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : state_(PCMA_BRAND_NEW), 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) manager_(manager), 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) device_id_(device_id), 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) waveout_(NULL), 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) callback_(NULL), 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) num_buffers_(num_buffers), 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffer_size_(params.GetBytesPerBuffer()), 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) volume_(1), 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) channels_(params.channels()), 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_bytes_(0), 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) waiting_handle_(NULL), 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) audio_bus_(AudioBus::Create(params)) { 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.Format.nChannels = params.channels(); 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.Format.nSamplesPerSec = params.sample_rate(); 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.Format.wBitsPerSample = params.bits_per_sample(); 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.Format.cbSize = sizeof(format_) - sizeof(WAVEFORMATEX); 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The next are computed from above. 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.Format.nBlockAlign = (format_.Format.nChannels * 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.Format.wBitsPerSample) / 8; 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.Format.nAvgBytesPerSec = format_.Format.nBlockAlign * 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.Format.nSamplesPerSec; 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (params.channels() > kMaxChannelsToMask) { 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.dwChannelMask = kChannelsToMask[kMaxChannelsToMask]; 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.dwChannelMask = kChannelsToMask[params.channels()]; 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.Samples.wValidBitsPerSample = params.bits_per_sample(); 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PCMWaveOutAudioOutputStream::~PCMWaveOutAudioOutputStream() { 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(NULL == waveout_); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PCMWaveOutAudioOutputStream::Open() { 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (state_ != PCMA_BRAND_NEW) 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (BufferSize() * num_buffers_ > kMaxOpenBufferSize) 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (num_buffers_ < 2 || num_buffers_ > 5) 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create buffer event. 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffer_event_.Set(::CreateEvent(NULL, // Security attributes. 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FALSE, // It will auto-reset. 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FALSE, // Initial state. 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL)); // No name. 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!buffer_event_.Get()) 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Open the device. 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We'll be getting buffer_event_ events when it's time to refill the buffer. 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MMRESULT result = ::waveOutOpen( 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &waveout_, 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) device_id_, 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) reinterpret_cast<LPCWAVEFORMATEX>(&format_), 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) reinterpret_cast<DWORD_PTR>(buffer_event_.Get()), 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL, 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CALLBACK_EVENT); 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (result != MMSYSERR_NOERROR) 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetupBuffers(); 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state_ = PCMA_READY; 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::SetupBuffers() { 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffers_.reset(new char[BufferSize() * num_buffers_]); 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int ix = 0; ix != num_buffers_; ++ix) { 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WAVEHDR* buffer = GetBuffer(ix); 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffer->lpData = reinterpret_cast<char*>(buffer) + sizeof(WAVEHDR); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffer->dwBufferLength = buffer_size_; 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffer->dwBytesRecorded = 0; 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffer->dwFlags = WHDR_DONE; 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffer->dwLoops = 0; 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Tell windows sound drivers about our buffers. Not documented what 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // this does but we can guess that causes the OS to keep a reference to 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the memory pages so the driver can use them without worries. 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ::waveOutPrepareHeader(waveout_, buffer, sizeof(WAVEHDR)); 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::FreeBuffers() { 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int ix = 0; ix != num_buffers_; ++ix) { 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ::waveOutUnprepareHeader(waveout_, GetBuffer(ix), sizeof(WAVEHDR)); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) buffers_.reset(); 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Initially we ask the source to fill up all audio buffers. If we don't do 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// this then we would always get the driver callback when it is about to run 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// samples and that would leave too little time to react. 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (state_ != PCMA_READY) 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) callback_ = callback; 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Reset buffer event, it can be left in the arbitrary state if we 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // previously stopped the stream. Can happen because we are stopping 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // callbacks before stopping playback itself. 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!::ResetEvent(buffer_event_.Get())) { 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HandleError(MMSYSERR_ERROR); 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Start watching for buffer events. 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!::RegisterWaitForSingleObject(&waiting_handle_, 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffer_event_.Get(), 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &BufferCallback, 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this, 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) INFINITE, 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WT_EXECUTEDEFAULT)) { 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HandleError(MMSYSERR_ERROR); 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) waiting_handle_ = NULL; 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state_ = PCMA_PLAYING; 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Queue the buffers. 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_bytes_ = 0; 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int ix = 0; ix != num_buffers_; ++ix) { 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WAVEHDR* buffer = GetBuffer(ix); 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) QueueNextPacket(buffer); // Read more data. 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_bytes_ += buffer->dwBufferLength; 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // From now on |pending_bytes_| would be accessed by callback thread. 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Most likely waveOutPause() or waveOutRestart() has its own memory barrier, 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // but issuing our own is safer. 2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::subtle::MemoryBarrier(); 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MMRESULT result = ::waveOutPause(waveout_); 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (result != MMSYSERR_NOERROR) { 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HandleError(result); 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Send the buffers to the audio driver. Note that the device is paused 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // so we avoid entering the callback method while still here. 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int ix = 0; ix != num_buffers_; ++ix) { 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result = ::waveOutWrite(waveout_, GetBuffer(ix), sizeof(WAVEHDR)); 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (result != MMSYSERR_NOERROR) { 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HandleError(result); 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result = ::waveOutRestart(waveout_); 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (result != MMSYSERR_NOERROR) { 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HandleError(result); 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Stopping is tricky if we want it be fast. 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For now just do it synchronously and avoid all the complexities. 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(enal): if we want faster Stop() we can create singleton that keeps track 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// of all currently playing streams. Then you don't have to wait 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// till all callbacks are completed. Of course access to singleton 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// should be under its own lock, and checking the liveness and 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// acquiring the lock on stream should be done atomically. 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::Stop() { 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (state_ != PCMA_PLAYING) 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state_ = PCMA_STOPPING; 2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::subtle::MemoryBarrier(); 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Stop watching for buffer event, waits until outstanding callbacks finish. 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (waiting_handle_) { 2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!::UnregisterWaitEx(waiting_handle_, INVALID_HANDLE_VALUE)) 2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) HandleError(::GetLastError()); 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) waiting_handle_ = NULL; 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Stop playback. 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MMRESULT res = ::waveOutReset(waveout_); 2605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (res != MMSYSERR_NOERROR) 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HandleError(res); 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Wait for lock to ensure all outstanding callbacks have completed. 2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::AutoLock auto_lock(lock_); 2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // waveOutReset() leaves buffers in the unpredictable state, causing 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // problems if we want to close, release, or reuse them. Fix the states. 2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (int ix = 0; ix != num_buffers_; ++ix) 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetBuffer(ix)->dwFlags = WHDR_PREPARED; 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Don't use callback after Stop(). 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) callback_ = NULL; 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state_ = PCMA_READY; 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// We can Close in any state except that trying to close a stream that is 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// playing Windows generates an error. We cannot propagate it to the source, 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// as callback_ is set to NULL. Just print it and hope somebody somehow 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// will find it... 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::Close() { 2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Force Stop() to ensure it's safe to release buffers and free the stream. 2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Stop(); 2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (waveout_) { 2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) FreeBuffers(); 2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // waveOutClose() generates a WIM_CLOSE callback. In case Start() was never 2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // called, force a reset to ensure close succeeds. 2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) MMRESULT res = ::waveOutReset(waveout_); 2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR)); 2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) res = ::waveOutClose(waveout_); 2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR)); 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state_ = PCMA_CLOSED; 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) waveout_ = NULL; 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Tell the audio manager that we have been released. This can result in 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the manager destroying us in-place so this needs to be the last thing 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // we do on this function. 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) manager_->ReleaseOutputStream(this); 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::SetVolume(double volume) { 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!waveout_) 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) volume_ = static_cast<float>(volume); 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::GetVolume(double* volume) { 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!waveout_) 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *volume = volume_; 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::HandleError(MMRESULT error) { 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(WARNING) << "PCMWaveOutAudio error " << error; 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (callback_) 3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) callback_->OnError(this); 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) { 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(channels_, format_.Format.nChannels); 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Call the source which will fill our buffer with pleasant sounds and 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // return to us how many bytes were used. 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(fbarchard): Handle used 0 by queueing more. 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(sergeyu): Specify correct hardware delay for AudioBuffersState. 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int frames_filled = callback_->OnMoreData( 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) audio_bus_.get(), AudioBuffersState(pending_bytes_, 0)); 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uint32 used = frames_filled * audio_bus_->channels() * 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_.Format.wBitsPerSample / 8; 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (used <= buffer_size_) { 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Note: If this ever changes to output raw float the data must be clipped 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // and sanitized since it may come from an untrusted source such as NaCl. 337c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) audio_bus_->Scale(volume_); 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) audio_bus_->ToInterleaved( 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) frames_filled, format_.Format.wBitsPerSample / 8, buffer->lpData); 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffer->dwBufferLength = used * format_.Format.nChannels / channels_; 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HandleError(0); 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffer->dwFlags = WHDR_PREPARED; 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// One of the threads in our thread pool asynchronously calls this function when 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// buffer_event_ is signalled. Search through all the buffers looking for freed 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ones, fills them with data, and "feed" the Windows. 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Note: by searching through all the buffers we guarantee that we fill all the 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// buffers, even when "event loss" happens, i.e. if Windows signals event 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// when it did not flip into unsignaled state from the previous signal. 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NTAPI PCMWaveOutAudioOutputStream::BufferCallback(PVOID lpParameter, 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOOLEAN timer_fired) { 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TRACE_EVENT0("audio", "PCMWaveOutAudioOutputStream::BufferCallback"); 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!timer_fired); 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PCMWaveOutAudioOutputStream* stream = 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) reinterpret_cast<PCMWaveOutAudioOutputStream*>(lpParameter); 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Lock the stream so callbacks do not interfere with each other. 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Several callbacks can be called simultaneously by different threads in the 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // thread pool if some of the callbacks are slow, or system is very busy and 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // scheduled callbacks are not called on time. 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::AutoLock auto_lock(stream->lock_); 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (stream->state_ != PCMA_PLAYING) 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int ix = 0; ix != stream->num_buffers_; ++ix) { 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WAVEHDR* buffer = stream->GetBuffer(ix); 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (buffer->dwFlags & WHDR_DONE) { 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Before we queue the next packet, we need to adjust the number of 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // pending bytes since the last write to hardware. 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stream->pending_bytes_ -= buffer->dwBufferLength; 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stream->QueueNextPacket(buffer); 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // QueueNextPacket() can take a long time, especially if several of them 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // were called back-to-back. Check if we are stopping now. 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (stream->state_ != PCMA_PLAYING) 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Time to send the buffer to the audio driver. Since we are reusing 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the same buffers we can get away without calling waveOutPrepareHeader. 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MMRESULT result = ::waveOutWrite(stream->waveout_, 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) buffer, 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sizeof(WAVEHDR)); 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (result != MMSYSERR_NOERROR) 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stream->HandleError(result); 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stream->pending_bytes_ += buffer->dwBufferLength; 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace media 397