Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api/media_stream_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,11 @@ class RTC_EXPORT AudioSourceInterface : public MediaSourceInterface {
// audio network adaptation on the source is the wrong layer of abstraction).
virtual const AudioOptions options() const;
virtual void SetOptions(const AudioOptions & /* options */) {}

// Returns true if this source delivers audio externally (via AddSink),
// bypassing the ADM/AudioState audio distribution path.
// When true, AudioSendStream should not register with AudioState.
virtual bool is_external_source() const { return false; }
};

// Interface of the audio processor used by the audio track to collect
Expand Down
16 changes: 12 additions & 4 deletions audio/audio_send_stream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,13 @@ void AudioSendStream::Start() {
}
channel_send_->StartSend();
sending_ = true;
audio_state()->AddSendingStream(this, encoder_sample_rate_hz_,
encoder_num_channels_);
// Only register with AudioState if not using an external source.
// External sources deliver audio directly via AddSink, so AudioState
// must not also push device-captured audio into this stream.
if (!config_.external_source) {
audio_state()->AddSendingStream(this, encoder_sample_rate_hz_,
encoder_num_channels_);
}
}

void AudioSendStream::Stop() {
Expand All @@ -386,7 +391,10 @@ void AudioSendStream::Stop() {
RemoveBitrateObserver();
channel_send_->StopSend();
sending_ = false;
audio_state()->RemoveSendingStream(this);
// Only unregister if we registered (when not using an external source).
if (!config_.external_source) {
audio_state()->RemoveSendingStream(this);
}
}

void AudioSendStream::SendAudioData(std::unique_ptr<AudioFrame> audio_frame) {
Expand Down Expand Up @@ -577,7 +585,7 @@ void AudioSendStream::StoreEncoderProperties(int sample_rate_hz,
size_t num_channels) {
encoder_sample_rate_hz_ = sample_rate_hz;
encoder_num_channels_ = num_channels;
if (sending_) {
if (sending_ && !config_.external_source) {
// Update AudioState's information about the stream.
audio_state()->AddSendingStream(this, sample_rate_hz, num_channels);
}
Expand Down
1 change: 1 addition & 0 deletions call/audio_send_stream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ std::string AudioSendStream::Config::ToString() const {
ss << ", has audio_network_adaptor_config: "
<< (audio_network_adaptor_config ? "true" : "false");
ss << ", has_dscp: " << (has_dscp ? "true" : "false");
ss << ", external_source: " << (external_source ? "true" : "false");
ss << ", send_codec_spec: "
<< (send_codec_spec ? send_codec_spec->ToString() : "<unset>");
ss << "}";
Expand Down
6 changes: 6 additions & 0 deletions call/audio_send_stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ class AudioSendStream : public AudioSender {
// An optional frame transformer used by insertable streams to transform
// encoded frames.
scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer;

// When true, this stream uses an external audio source (not ADM).
// AudioState will NOT send device-captured audio to this stream.
// Audio is delivered directly via the source's AddSink mechanism.
// This prevents mixing of device audio with externally-sourced audio.
bool external_source = false;
};

virtual ~AudioSendStream() = default;
Expand Down
4 changes: 4 additions & 0 deletions media/base/audio_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ class AudioSource {
// to the source at a time.
virtual void SetSink(Sink* sink) = 0;

// Returns true if this source delivers audio externally (bypassing ADM).
// When true, AudioSendStream should not register with AudioState.
virtual bool is_external_source() const { return false; }

protected:
virtual ~AudioSource() {}
};
Expand Down
13 changes: 13 additions & 0 deletions media/engine/webrtc_voice_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,19 @@ class WebRtcVoiceSendChannel::WebRtcAudioSendStream : public AudioSource::Sink {
void SetSource(AudioSource* source) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_DCHECK(source);
// If the source delivers audio externally (via its own AddSink path),
// mark the config so AudioState doesn't also push device audio into this
// stream. Evaluated before the early return below because on track
// replacement the adapter object stays the same while the underlying
// source type may change. The stream is stopped across the flip so that
// its AudioState registration is always added and removed under the same
// flag value, then restarted under the new one.
if (source->is_external_source() != config_.external_source) {
stream_->Stop();
config_.external_source = source->is_external_source();
ReconfigureAudioSendStream(nullptr);
UpdateSendState();
}
if (source_) {
RTC_DCHECK(source_ == source);
return;
Expand Down
27 changes: 27 additions & 0 deletions pc/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,29 @@ rtc_library("local_audio_source") {
"../api:scoped_refptr",
]
}
rtc_library("external_audio_source") {
visibility = [ "*" ]
sources = [
"external_audio_source.cc",
"external_audio_source.h",
]
public_deps = [ ":local_audio_source" ]
deps = [
"../api:audio_options_api",
"../api:make_ref_counted",
"../api:media_stream_interface",
"../api:scoped_refptr",
"../api/task_queue",
"../api/units:time_delta",
"../rtc_base:checks",
"../rtc_base:logging",
"../rtc_base:macromagic",
"../rtc_base/synchronization:mutex",
"../rtc_base/task_utils:repeating_task",
"../system_wrappers",
"//third_party/abseil-cpp/absl/functional:any_invocable",
]
}
rtc_library("peer_connection") {
visibility = [ ":*" ]
sources = [
Expand Down Expand Up @@ -2538,6 +2561,7 @@ if (rtc_include_tests && !build_with_chromium) {
"data_channel_integrationtest.cc",
"data_channel_unittest.cc",
"dtmf_sender_unittest.cc",
"external_audio_source_unittest.cc",
"ice_server_parsing_unittest.cc",
"jitter_buffer_delay_unittest.cc",
"jsep_session_description_unittest.cc",
Expand Down Expand Up @@ -2595,6 +2619,7 @@ if (rtc_include_tests && !build_with_chromium) {
":dtls_transport",
":dtmf_sender",
":enable_fake_media",
":external_audio_source",
":fake_codec_lookup_helper",
":ice_server_parsing",
":integration_test_helpers",
Expand Down Expand Up @@ -2774,6 +2799,7 @@ if (rtc_include_tests && !build_with_chromium) {
"../rtc_base:network",
"../rtc_base:network_constants",
"../rtc_base:null_socket_server",
"../rtc_base:platform_thread",
"../rtc_base:random",
"../rtc_base:refcount",
"../rtc_base:rtc_base_tests_utils",
Expand Down Expand Up @@ -2803,6 +2829,7 @@ if (rtc_include_tests && !build_with_chromium) {
"../test:test_support",
"../test:wait_until",
"../test/pc/sctp:fake_sctp_transport",
"../test/time_controller",
"scenario_tests:pc_scenario_tests",
"//third_party/abseil-cpp/absl/algorithm:container",
"//third_party/abseil-cpp/absl/base:nullability",
Expand Down
Loading