blob: 70ff0b206f8a6f825b402059f9352c5b9601513c [file] [log] [blame]
// Copyright (c) 2013 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/remote_media_stream_impl.h"
#include <stddef.h>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/renderer/media/media_stream.h"
#include "content/renderer/media/media_stream_track.h"
#include "content/renderer/media/media_stream_video_track.h"
#include "content/renderer/media/webrtc/media_stream_remote_video_source.h"
#include "content/renderer/media/webrtc/peer_connection_dependency_factory.h"
#include "content/renderer/media/webrtc/peer_connection_remote_audio_source.h"
#include "content/renderer/media/webrtc/track_observer.h"
#include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
#include "third_party/WebKit/public/platform/WebString.h"
namespace content {
namespace {
template <typename WebRtcTrackVector, typename AdapterType>
void CreateAdaptersForTracks(
const WebRtcTrackVector& tracks,
std::vector<scoped_refptr<AdapterType>>* observers,
const scoped_refptr<base::SingleThreadTaskRunner>& main_thread) {
for (auto& track : tracks)
observers->push_back(new AdapterType(main_thread, track));
}
template<typename VectorType>
bool IsTrackInVector(const VectorType& v, const std::string& id) {
for (const auto& t : v) {
if (t->id() == id)
return true;
}
return false;
}
} // namespace
// Base class used for mapping between webrtc and blink MediaStream tracks.
// An instance of a RemoteMediaStreamTrackAdapter is stored in
// RemoteMediaStreamImpl per remote audio and video track.
template<typename WebRtcMediaStreamTrackType>
class RemoteMediaStreamTrackAdapter
: public base::RefCountedThreadSafe<
RemoteMediaStreamTrackAdapter<WebRtcMediaStreamTrackType>> {
public:
RemoteMediaStreamTrackAdapter(
const scoped_refptr<base::SingleThreadTaskRunner>& main_thread,
WebRtcMediaStreamTrackType* webrtc_track)
: main_thread_(main_thread), webrtc_track_(webrtc_track),
id_(webrtc_track->id()) {
}
const scoped_refptr<WebRtcMediaStreamTrackType>& observed_track() {
return webrtc_track_;
}
blink::WebMediaStreamTrack* webkit_track() {
DCHECK(main_thread_->BelongsToCurrentThread());
DCHECK(!webkit_track_.isNull());
return &webkit_track_;
}
const std::string& id() const { return id_; }
bool initialized() const {
DCHECK(main_thread_->BelongsToCurrentThread());
return !webkit_track_.isNull();
}
void Initialize() {
DCHECK(main_thread_->BelongsToCurrentThread());
DCHECK(!initialized());
webkit_initialize_.Run();
webkit_initialize_.Reset();
DCHECK(initialized());
}
protected:
friend class base::RefCountedThreadSafe<
RemoteMediaStreamTrackAdapter<WebRtcMediaStreamTrackType>>;
virtual ~RemoteMediaStreamTrackAdapter() {
DCHECK(main_thread_->BelongsToCurrentThread());
}
void InitializeWebkitTrack(blink::WebMediaStreamSource::Type type) {
DCHECK(main_thread_->BelongsToCurrentThread());
DCHECK(webkit_track_.isNull());
blink::WebString webkit_track_id(blink::WebString::fromUTF8(id_));
blink::WebMediaStreamSource webkit_source;
webkit_source.initialize(webkit_track_id, type, webkit_track_id);
webkit_track_.initialize(webkit_track_id, webkit_source);
DCHECK(!webkit_track_.isNull());
}
const scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
// This callback will be run when Initialize() is called and then freed.
// The callback is used by derived classes to bind objects that need to be
// instantiated and initialized on the signaling thread but then moved to
// and used on the main thread when initializing the webkit object(s).
base::Callback<void()> webkit_initialize_;
private:
const scoped_refptr<WebRtcMediaStreamTrackType> webrtc_track_;
blink::WebMediaStreamTrack webkit_track_;
// const copy of the webrtc track id that allows us to check it from both the
// main and signaling threads without incurring a synchronous thread hop.
const std::string id_;
DISALLOW_COPY_AND_ASSIGN(RemoteMediaStreamTrackAdapter);
};
class RemoteVideoTrackAdapter
: public RemoteMediaStreamTrackAdapter<webrtc::VideoTrackInterface> {
public:
// Called on the signaling thread
RemoteVideoTrackAdapter(
const scoped_refptr<base::SingleThreadTaskRunner>& main_thread,
webrtc::VideoTrackInterface* webrtc_track)
: RemoteMediaStreamTrackAdapter(main_thread, webrtc_track) {
std::unique_ptr<TrackObserver> observer(
new TrackObserver(main_thread, observed_track().get()));
// Here, we use base::Unretained() to avoid a circular reference.
webkit_initialize_ = base::Bind(
&RemoteVideoTrackAdapter::InitializeWebkitVideoTrack,
base::Unretained(this), base::Passed(&observer),
observed_track()->enabled());
}
protected:
~RemoteVideoTrackAdapter() override {
DCHECK(main_thread_->BelongsToCurrentThread());
if (initialized()) {
static_cast<MediaStreamRemoteVideoSource*>(
webkit_track()->source().getExtraData())
->OnSourceTerminated();
}
}
private:
void InitializeWebkitVideoTrack(std::unique_ptr<TrackObserver> observer,
bool enabled) {
DCHECK(main_thread_->BelongsToCurrentThread());
std::unique_ptr<MediaStreamRemoteVideoSource> video_source(
new MediaStreamRemoteVideoSource(std::move(observer)));
InitializeWebkitTrack(blink::WebMediaStreamSource::TypeVideo);
webkit_track()->source().setExtraData(video_source.get());
// Initial constraints must be provided to a MediaStreamVideoTrack. But
// no constraints are available initially on a remote video track.
blink::WebMediaConstraints constraints;
constraints.initialize();
MediaStreamVideoTrack* media_stream_track =
new MediaStreamVideoTrack(video_source.release(), constraints,
MediaStreamVideoSource::ConstraintsCallback(), enabled);
webkit_track()->setTrackData(media_stream_track);
}
};
// RemoteAudioTrackAdapter is responsible for listening on state
// change notifications on a remote webrtc audio MediaStreamTracks and notify
// WebKit.
class RemoteAudioTrackAdapter
: public RemoteMediaStreamTrackAdapter<webrtc::AudioTrackInterface>,
public webrtc::ObserverInterface {
public:
RemoteAudioTrackAdapter(
const scoped_refptr<base::SingleThreadTaskRunner>& main_thread,
webrtc::AudioTrackInterface* webrtc_track);
void Unregister();
protected:
~RemoteAudioTrackAdapter() override;
private:
void InitializeWebkitAudioTrack();
// webrtc::ObserverInterface implementation.
void OnChanged() override;
void OnChangedOnMainThread(
webrtc::MediaStreamTrackInterface::TrackState state);
#if DCHECK_IS_ON()
bool unregistered_;
#endif
webrtc::MediaStreamTrackInterface::TrackState state_;
DISALLOW_COPY_AND_ASSIGN(RemoteAudioTrackAdapter);
};
// Called on the signaling thread.
RemoteAudioTrackAdapter::RemoteAudioTrackAdapter(
const scoped_refptr<base::SingleThreadTaskRunner>& main_thread,
webrtc::AudioTrackInterface* webrtc_track)
: RemoteMediaStreamTrackAdapter(main_thread, webrtc_track),
#if DCHECK_IS_ON()
unregistered_(false),
#endif
state_(observed_track()->state()) {
// TODO(tommi): Use TrackObserver instead.
observed_track()->RegisterObserver(this);
// Here, we use base::Unretained() to avoid a circular reference.
webkit_initialize_ =
base::Bind(&RemoteAudioTrackAdapter::InitializeWebkitAudioTrack,
base::Unretained(this));
}
RemoteAudioTrackAdapter::~RemoteAudioTrackAdapter() {
#if DCHECK_IS_ON()
DCHECK(unregistered_);
#endif
}
void RemoteAudioTrackAdapter::Unregister() {
#if DCHECK_IS_ON()
DCHECK(!unregistered_);
unregistered_ = true;
#endif
observed_track()->UnregisterObserver(this);
}
void RemoteAudioTrackAdapter::InitializeWebkitAudioTrack() {
InitializeWebkitTrack(blink::WebMediaStreamSource::TypeAudio);
MediaStreamAudioSource* const source =
new PeerConnectionRemoteAudioSource(observed_track().get());
webkit_track()->source().setExtraData(source); // Takes ownership.
source->ConnectToTrack(*(webkit_track()));
}
void RemoteAudioTrackAdapter::OnChanged() {
main_thread_->PostTask(FROM_HERE,
base::Bind(&RemoteAudioTrackAdapter::OnChangedOnMainThread,
this, observed_track()->state()));
}
void RemoteAudioTrackAdapter::OnChangedOnMainThread(
webrtc::MediaStreamTrackInterface::TrackState state) {
DCHECK(main_thread_->BelongsToCurrentThread());
if (state == state_ || !initialized())
return;
state_ = state;
switch (state) {
case webrtc::MediaStreamTrackInterface::kLive:
webkit_track()->source().setReadyState(
blink::WebMediaStreamSource::ReadyStateLive);
break;
case webrtc::MediaStreamTrackInterface::kEnded:
webkit_track()->source().setReadyState(
blink::WebMediaStreamSource::ReadyStateEnded);
break;
default:
NOTREACHED();
break;
}
}
RemoteMediaStreamImpl::Observer::Observer(
const base::WeakPtr<RemoteMediaStreamImpl>& media_stream,
const scoped_refptr<base::SingleThreadTaskRunner>& main_thread,
webrtc::MediaStreamInterface* webrtc_stream)
: media_stream_(media_stream),
main_thread_(main_thread),
webrtc_stream_(webrtc_stream) {
webrtc_stream_->RegisterObserver(this);
}
RemoteMediaStreamImpl::Observer::~Observer() {
DCHECK(!webrtc_stream_.get()) << "Unregister hasn't been called";
}
void RemoteMediaStreamImpl::Observer::InitializeOnMainThread(
const std::string& label) {
DCHECK(main_thread_->BelongsToCurrentThread());
if (media_stream_)
media_stream_->InitializeOnMainThread(label);
}
void RemoteMediaStreamImpl::Observer::Unregister() {
DCHECK(main_thread_->BelongsToCurrentThread());
webrtc_stream_->UnregisterObserver(this);
// Since we're guaranteed to not get further notifications, it's safe to
// release the webrtc_stream_ here.
webrtc_stream_ = nullptr;
}
void RemoteMediaStreamImpl::Observer::OnChanged() {
std::unique_ptr<RemoteAudioTrackAdapters> audio(
new RemoteAudioTrackAdapters());
std::unique_ptr<RemoteVideoTrackAdapters> video(
new RemoteVideoTrackAdapters());
CreateAdaptersForTracks(
webrtc_stream_->GetAudioTracks(), audio.get(), main_thread_);
CreateAdaptersForTracks(
webrtc_stream_->GetVideoTracks(), video.get(), main_thread_);
main_thread_->PostTask(FROM_HERE,
base::Bind(&RemoteMediaStreamImpl::Observer::OnChangedOnMainThread,
this, base::Passed(&audio), base::Passed(&video)));
}
void RemoteMediaStreamImpl::Observer::OnChangedOnMainThread(
std::unique_ptr<RemoteAudioTrackAdapters> audio_tracks,
std::unique_ptr<RemoteVideoTrackAdapters> video_tracks) {
DCHECK(main_thread_->BelongsToCurrentThread());
if (media_stream_)
media_stream_->OnChanged(std::move(audio_tracks), std::move(video_tracks));
}
// Called on the signaling thread.
RemoteMediaStreamImpl::RemoteMediaStreamImpl(
const scoped_refptr<base::SingleThreadTaskRunner>& main_thread,
webrtc::MediaStreamInterface* webrtc_stream)
: signaling_thread_(base::ThreadTaskRunnerHandle::Get()),
weak_factory_(this) {
observer_ = new RemoteMediaStreamImpl::Observer(
weak_factory_.GetWeakPtr(), main_thread, webrtc_stream);
CreateAdaptersForTracks(webrtc_stream->GetAudioTracks(),
&audio_track_observers_, main_thread);
CreateAdaptersForTracks(webrtc_stream->GetVideoTracks(),
&video_track_observers_, main_thread);
main_thread->PostTask(FROM_HERE,
base::Bind(&RemoteMediaStreamImpl::Observer::InitializeOnMainThread,
observer_, webrtc_stream->label()));
}
RemoteMediaStreamImpl::~RemoteMediaStreamImpl() {
DCHECK(observer_->main_thread()->BelongsToCurrentThread());
for (auto& track : audio_track_observers_)
track->Unregister();
observer_->Unregister();
}
void RemoteMediaStreamImpl::InitializeOnMainThread(const std::string& label) {
DCHECK(observer_->main_thread()->BelongsToCurrentThread());
blink::WebVector<blink::WebMediaStreamTrack> webkit_audio_tracks(
audio_track_observers_.size());
for (size_t i = 0; i < audio_track_observers_.size(); ++i) {
audio_track_observers_[i]->Initialize();
webkit_audio_tracks[i] = *audio_track_observers_[i]->webkit_track();
}
blink::WebVector<blink::WebMediaStreamTrack> webkit_video_tracks(
video_track_observers_.size());
for (size_t i = 0; i < video_track_observers_.size(); ++i) {
video_track_observers_[i]->Initialize();
webkit_video_tracks[i] = *video_track_observers_[i]->webkit_track();
}
webkit_stream_.initialize(blink::WebString::fromUTF8(label),
webkit_audio_tracks, webkit_video_tracks);
webkit_stream_.setExtraData(new MediaStream());
}
void RemoteMediaStreamImpl::OnChanged(
std::unique_ptr<RemoteAudioTrackAdapters> audio_tracks,
std::unique_ptr<RemoteVideoTrackAdapters> video_tracks) {
// Find removed tracks.
auto audio_it = audio_track_observers_.begin();
while (audio_it != audio_track_observers_.end()) {
if (!IsTrackInVector(*audio_tracks.get(), (*audio_it)->id())) {
(*audio_it)->Unregister();
webkit_stream_.removeTrack(*(*audio_it)->webkit_track());
audio_it = audio_track_observers_.erase(audio_it);
} else {
++audio_it;
}
}
auto video_it = video_track_observers_.begin();
while (video_it != video_track_observers_.end()) {
if (!IsTrackInVector(*video_tracks.get(), (*video_it)->id())) {
webkit_stream_.removeTrack(*(*video_it)->webkit_track());
video_it = video_track_observers_.erase(video_it);
} else {
++video_it;
}
}
// Find added tracks.
for (auto& track : *audio_tracks.get()) {
if (!IsTrackInVector(audio_track_observers_, track->id())) {
track->Initialize();
audio_track_observers_.push_back(track);
webkit_stream_.addTrack(*track->webkit_track());
// Set the track to null to avoid unregistering it below now that it's
// been associated with a media stream.
track = nullptr;
}
}
// Find added video tracks.
for (const auto& track : *video_tracks.get()) {
if (!IsTrackInVector(video_track_observers_, track->id())) {
track->Initialize();
video_track_observers_.push_back(track);
webkit_stream_.addTrack(*track->webkit_track());
}
}
// Unregister all the audio track observers that were not used.
// We need to do this before destruction since the observers can't unregister
// from within the dtor due to a race.
for (auto& track : *audio_tracks.get()) {
if (track.get())
track->Unregister();
}
}
} // namespace content