Support for unmixed remote audio into tracks.
BUG=chromium:121673
R=solenberg@webrtc.org
Review URL: https://codereview.webrtc.org/1505253004 .
Cr-Commit-Position: refs/heads/master@{#10995}
diff --git a/talk/app/webrtc/mediastreaminterface.h b/talk/app/webrtc/mediastreaminterface.h
index 00782cb..5327bd2 100644
--- a/talk/app/webrtc/mediastreaminterface.h
+++ b/talk/app/webrtc/mediastreaminterface.h
@@ -167,6 +167,8 @@
// TODO(xians): Makes all the interface pure virtual after Chrome has their
// implementations.
// Sets the volume to the source. |volume| is in the range of [0, 10].
+ // TODO(tommi): This method should be on the track and ideally volume should
+ // be applied in the track in a way that does not affect clones of the track.
virtual void SetVolume(double volume) {}
// Registers/unregisters observer to the audio source.
diff --git a/talk/app/webrtc/mediastreamprovider.h b/talk/app/webrtc/mediastreamprovider.h
index a78b55a..585d51b 100644
--- a/talk/app/webrtc/mediastreamprovider.h
+++ b/talk/app/webrtc/mediastreamprovider.h
@@ -29,6 +29,7 @@
#define TALK_APP_WEBRTC_MEDIASTREAMPROVIDER_H_
#include "webrtc/base/basictypes.h"
+#include "webrtc/base/scoped_ptr.h"
namespace cricket {
@@ -42,6 +43,8 @@
namespace webrtc {
+class AudioSinkInterface;
+
// TODO(deadbeef): Change the key from an ssrc to a "sender_id" or
// "receiver_id" string, which will be the MSID in the short term and MID in
// the long term.
@@ -67,6 +70,13 @@
// |volume| is in the range of [0, 10].
virtual void SetAudioPlayoutVolume(uint32_t ssrc, double volume) = 0;
+ // Allows for setting a direct audio sink for an incoming audio source.
+ // Only one audio sink is supported per ssrc and ownership of the sink is
+ // passed to the provider.
+ virtual void SetRawAudioSink(
+ uint32_t ssrc,
+ rtc::scoped_ptr<webrtc::AudioSinkInterface> sink) = 0;
+
protected:
virtual ~AudioProviderInterface() {}
};
diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc
index 933dc83..83e2919 100644
--- a/talk/app/webrtc/peerconnection.cc
+++ b/talk/app/webrtc/peerconnection.cc
@@ -39,6 +39,7 @@
#include "talk/app/webrtc/mediastreamproxy.h"
#include "talk/app/webrtc/mediastreamtrackproxy.h"
#include "talk/app/webrtc/remoteaudiosource.h"
+#include "talk/app/webrtc/remoteaudiotrack.h"
#include "talk/app/webrtc/remotevideocapturer.h"
#include "talk/app/webrtc/rtpreceiver.h"
#include "talk/app/webrtc/rtpsender.h"
@@ -448,10 +449,12 @@
MediaStream::Create(stream_label));
}
- AudioTrackInterface* AddAudioTrack(webrtc::MediaStreamInterface* stream,
+ AudioTrackInterface* AddAudioTrack(uint32_t ssrc,
+ AudioProviderInterface* provider,
+ webrtc::MediaStreamInterface* stream,
const std::string& track_id) {
- return AddTrack<AudioTrackInterface, AudioTrack, AudioTrackProxy>(
- stream, track_id, RemoteAudioSource::Create().get());
+ return AddTrack<AudioTrackInterface, RemoteAudioTrack, AudioTrackProxy>(
+ stream, track_id, RemoteAudioSource::Create(ssrc, provider));
}
VideoTrackInterface* AddVideoTrack(webrtc::MediaStreamInterface* stream,
@@ -467,7 +470,7 @@
template <typename TI, typename T, typename TP, typename S>
TI* AddTrack(MediaStreamInterface* stream,
const std::string& track_id,
- S* source) {
+ const S& source) {
rtc::scoped_refptr<TI> track(
TP::Create(signaling_thread_, T::Create(track_id, source)));
track->set_state(webrtc::MediaStreamTrackInterface::kLive);
@@ -1583,8 +1586,8 @@
MediaStreamInterface* stream = remote_streams_->find(stream_label);
if (media_type == cricket::MEDIA_TYPE_AUDIO) {
- AudioTrackInterface* audio_track =
- remote_stream_factory_->AddAudioTrack(stream, track_id);
+ AudioTrackInterface* audio_track = remote_stream_factory_->AddAudioTrack(
+ ssrc, session_.get(), stream, track_id);
CreateAudioReceiver(stream, audio_track, ssrc);
} else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
VideoTrackInterface* video_track =
diff --git a/talk/app/webrtc/remoteaudiosource.cc b/talk/app/webrtc/remoteaudiosource.cc
index 41f3d87..e5af1e9 100644
--- a/talk/app/webrtc/remoteaudiosource.cc
+++ b/talk/app/webrtc/remoteaudiosource.cc
@@ -29,44 +29,143 @@
#include <algorithm>
#include <functional>
+#include <utility>
+#include "talk/app/webrtc/mediastreamprovider.h"
+#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
+#include "webrtc/base/thread.h"
namespace webrtc {
-rtc::scoped_refptr<RemoteAudioSource> RemoteAudioSource::Create() {
- return new rtc::RefCountedObject<RemoteAudioSource>();
+class RemoteAudioSource::MessageHandler : public rtc::MessageHandler {
+ public:
+ explicit MessageHandler(RemoteAudioSource* source) : source_(source) {}
+
+ private:
+ ~MessageHandler() override {}
+
+ void OnMessage(rtc::Message* msg) override {
+ source_->OnMessage(msg);
+ delete this;
+ }
+
+ const rtc::scoped_refptr<RemoteAudioSource> source_;
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(MessageHandler);
+};
+
+class RemoteAudioSource::Sink : public AudioSinkInterface {
+ public:
+ explicit Sink(RemoteAudioSource* source) : source_(source) {}
+ ~Sink() override { source_->OnAudioProviderGone(); }
+
+ private:
+ void OnData(const AudioSinkInterface::Data& audio) override {
+ if (source_)
+ source_->OnData(audio);
+ }
+
+ const rtc::scoped_refptr<RemoteAudioSource> source_;
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Sink);
+};
+
+rtc::scoped_refptr<RemoteAudioSource> RemoteAudioSource::Create(
+ uint32_t ssrc,
+ AudioProviderInterface* provider) {
+ rtc::scoped_refptr<RemoteAudioSource> ret(
+ new rtc::RefCountedObject<RemoteAudioSource>());
+ ret->Initialize(ssrc, provider);
+ return ret;
}
-RemoteAudioSource::RemoteAudioSource() {
+RemoteAudioSource::RemoteAudioSource()
+ : main_thread_(rtc::Thread::Current()),
+ state_(MediaSourceInterface::kLive) {
+ RTC_DCHECK(main_thread_);
}
RemoteAudioSource::~RemoteAudioSource() {
- ASSERT(audio_observers_.empty());
+ RTC_DCHECK(main_thread_->IsCurrent());
+ RTC_DCHECK(audio_observers_.empty());
+ RTC_DCHECK(sinks_.empty());
}
-MediaSourceInterface::SourceState RemoteAudioSource::state() const {
- return MediaSourceInterface::kLive;
-}
-
-void RemoteAudioSource::SetVolume(double volume) {
- ASSERT(volume >= 0 && volume <= 10);
- for (AudioObserverList::iterator it = audio_observers_.begin();
- it != audio_observers_.end(); ++it) {
- (*it)->OnSetVolume(volume);
+void RemoteAudioSource::Initialize(uint32_t ssrc,
+ AudioProviderInterface* provider) {
+ RTC_DCHECK(main_thread_->IsCurrent());
+ // To make sure we always get notified when the provider goes out of scope,
+ // we register for callbacks here and not on demand in AddSink.
+ if (provider) { // May be null in tests.
+ provider->SetRawAudioSink(
+ ssrc, std::move(rtc::scoped_ptr<AudioSinkInterface>(new Sink(this))));
}
}
+MediaSourceInterface::SourceState RemoteAudioSource::state() const {
+ RTC_DCHECK(main_thread_->IsCurrent());
+ return state_;
+}
+
+void RemoteAudioSource::SetVolume(double volume) {
+ RTC_DCHECK(volume >= 0 && volume <= 10);
+ for (auto* observer : audio_observers_)
+ observer->OnSetVolume(volume);
+}
+
void RemoteAudioSource::RegisterAudioObserver(AudioObserver* observer) {
- ASSERT(observer != NULL);
- ASSERT(std::find(audio_observers_.begin(), audio_observers_.end(),
- observer) == audio_observers_.end());
+ RTC_DCHECK(observer != NULL);
+ RTC_DCHECK(std::find(audio_observers_.begin(), audio_observers_.end(),
+ observer) == audio_observers_.end());
audio_observers_.push_back(observer);
}
void RemoteAudioSource::UnregisterAudioObserver(AudioObserver* observer) {
- ASSERT(observer != NULL);
+ RTC_DCHECK(observer != NULL);
audio_observers_.remove(observer);
}
+void RemoteAudioSource::AddSink(AudioTrackSinkInterface* sink) {
+ RTC_DCHECK(main_thread_->IsCurrent());
+ RTC_DCHECK(sink);
+
+ if (state_ != MediaSourceInterface::kLive) {
+ LOG(LS_ERROR) << "Can't register sink as the source isn't live.";
+ return;
+ }
+
+ rtc::CritScope lock(&sink_lock_);
+ RTC_DCHECK(std::find(sinks_.begin(), sinks_.end(), sink) == sinks_.end());
+ sinks_.push_back(sink);
+}
+
+void RemoteAudioSource::RemoveSink(AudioTrackSinkInterface* sink) {
+ RTC_DCHECK(main_thread_->IsCurrent());
+ RTC_DCHECK(sink);
+
+ rtc::CritScope lock(&sink_lock_);
+ sinks_.remove(sink);
+}
+
+void RemoteAudioSource::OnData(const AudioSinkInterface::Data& audio) {
+ // Called on the externally-owned audio callback thread, via/from webrtc.
+ rtc::CritScope lock(&sink_lock_);
+ for (auto* sink : sinks_) {
+ sink->OnData(audio.data, 16, audio.sample_rate, audio.channels,
+ audio.samples_per_channel);
+ }
+}
+
+void RemoteAudioSource::OnAudioProviderGone() {
+ // Called when the data provider is deleted. It may be the worker thread
+ // in libjingle or may be a different worker thread.
+ main_thread_->Post(new MessageHandler(this));
+}
+
+void RemoteAudioSource::OnMessage(rtc::Message* msg) {
+ RTC_DCHECK(main_thread_->IsCurrent());
+ sinks_.clear();
+ state_ = MediaSourceInterface::kEnded;
+ FireOnChanged();
+}
+
} // namespace webrtc
diff --git a/talk/app/webrtc/remoteaudiosource.h b/talk/app/webrtc/remoteaudiosource.h
index e49aca5..f518d9b 100644
--- a/talk/app/webrtc/remoteaudiosource.h
+++ b/talk/app/webrtc/remoteaudiosource.h
@@ -29,36 +29,65 @@
#define TALK_APP_WEBRTC_REMOTEAUDIOSOURCE_H_
#include <list>
+#include <string>
#include "talk/app/webrtc/mediastreaminterface.h"
#include "talk/app/webrtc/notifier.h"
+#include "talk/media/base/audiorenderer.h"
+#include "webrtc/audio/audio_sink.h"
+#include "webrtc/base/criticalsection.h"
+
+namespace rtc {
+struct Message;
+class Thread;
+} // namespace rtc
namespace webrtc {
-using webrtc::AudioSourceInterface;
+class AudioProviderInterface;
// This class implements the audio source used by the remote audio track.
class RemoteAudioSource : public Notifier<AudioSourceInterface> {
public:
// Creates an instance of RemoteAudioSource.
- static rtc::scoped_refptr<RemoteAudioSource> Create();
-
- protected:
- RemoteAudioSource();
- virtual ~RemoteAudioSource();
-
- private:
- typedef std::list<AudioObserver*> AudioObserverList;
+ static rtc::scoped_refptr<RemoteAudioSource> Create(
+ uint32_t ssrc,
+ AudioProviderInterface* provider);
// MediaSourceInterface implementation.
MediaSourceInterface::SourceState state() const override;
+ void AddSink(AudioTrackSinkInterface* sink);
+ void RemoveSink(AudioTrackSinkInterface* sink);
+
+ protected:
+ RemoteAudioSource();
+ ~RemoteAudioSource() override;
+
+ // Post construction initialize where we can do things like save a reference
+ // to ourselves (need to be fully constructed).
+ void Initialize(uint32_t ssrc, AudioProviderInterface* provider);
+
+ private:
+ typedef std::list<AudioObserver*> AudioObserverList;
+
// AudioSourceInterface implementation.
void SetVolume(double volume) override;
void RegisterAudioObserver(AudioObserver* observer) override;
void UnregisterAudioObserver(AudioObserver* observer) override;
+ class Sink;
+ void OnData(const AudioSinkInterface::Data& audio);
+ void OnAudioProviderGone();
+
+ class MessageHandler;
+ void OnMessage(rtc::Message* msg);
+
AudioObserverList audio_observers_;
+ rtc::CriticalSection sink_lock_;
+ std::list<AudioTrackSinkInterface*> sinks_;
+ rtc::Thread* const main_thread_;
+ SourceState state_;
};
} // namespace webrtc
diff --git a/talk/app/webrtc/remoteaudiotrack.cc b/talk/app/webrtc/remoteaudiotrack.cc
index df27f05..2c4481c 100644
--- a/talk/app/webrtc/remoteaudiotrack.cc
+++ b/talk/app/webrtc/remoteaudiotrack.cc
@@ -26,3 +26,70 @@
*/
#include "talk/app/webrtc/remoteaudiotrack.h"
+
+#include "talk/app/webrtc/remoteaudiosource.h"
+
+using rtc::scoped_refptr;
+
+namespace webrtc {
+
+// static
+scoped_refptr<RemoteAudioTrack> RemoteAudioTrack::Create(
+ const std::string& id,
+ const scoped_refptr<RemoteAudioSource>& source) {
+ return new rtc::RefCountedObject<RemoteAudioTrack>(id, source);
+}
+
+RemoteAudioTrack::RemoteAudioTrack(
+ const std::string& label,
+ const scoped_refptr<RemoteAudioSource>& source)
+ : MediaStreamTrack<AudioTrackInterface>(label), audio_source_(source) {
+ audio_source_->RegisterObserver(this);
+ TrackState new_state = kInitializing;
+ switch (audio_source_->state()) {
+ case MediaSourceInterface::kLive:
+ case MediaSourceInterface::kMuted:
+ new_state = kLive;
+ break;
+ case MediaSourceInterface::kEnded:
+ new_state = kEnded;
+ break;
+ case MediaSourceInterface::kInitializing:
+ default:
+ // kInitializing;
+ break;
+ }
+ set_state(new_state);
+}
+
+RemoteAudioTrack::~RemoteAudioTrack() {
+ set_state(MediaStreamTrackInterface::kEnded);
+ audio_source_->UnregisterObserver(this);
+}
+
+std::string RemoteAudioTrack::kind() const {
+ return MediaStreamTrackInterface::kAudioKind;
+}
+
+AudioSourceInterface* RemoteAudioTrack::GetSource() const {
+ return audio_source_.get();
+}
+
+void RemoteAudioTrack::AddSink(AudioTrackSinkInterface* sink) {
+ audio_source_->AddSink(sink);
+}
+
+void RemoteAudioTrack::RemoveSink(AudioTrackSinkInterface* sink) {
+ audio_source_->RemoveSink(sink);
+}
+
+bool RemoteAudioTrack::GetSignalLevel(int* level) {
+ return false;
+}
+
+void RemoteAudioTrack::OnChanged() {
+ if (audio_source_->state() == MediaSourceInterface::kEnded)
+ set_state(MediaStreamTrackInterface::kEnded);
+}
+
+} // namespace webrtc
diff --git a/talk/app/webrtc/remoteaudiotrack.h b/talk/app/webrtc/remoteaudiotrack.h
index 4ce4e79..c9240eb 100644
--- a/talk/app/webrtc/remoteaudiotrack.h
+++ b/talk/app/webrtc/remoteaudiotrack.h
@@ -28,4 +28,50 @@
#ifndef TALK_APP_WEBRTC_REMOTEAUDIOTRACK_H_
#define TALK_APP_WEBRTC_REMOTEAUDIOTRACK_H_
+#include <string>
+
+#include "talk/app/webrtc/mediastreaminterface.h"
+#include "talk/app/webrtc/mediastreamtrack.h"
+#include "talk/app/webrtc/notifier.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+
+namespace webrtc {
+
+class RemoteAudioSource;
+
+class RemoteAudioTrack : public MediaStreamTrack<AudioTrackInterface>,
+ public ObserverInterface {
+ protected:
+ // Protected ctor to force use of factory method.
+ RemoteAudioTrack(const std::string& label,
+ const rtc::scoped_refptr<RemoteAudioSource>& source);
+ ~RemoteAudioTrack() override;
+
+ public:
+ static rtc::scoped_refptr<RemoteAudioTrack> Create(
+ const std::string& id,
+ const rtc::scoped_refptr<RemoteAudioSource>& source);
+
+ private:
+ // MediaStreamTrack implementation.
+ std::string kind() const override;
+
+ // AudioTrackInterface implementation.
+ AudioSourceInterface* GetSource() const override;
+
+ void AddSink(AudioTrackSinkInterface* sink) override;
+ void RemoveSink(AudioTrackSinkInterface* sink) override;
+ bool GetSignalLevel(int* level) override;
+
+ // ObserverInterface implementation.
+ void OnChanged() override;
+
+ private:
+ const rtc::scoped_refptr<RemoteAudioSource> audio_source_;
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RemoteAudioTrack);
+};
+
+} // namespace webrtc
+
#endif // TALK_APP_WEBRTC_REMOTEAUDIOTRACK_H_
diff --git a/talk/app/webrtc/rtpreceiver.h b/talk/app/webrtc/rtpreceiver.h
index a93ccbc..db021ba 100644
--- a/talk/app/webrtc/rtpreceiver.h
+++ b/talk/app/webrtc/rtpreceiver.h
@@ -68,10 +68,10 @@
private:
void Reconfigure();
- std::string id_;
- rtc::scoped_refptr<AudioTrackInterface> track_;
- uint32_t ssrc_;
- AudioProviderInterface* provider_;
+ const std::string id_;
+ const rtc::scoped_refptr<AudioTrackInterface> track_;
+ const uint32_t ssrc_;
+ AudioProviderInterface* provider_; // Set to null in Stop().
bool cached_track_enabled_;
};
diff --git a/talk/app/webrtc/rtpsenderreceiver_unittest.cc b/talk/app/webrtc/rtpsenderreceiver_unittest.cc
index c987186..3f61504 100644
--- a/talk/app/webrtc/rtpsenderreceiver_unittest.cc
+++ b/talk/app/webrtc/rtpsenderreceiver_unittest.cc
@@ -26,10 +26,12 @@
*/
#include <string>
+#include <utility>
#include "talk/app/webrtc/audiotrack.h"
#include "talk/app/webrtc/mediastream.h"
#include "talk/app/webrtc/remoteaudiosource.h"
+#include "talk/app/webrtc/remoteaudiotrack.h"
#include "talk/app/webrtc/rtpreceiver.h"
#include "talk/app/webrtc/rtpsender.h"
#include "talk/app/webrtc/streamcollection.h"
@@ -57,7 +59,8 @@
// Helper class to test RtpSender/RtpReceiver.
class MockAudioProvider : public AudioProviderInterface {
public:
- virtual ~MockAudioProvider() {}
+ ~MockAudioProvider() override {}
+
MOCK_METHOD2(SetAudioPlayout,
void(uint32_t ssrc,
bool enable));
@@ -67,6 +70,14 @@
const cricket::AudioOptions& options,
cricket::AudioRenderer* renderer));
MOCK_METHOD2(SetAudioPlayoutVolume, void(uint32_t ssrc, double volume));
+
+ void SetRawAudioSink(uint32_t,
+ rtc::scoped_ptr<AudioSinkInterface> sink) override {
+ sink_ = std::move(sink);
+ }
+
+ private:
+ rtc::scoped_ptr<AudioSinkInterface> sink_;
};
// Helper class to test RtpSender/RtpReceiver.
@@ -151,8 +162,8 @@
}
void CreateAudioRtpReceiver() {
- audio_track_ =
- AudioTrack::Create(kAudioTrackId, RemoteAudioSource::Create().get());
+ audio_track_ = RemoteAudioTrack::Create(
+ kAudioTrackId, RemoteAudioSource::Create(kAudioSsrc, NULL));
EXPECT_TRUE(stream_->AddTrack(audio_track_));
EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, true));
audio_rtp_receiver_ = new AudioRtpReceiver(stream_->GetAudioTracks()[0],
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index 015531d..5e946a2 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -30,8 +30,9 @@
#include <limits.h>
#include <algorithm>
-#include <vector>
#include <set>
+#include <utility>
+#include <vector>
#include "talk/app/webrtc/jsepicecandidate.h"
#include "talk/app/webrtc/jsepsessiondescription.h"
@@ -44,6 +45,7 @@
#include "talk/session/media/channel.h"
#include "talk/session/media/channelmanager.h"
#include "talk/session/media/mediasession.h"
+#include "webrtc/audio/audio_sink.h"
#include "webrtc/base/basictypes.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/helpers.h"
@@ -1318,6 +1320,15 @@
}
}
+void WebRtcSession::SetRawAudioSink(uint32_t ssrc,
+ rtc::scoped_ptr<AudioSinkInterface> sink) {
+ ASSERT(signaling_thread()->IsCurrent());
+ if (!voice_channel_)
+ return;
+
+ voice_channel_->SetRawAudioSink(ssrc, std::move(sink));
+}
+
bool WebRtcSession::SetCaptureDevice(uint32_t ssrc,
cricket::VideoCapturer* camera) {
ASSERT(signaling_thread()->IsCurrent());
diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h
index d9c40d1..dd5ca18 100644
--- a/talk/app/webrtc/webrtcsession.h
+++ b/talk/app/webrtc/webrtcsession.h
@@ -38,11 +38,11 @@
#include "talk/app/webrtc/peerconnectioninterface.h"
#include "talk/app/webrtc/statstypes.h"
#include "talk/media/base/mediachannel.h"
-#include "webrtc/p2p/base/transportcontroller.h"
#include "talk/session/media/mediasession.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/sslidentity.h"
#include "webrtc/base/thread.h"
+#include "webrtc/p2p/base/transportcontroller.h"
namespace cricket {
@@ -250,6 +250,8 @@
const cricket::AudioOptions& options,
cricket::AudioRenderer* renderer) override;
void SetAudioPlayoutVolume(uint32_t ssrc, double volume) override;
+ void SetRawAudioSink(uint32_t ssrc,
+ rtc::scoped_ptr<AudioSinkInterface> sink) override;
// Implements VideoMediaProviderInterface.
bool SetCaptureDevice(uint32_t ssrc, cricket::VideoCapturer* camera) override;
diff --git a/talk/media/base/fakemediaengine.h b/talk/media/base/fakemediaengine.h
index 9dbb649..f5b2174 100644
--- a/talk/media/base/fakemediaengine.h
+++ b/talk/media/base/fakemediaengine.h
@@ -38,9 +38,10 @@
#include "talk/media/base/mediaengine.h"
#include "talk/media/base/rtputils.h"
#include "talk/media/base/streamparams.h"
-#include "webrtc/p2p/base/sessiondescription.h"
+#include "webrtc/audio/audio_sink.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/stringutils.h"
+#include "webrtc/p2p/base/sessiondescription.h"
namespace cricket {
@@ -346,6 +347,12 @@
virtual bool GetStats(VoiceMediaInfo* info) { return false; }
+ virtual void SetRawAudioSink(
+ uint32_t ssrc,
+ rtc::scoped_ptr<webrtc::AudioSinkInterface> sink) {
+ sink_ = std::move(sink);
+ }
+
private:
class VoiceChannelAudioSink : public AudioRenderer::Sink {
public:
@@ -418,6 +425,7 @@
int time_since_last_typing_;
AudioOptions options_;
std::map<uint32_t, VoiceChannelAudioSink*> local_renderers_;
+ rtc::scoped_ptr<webrtc::AudioSinkInterface> sink_;
};
// A helper function to compare the FakeVoiceMediaChannel::DtmfInfo.
diff --git a/talk/media/base/mediachannel.h b/talk/media/base/mediachannel.h
index fe223bb..44b9d4f 100644
--- a/talk/media/base/mediachannel.h
+++ b/talk/media/base/mediachannel.h
@@ -31,6 +31,7 @@
#include <string>
#include <vector>
+#include "talk/media/base/audiorenderer.h"
#include "talk/media/base/codec.h"
#include "talk/media/base/constants.h"
#include "talk/media/base/streamparams.h"
@@ -51,9 +52,12 @@
class Timing;
}
+namespace webrtc {
+class AudioSinkInterface;
+}
+
namespace cricket {
-class AudioRenderer;
struct RtpHeader;
class ScreencastId;
struct VideoFormat;
@@ -1028,6 +1032,10 @@
virtual bool InsertDtmf(uint32_t ssrc, int event, int duration) = 0;
// Gets quality stats for the channel.
virtual bool GetStats(VoiceMediaInfo* info) = 0;
+
+ virtual void SetRawAudioSink(
+ uint32_t ssrc,
+ rtc::scoped_ptr<webrtc::AudioSinkInterface> sink) = 0;
};
struct VideoSendParameters : RtpSendParameters<VideoCodec, VideoOptions> {
diff --git a/talk/media/webrtc/fakewebrtccall.cc b/talk/media/webrtc/fakewebrtccall.cc
index bf51fb3..d50a53c 100644
--- a/talk/media/webrtc/fakewebrtccall.cc
+++ b/talk/media/webrtc/fakewebrtccall.cc
@@ -28,10 +28,12 @@
#include "talk/media/webrtc/fakewebrtccall.h"
#include <algorithm>
+#include <utility>
#include "talk/media/base/rtputils.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/gunit.h"
+#include "webrtc/audio/audio_sink.h"
namespace cricket {
FakeAudioSendStream::FakeAudioSendStream(
@@ -90,6 +92,11 @@
return stats_;
}
+void FakeAudioReceiveStream::SetSink(
+ rtc::scoped_ptr<webrtc::AudioSinkInterface> sink) {
+ sink_ = std::move(sink);
+}
+
FakeVideoSendStream::FakeVideoSendStream(
const webrtc::VideoSendStream::Config& config,
const webrtc::VideoEncoderConfig& encoder_config)
diff --git a/talk/media/webrtc/fakewebrtccall.h b/talk/media/webrtc/fakewebrtccall.h
index 024c50d..3528c7a 100644
--- a/talk/media/webrtc/fakewebrtccall.h
+++ b/talk/media/webrtc/fakewebrtccall.h
@@ -106,10 +106,12 @@
// webrtc::AudioReceiveStream implementation.
webrtc::AudioReceiveStream::Stats GetStats() const override;
+ void SetSink(rtc::scoped_ptr<webrtc::AudioSinkInterface> sink) override;
webrtc::AudioReceiveStream::Config config_;
webrtc::AudioReceiveStream::Stats stats_;
int received_packets_;
+ rtc::scoped_ptr<webrtc::AudioSinkInterface> sink_;
};
class FakeVideoSendStream final : public webrtc::VideoSendStream,
diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc
index 0b5bed1..2634a24 100644
--- a/talk/media/webrtc/webrtcvoiceengine.cc
+++ b/talk/media/webrtc/webrtcvoiceengine.cc
@@ -44,6 +44,7 @@
#include "talk/media/base/streamparams.h"
#include "talk/media/webrtc/webrtcmediaengine.h"
#include "talk/media/webrtc/webrtcvoe.h"
+#include "webrtc/audio/audio_sink.h"
#include "webrtc/base/arraysize.h"
#include "webrtc/base/base64.h"
#include "webrtc/base/byteorder.h"
@@ -1248,6 +1249,11 @@
return config_.voe_channel_id;
}
+ void SetRawAudioSink(rtc::scoped_ptr<webrtc::AudioSinkInterface> sink) {
+ RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+ stream_->SetSink(std::move(sink));
+ }
+
private:
void RecreateAudioReceiveStream(bool use_combined_bwe,
const std::vector<webrtc::RtpExtension>& extensions) {
@@ -2032,6 +2038,7 @@
// Clean up and delete the receive stream+channel.
LOG(LS_INFO) << "Removing audio receive stream " << ssrc
<< " with VoiceEngine channel #" << channel << ".";
+ it->second->SetRawAudioSink(nullptr);
delete it->second;
recv_streams_.erase(it);
return DeleteVoEChannel(channel);
@@ -2408,6 +2415,19 @@
return true;
}
+void WebRtcVoiceMediaChannel::SetRawAudioSink(
+ uint32_t ssrc,
+ rtc::scoped_ptr<webrtc::AudioSinkInterface> sink) {
+ RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+ LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::SetRawAudioSink";
+ const auto it = recv_streams_.find(ssrc);
+ if (it == recv_streams_.end()) {
+ LOG(LS_WARNING) << "SetRawAudioSink: no recv stream" << ssrc;
+ return;
+ }
+ it->second->SetRawAudioSink(std::move(sink));
+}
+
int WebRtcVoiceMediaChannel::GetOutputLevel(int channel) {
unsigned int ulevel = 0;
int ret = engine()->voe()->volume()->GetSpeechOutputLevel(channel, ulevel);
diff --git a/talk/media/webrtc/webrtcvoiceengine.h b/talk/media/webrtc/webrtcvoiceengine.h
index 1de4fb9..0f2f59e 100644
--- a/talk/media/webrtc/webrtcvoiceengine.h
+++ b/talk/media/webrtc/webrtcvoiceengine.h
@@ -196,6 +196,10 @@
void OnReadyToSend(bool ready) override {}
bool GetStats(VoiceMediaInfo* info) override;
+ void SetRawAudioSink(
+ uint32_t ssrc,
+ rtc::scoped_ptr<webrtc::AudioSinkInterface> sink) override;
+
// implements Transport interface
bool SendRtp(const uint8_t* data,
size_t len,
diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc
index 588a036..9180852 100644
--- a/talk/session/media/channel.cc
+++ b/talk/session/media/channel.cc
@@ -30,6 +30,7 @@
#include "talk/media/base/constants.h"
#include "talk/media/base/rtputils.h"
#include "talk/session/media/channelmanager.h"
+#include "webrtc/audio/audio_sink.h"
#include "webrtc/base/bind.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/byteorder.h"
@@ -40,9 +41,18 @@
#include "webrtc/p2p/base/transportchannel.h"
namespace cricket {
-
using rtc::Bind;
+namespace {
+// See comment below for why we need to use a pointer to a scoped_ptr.
+bool SetRawAudioSink_w(VoiceMediaChannel* channel,
+ uint32_t ssrc,
+ rtc::scoped_ptr<webrtc::AudioSinkInterface>* sink) {
+ channel->SetRawAudioSink(ssrc, std::move(*sink));
+ return true;
+}
+} // namespace
+
enum {
MSG_EARLYMEDIATIMEOUT = 1,
MSG_SCREENCASTWINDOWEVENT,
@@ -1376,6 +1386,15 @@
media_channel(), ssrc, volume));
}
+void VoiceChannel::SetRawAudioSink(
+ uint32_t ssrc,
+ rtc::scoped_ptr<webrtc::AudioSinkInterface> sink) {
+ // We need to work around Bind's lack of support for scoped_ptr and ownership
+ // passing. So we invoke to our own little routine that gets a pointer to
+ // our local variable. This is OK since we're synchronously invoking.
+ InvokeOnWorker(Bind(&SetRawAudioSink_w, media_channel(), ssrc, &sink));
+}
+
bool VoiceChannel::GetStats(VoiceMediaInfo* stats) {
return InvokeOnWorker(Bind(&VoiceMediaChannel::GetStats,
media_channel(), stats));
diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h
index 1140954..8faefe6 100644
--- a/talk/session/media/channel.h
+++ b/talk/session/media/channel.h
@@ -38,19 +38,24 @@
#include "talk/media/base/mediaengine.h"
#include "talk/media/base/streamparams.h"
#include "talk/media/base/videocapturer.h"
-#include "webrtc/p2p/base/transportcontroller.h"
-#include "webrtc/p2p/client/socketmonitor.h"
#include "talk/session/media/audiomonitor.h"
#include "talk/session/media/bundlefilter.h"
#include "talk/session/media/mediamonitor.h"
#include "talk/session/media/mediasession.h"
#include "talk/session/media/rtcpmuxfilter.h"
#include "talk/session/media/srtpfilter.h"
+#include "webrtc/audio/audio_sink.h"
#include "webrtc/base/asyncudpsocket.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/network.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/window.h"
+#include "webrtc/p2p/base/transportcontroller.h"
+#include "webrtc/p2p/client/socketmonitor.h"
+
+namespace webrtc {
+class AudioSinkInterface;
+} // namespace webrtc
namespace cricket {
@@ -367,6 +372,9 @@
// event 0-9, *, #, A-D.
bool InsertDtmf(uint32_t ssrc, int event_code, int duration);
bool SetOutputVolume(uint32_t ssrc, double volume);
+ void SetRawAudioSink(uint32_t ssrc,
+ rtc::scoped_ptr<webrtc::AudioSinkInterface> sink);
+
// Get statistics about the current media session.
bool GetStats(VoiceMediaInfo* stats);
diff --git a/talk/session/media/channel_unittest.cc b/talk/session/media/channel_unittest.cc
index 08d83b5..35e7142 100644
--- a/talk/session/media/channel_unittest.cc
+++ b/talk/session/media/channel_unittest.cc
@@ -34,7 +34,6 @@
#include "talk/media/base/screencastid.h"
#include "talk/media/base/testutils.h"
#include "talk/session/media/channel.h"
-#include "webrtc/p2p/base/faketransportcontroller.h"
#include "webrtc/base/arraysize.h"
#include "webrtc/base/fileutils.h"
#include "webrtc/base/gunit.h"
@@ -45,6 +44,7 @@
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/sslidentity.h"
#include "webrtc/base/window.h"
+#include "webrtc/p2p/base/faketransportcontroller.h"
#define MAYBE_SKIP_TEST(feature) \
if (!(rtc::SSLStreamAdapter::feature())) { \
diff --git a/webrtc/audio/BUILD.gn b/webrtc/audio/BUILD.gn
index abae434..5a9902e 100644
--- a/webrtc/audio/BUILD.gn
+++ b/webrtc/audio/BUILD.gn
@@ -14,6 +14,7 @@
"audio_receive_stream.h",
"audio_send_stream.cc",
"audio_send_stream.h",
+ "audio_sink.h",
"audio_state.cc",
"audio_state.h",
"conversion.h",
@@ -31,7 +32,7 @@
deps = [
"..:webrtc_common",
- "../voice_engine",
"../system_wrappers",
+ "../voice_engine",
]
}
diff --git a/webrtc/audio/audio_receive_stream.cc b/webrtc/audio/audio_receive_stream.cc
index 87cb215..dfad79f 100644
--- a/webrtc/audio/audio_receive_stream.cc
+++ b/webrtc/audio/audio_receive_stream.cc
@@ -11,7 +11,9 @@
#include "webrtc/audio/audio_receive_stream.h"
#include <string>
+#include <utility>
+#include "webrtc/audio/audio_sink.h"
#include "webrtc/audio/audio_state.h"
#include "webrtc/audio/conversion.h"
#include "webrtc/base/checks.h"
@@ -201,6 +203,11 @@
return stats;
}
+void AudioReceiveStream::SetSink(rtc::scoped_ptr<AudioSinkInterface> sink) {
+ RTC_DCHECK(thread_checker_.CalledOnValidThread());
+ channel_proxy_->SetSink(std::move(sink));
+}
+
const webrtc::AudioReceiveStream::Config& AudioReceiveStream::config() const {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
return config_;
diff --git a/webrtc/audio/audio_receive_stream.h b/webrtc/audio/audio_receive_stream.h
index 08e65cd..42286af 100644
--- a/webrtc/audio/audio_receive_stream.h
+++ b/webrtc/audio/audio_receive_stream.h
@@ -24,6 +24,7 @@
} // namespace voe
namespace internal {
+
class AudioReceiveStream final : public webrtc::AudioReceiveStream {
public:
AudioReceiveStream(RemoteBitrateEstimator* remote_bitrate_estimator,
@@ -43,6 +44,8 @@
// webrtc::AudioReceiveStream implementation.
webrtc::AudioReceiveStream::Stats GetStats() const override;
+ void SetSink(rtc::scoped_ptr<AudioSinkInterface> sink) override;
+
const webrtc::AudioReceiveStream::Config& config() const;
private:
diff --git a/webrtc/audio/audio_sink.h b/webrtc/audio/audio_sink.h
new file mode 100644
index 0000000..d022b32
--- /dev/null
+++ b/webrtc/audio/audio_sink.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_AUDIO_AUDIO_SINK_H_
+#define WEBRTC_AUDIO_AUDIO_SINK_H_
+
+#if defined(WEBRTC_POSIX) && !defined(__STDC_FORMAT_MACROS)
+// Avoid conflict with format_macros.h.
+#define __STDC_FORMAT_MACROS
+#endif
+
+#include <inttypes.h>
+#include <stddef.h>
+
+namespace webrtc {
+
+// Represents a simple push audio sink.
+class AudioSinkInterface {
+ public:
+ virtual ~AudioSinkInterface() {}
+
+ struct Data {
+ Data(int16_t* data,
+ size_t samples_per_channel,
+ int sample_rate,
+ int channels,
+ uint32_t timestamp)
+ : data(data),
+ samples_per_channel(samples_per_channel),
+ sample_rate(sample_rate),
+ channels(channels),
+ timestamp(timestamp) {}
+
+ int16_t* data; // The actual 16bit audio data.
+ size_t samples_per_channel; // Number of frames in the buffer.
+ int sample_rate; // Sample rate in Hz.
+ int channels; // Number of channels in the audio data.
+ uint32_t timestamp; // The RTP timestamp of the first sample.
+ };
+
+ virtual void OnData(const Data& audio) = 0;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_AUDIO_AUDIO_SINK_H_
diff --git a/webrtc/audio/webrtc_audio.gypi b/webrtc/audio/webrtc_audio.gypi
index 9b4879a..53b7d16 100644
--- a/webrtc/audio/webrtc_audio.gypi
+++ b/webrtc/audio/webrtc_audio.gypi
@@ -18,6 +18,7 @@
'audio/audio_receive_stream.h',
'audio/audio_send_stream.cc',
'audio/audio_send_stream.h',
+ 'audio/audio_sink.h',
'audio/audio_state.cc',
'audio/audio_state.h',
'audio/conversion.h',
diff --git a/webrtc/audio_receive_stream.h b/webrtc/audio_receive_stream.h
index 356a3a3..daf4598 100644
--- a/webrtc/audio_receive_stream.h
+++ b/webrtc/audio_receive_stream.h
@@ -15,6 +15,7 @@
#include <string>
#include <vector>
+#include "webrtc/base/scoped_ptr.h"
#include "webrtc/config.h"
#include "webrtc/stream.h"
#include "webrtc/transport.h"
@@ -23,6 +24,7 @@
namespace webrtc {
class AudioDecoder;
+class AudioSinkInterface;
// WORK IN PROGRESS
// This class is under development and is not yet intended for for use outside
@@ -100,6 +102,16 @@
};
virtual Stats GetStats() const = 0;
+
+ // Sets an audio sink that receives unmixed audio from the receive stream.
+ // Ownership of the sink is passed to the stream and can be used by the
+ // caller to do lifetime management (i.e. when the sink's dtor is called).
+ // Only one sink can be set and passing a null sink, clears an existing one.
+ // NOTE: Audio must still somehow be pulled through AudioTransport for audio
+ // to stream through this sink. In practice, this happens if mixed audio
+ // is being pulled+rendered and/or if audio is being pulled for the purposes
+ // of feeding to the AEC.
+ virtual void SetSink(rtc::scoped_ptr<AudioSinkInterface> sink) = 0;
};
} // namespace webrtc
diff --git a/webrtc/base/format_macros.h b/webrtc/base/format_macros.h
index 5d7dcc3..90f86a6 100644
--- a/webrtc/base/format_macros.h
+++ b/webrtc/base/format_macros.h
@@ -73,6 +73,8 @@
#else // WEBRTC_WIN
+#include <inttypes.h>
+
#if !defined(PRId64)
#define PRId64 "I64d"
#endif
diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc
index 18f3408..48111db 100644
--- a/webrtc/voice_engine/channel.cc
+++ b/webrtc/voice_engine/channel.cc
@@ -11,6 +11,7 @@
#include "webrtc/voice_engine/channel.h"
#include <algorithm>
+#include <utility>
#include "webrtc/base/checks.h"
#include "webrtc/base/format_macros.h"
@@ -560,6 +561,21 @@
}
}
+ {
+ // Pass the audio buffers to an optional sink callback, before applying
+ // scaling/panning, as that applies to the mix operation.
+ // External recipients of the audio (e.g. via AudioTrack), will do their
+ // own mixing/dynamic processing.
+ CriticalSectionScoped cs(&_callbackCritSect);
+ if (audio_sink_) {
+ AudioSinkInterface::Data data(
+ &audioFrame->data_[0],
+ audioFrame->samples_per_channel_, audioFrame->sample_rate_hz_,
+ audioFrame->num_channels_, audioFrame->timestamp_);
+ audio_sink_->OnData(data);
+ }
+ }
+
float output_gain = 1.0f;
float left_pan = 1.0f;
float right_pan = 1.0f;
@@ -608,13 +624,10 @@
const bool isStereo = (audioFrame->num_channels_ == 2);
if (_outputExternalMediaCallbackPtr)
{
- _outputExternalMediaCallbackPtr->Process(
- _channelId,
- kPlaybackPerChannel,
- (int16_t*)audioFrame->data_,
- audioFrame->samples_per_channel_,
- audioFrame->sample_rate_hz_,
- isStereo);
+ _outputExternalMediaCallbackPtr->Process(
+ _channelId, kPlaybackPerChannel, (int16_t*)audioFrame->data_,
+ audioFrame->samples_per_channel_, audioFrame->sample_rate_hz_,
+ isStereo);
}
}
@@ -1172,6 +1185,11 @@
return 0;
}
+void Channel::SetSink(rtc::scoped_ptr<AudioSinkInterface> sink) {
+ CriticalSectionScoped cs(&_callbackCritSect);
+ audio_sink_ = std::move(sink);
+}
+
int32_t
Channel::StartPlayout()
{
diff --git a/webrtc/voice_engine/channel.h b/webrtc/voice_engine/channel.h
index d3b1b93..9184b93 100644
--- a/webrtc/voice_engine/channel.h
+++ b/webrtc/voice_engine/channel.h
@@ -11,6 +11,7 @@
#ifndef WEBRTC_VOICE_ENGINE_CHANNEL_H_
#define WEBRTC_VOICE_ENGINE_CHANNEL_H_
+#include "webrtc/audio/audio_sink.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/common_audio/resampler/include/push_resampler.h"
@@ -192,6 +193,8 @@
CriticalSectionWrapper* callbackCritSect);
int32_t UpdateLocalTimeStamp();
+ void SetSink(rtc::scoped_ptr<AudioSinkInterface> sink);
+
// API methods
// VoEBase
@@ -508,6 +511,7 @@
TelephoneEventHandler* telephone_event_handler_;
rtc::scoped_ptr<RtpRtcp> _rtpRtcpModule;
rtc::scoped_ptr<AudioCodingModule> audio_coding_;
+ rtc::scoped_ptr<AudioSinkInterface> audio_sink_;
AudioLevel _outputAudioLevel;
bool _externalTransport;
AudioFrame _audioFrame;
diff --git a/webrtc/voice_engine/channel_proxy.cc b/webrtc/voice_engine/channel_proxy.cc
index 68fbf38..f54c81e 100644
--- a/webrtc/voice_engine/channel_proxy.cc
+++ b/webrtc/voice_engine/channel_proxy.cc
@@ -10,6 +10,9 @@
#include "webrtc/voice_engine/channel_proxy.h"
+#include <utility>
+
+#include "webrtc/audio/audio_sink.h"
#include "webrtc/base/checks.h"
#include "webrtc/voice_engine/channel.h"
@@ -22,6 +25,8 @@
RTC_CHECK(channel_owner_.channel());
}
+ChannelProxy::~ChannelProxy() {}
+
void ChannelProxy::SetRTCPStatus(bool enable) {
channel()->SetRTCPStatus(enable);
}
@@ -134,6 +139,11 @@
channel()->SendTelephoneEventOutband(event, duration_ms, 10, false) == 0;
}
+void ChannelProxy::SetSink(rtc::scoped_ptr<AudioSinkInterface> sink) {
+ RTC_DCHECK(thread_checker_.CalledOnValidThread());
+ channel()->SetSink(std::move(sink));
+}
+
Channel* ChannelProxy::channel() const {
RTC_DCHECK(channel_owner_.channel());
return channel_owner_.channel();
diff --git a/webrtc/voice_engine/channel_proxy.h b/webrtc/voice_engine/channel_proxy.h
index fa33e6c..b990d91 100644
--- a/webrtc/voice_engine/channel_proxy.h
+++ b/webrtc/voice_engine/channel_proxy.h
@@ -20,6 +20,7 @@
namespace webrtc {
+class AudioSinkInterface;
class PacketRouter;
class RtpPacketSender;
class TransportFeedbackObserver;
@@ -39,7 +40,7 @@
public:
ChannelProxy();
explicit ChannelProxy(const ChannelOwner& channel_owner);
- virtual ~ChannelProxy() {}
+ virtual ~ChannelProxy();
virtual void SetRTCPStatus(bool enable);
virtual void SetLocalSSRC(uint32_t ssrc);
@@ -64,6 +65,8 @@
virtual bool SetSendTelephoneEventPayloadType(int payload_type);
virtual bool SendTelephoneEventOutband(uint8_t event, uint32_t duration_ms);
+ virtual void SetSink(rtc::scoped_ptr<AudioSinkInterface> sink);
+
private:
Channel* channel() const;