| Index: content/renderer/media/webrtc/peer_connection_remote_audio_source.cc | 
| diff --git a/content/renderer/media/webrtc/peer_connection_remote_audio_source.cc b/content/renderer/media/webrtc/peer_connection_remote_audio_source.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..46d7318c3b2f5f91b440a4cb82e7a3b0f9245799 | 
| --- /dev/null | 
| +++ b/content/renderer/media/webrtc/peer_connection_remote_audio_source.cc | 
| @@ -0,0 +1,153 @@ | 
| +// Copyright 2015 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "content/renderer/media/webrtc/peer_connection_remote_audio_source.h" | 
| + | 
| +#include "base/logging.h" | 
| +#include "base/time/time.h" | 
| +#include "media/base/audio_bus.h" | 
| + | 
| +namespace content { | 
| + | 
| +namespace { | 
| +// Used as an identifier for the down-casters. | 
| +void* const kClassIdentifier = const_cast<void**>(&kClassIdentifier); | 
| +}  // namespace | 
| + | 
| +PeerConnectionRemoteAudioTrack::PeerConnectionRemoteAudioTrack( | 
| +    scoped_refptr<webrtc::AudioTrackInterface> track_interface) | 
| +    : MediaStreamAudioTrack(false /* is_local_track */), | 
| +      track_interface_(std::move(track_interface)) { | 
| +  DVLOG(1) | 
| +      << "PeerConnectionRemoteAudioTrack::PeerConnectionRemoteAudioTrack()"; | 
| +} | 
| + | 
| +PeerConnectionRemoteAudioTrack::~PeerConnectionRemoteAudioTrack() { | 
| +  DVLOG(1) | 
| +      << "PeerConnectionRemoteAudioTrack::~PeerConnectionRemoteAudioTrack()"; | 
| +  // Ensure the track is stopped. | 
| +  MediaStreamAudioTrack::Stop(); | 
| +} | 
| + | 
| +// static | 
| +PeerConnectionRemoteAudioTrack* PeerConnectionRemoteAudioTrack::From( | 
| +    MediaStreamAudioTrack* track) { | 
| +  if (track && track->GetClassIdentifier() == kClassIdentifier) | 
| +    return static_cast<PeerConnectionRemoteAudioTrack*>(track); | 
| +  return nullptr; | 
| +} | 
| + | 
| +void PeerConnectionRemoteAudioTrack::SetEnabled(bool enabled) { | 
| +  DCHECK(thread_checker_.CalledOnValidThread()); | 
| + | 
| +  // This affects the shared state of the source for whether or not it's a part | 
| +  // of the mixed audio that's rendered for remote tracks from WebRTC. | 
| +  // All tracks from the same source will share this state and thus can step | 
| +  // on each other's toes. | 
| +  // This is also why we can't check the enabled state for equality with | 
| +  // |enabled| before setting the mixing enabled state. This track's enabled | 
| +  // state and the shared state might not be the same. | 
| +  track_interface_->set_enabled(enabled); | 
| + | 
| +  MediaStreamAudioTrack::SetEnabled(enabled); | 
| +} | 
| + | 
| +void* PeerConnectionRemoteAudioTrack::GetClassIdentifier() const { | 
| +  return kClassIdentifier; | 
| +} | 
| + | 
| +PeerConnectionRemoteAudioSource::PeerConnectionRemoteAudioSource( | 
| +    scoped_refptr<webrtc::AudioTrackInterface> track_interface) | 
| +    : MediaStreamAudioSource(false /* is_local_source */), | 
| +      track_interface_(std::move(track_interface)), | 
| +      is_sink_of_peer_connection_(false) { | 
| +  DCHECK(track_interface_); | 
| +  DVLOG(1) | 
| +      << "PeerConnectionRemoteAudioSource::PeerConnectionRemoteAudioSource()"; | 
| +} | 
| + | 
| +PeerConnectionRemoteAudioSource::~PeerConnectionRemoteAudioSource() { | 
| +  DVLOG(1) | 
| +      << "PeerConnectionRemoteAudioSource::~PeerConnectionRemoteAudioSource()"; | 
| +  EnsureSourceIsStopped(); | 
| +} | 
| + | 
| +std::unique_ptr<MediaStreamAudioTrack> | 
| +PeerConnectionRemoteAudioSource::CreateMediaStreamAudioTrack( | 
| +    const std::string& id) { | 
| +  DCHECK(thread_checker_.CalledOnValidThread()); | 
| +  return std::unique_ptr<MediaStreamAudioTrack>( | 
| +      new PeerConnectionRemoteAudioTrack(track_interface_)); | 
| +} | 
| + | 
| +bool PeerConnectionRemoteAudioSource::EnsureSourceIsStarted() { | 
| +  DCHECK(thread_checker_.CalledOnValidThread()); | 
| +  if (is_sink_of_peer_connection_) | 
| +    return true; | 
| +  VLOG(1) << "Starting PeerConnection remote audio source with id=" | 
| +          << track_interface_->id(); | 
| +  track_interface_->AddSink(this); | 
| +  is_sink_of_peer_connection_ = true; | 
| +  return true; | 
| +} | 
| + | 
| +void PeerConnectionRemoteAudioSource::EnsureSourceIsStopped() { | 
| +  DCHECK(thread_checker_.CalledOnValidThread()); | 
| +  if (is_sink_of_peer_connection_) { | 
| +    track_interface_->RemoveSink(this); | 
| +    is_sink_of_peer_connection_ = false; | 
| +    VLOG(1) << "Stopped PeerConnection remote audio source with id=" | 
| +            << track_interface_->id(); | 
| +  } | 
| +} | 
| + | 
| +void PeerConnectionRemoteAudioSource::OnData(const void* audio_data, | 
| +                                             int bits_per_sample, | 
| +                                             int sample_rate, | 
| +                                             size_t number_of_channels, | 
| +                                             size_t number_of_frames) { | 
| +  // Debug builds: Note that this lock isn't meant to synchronize anything. | 
| +  // Instead, it is being used as a run-time check to ensure there isn't already | 
| +  // another thread executing this method. The reason we don't use | 
| +  // base::ThreadChecker here is because we shouldn't be making assumptions | 
| +  // about the private threading model of libjingle. For example, it would be | 
| +  // legitimate for libjingle to use a different thread to invoke this method | 
| +  // whenever the audio format changes. | 
| +#ifndef NDEBUG | 
| +  const bool is_only_thread_here = single_audio_thread_guard_.Try(); | 
| +  DCHECK(is_only_thread_here); | 
| +#endif | 
| + | 
| +  // TODO(tommi): We should get the timestamp from WebRTC. | 
| +  base::TimeTicks playout_time(base::TimeTicks::Now()); | 
| + | 
| +  if (!audio_bus_ || | 
| +      static_cast<size_t>(audio_bus_->channels()) != number_of_channels || | 
| +      static_cast<size_t>(audio_bus_->frames()) != number_of_frames) { | 
| +    audio_bus_ = media::AudioBus::Create(number_of_channels, number_of_frames); | 
| +  } | 
| + | 
| +  audio_bus_->FromInterleaved(audio_data, number_of_frames, | 
| +                              bits_per_sample / 8); | 
| + | 
| +  media::AudioParameters params = MediaStreamAudioSource::GetAudioParameters(); | 
| +  if (!params.IsValid() || | 
| +      params.format() != media::AudioParameters::AUDIO_PCM_LOW_LATENCY || | 
| +      static_cast<size_t>(params.channels()) != number_of_channels || | 
| +      params.sample_rate() != sample_rate || | 
| +      static_cast<size_t>(params.frames_per_buffer()) != number_of_frames) { | 
| +    MediaStreamAudioSource::SetFormat( | 
| +        media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | 
| +                               media::GuessChannelLayout(number_of_channels), | 
| +                               sample_rate, bits_per_sample, number_of_frames)); | 
| +  } | 
| + | 
| +  MediaStreamAudioSource::DeliverDataToTracks(*audio_bus_, playout_time); | 
| + | 
| +#ifndef NDEBUG | 
| +  single_audio_thread_guard_.Release(); | 
| +#endif | 
| +} | 
| + | 
| +}  // namespace content | 
|  |