Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
libwebrtc: patch
livekit: patch
livekit-ffi: patch
webrtc-sys: patch
---

Improve WebRTC build scripts and add external_audio_source patch - #1053 (@xianshijing-lk)
10 changes: 6 additions & 4 deletions webrtc-sys/libwebrtc/build_android.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ cd src
git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
git apply "$COMMAND_DIR/patches/android_use_libunwind.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
git apply "$COMMAND_DIR/patches/external_audio_source.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
# livekit prefixed jni
git apply "$COMMAND_DIR/patches/jni_prefix.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn

Expand Down Expand Up @@ -105,7 +106,6 @@ args="is_debug=$debug \
enable_iterator_debugging=false \
android_package_prefix=\"livekit\" \
use_custom_libcxx=false \
use_clang_modules=false \
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use_clang_modules=false seem to make the licence generate script fail, @cloudwebrtc , any concern of removing this flag ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, use_clang_modules=false was introduced in the m144 upgrade; I'll double-check if it's a required parameter.

use_rtti=true"

if [ "$debug" = "true" ]; then
Expand All @@ -125,13 +125,15 @@ autoninja -C "$OUTPUT_DIR" :default \
# don't include nasm
ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"`

python3 "./src/tools_webrtc/libs/generate_licenses.py" \
--target :default "$OUTPUT_DIR" "$OUTPUT_DIR"
# License generation is optional - may fail with some Python versions
# Use vpython3 from depot_tools for consistent Python version
vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \
--target :default "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed (non-critical)"

cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/libjingle_peerconnection_so.so" "$ARTIFACTS_DIR/lib"
cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR" 2>/dev/null || echo "Warning: LICENSE.md not found (non-critical)"

mkdir -p "$COMMAND_DIR/prefixed-jni/libs"
cp "$OUTPUT_DIR/lib.java/sdk/android/libwebrtc.jar" "$COMMAND_DIR/prefixed-jni/libs/classes.jar"
Expand Down
27 changes: 20 additions & 7 deletions webrtc-sys/libwebrtc/build_ios.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,22 @@ then
fi

cd src

# Apply patches only if not already applied (check with --reverse --check)
apply_patch_if_needed() {
local patch="$1"
if git apply --reverse --check "$patch" 2>/dev/null; then
echo "Patch already applied: $(basename "$patch")"
else
echo "Applying patch: $(basename "$patch")"
git apply "$patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn || true
fi
}

# git apply "$COMMAND_DIR/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
apply_patch_if_needed "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch"
apply_patch_if_needed "$COMMAND_DIR/patches/add_deps.patch"
apply_patch_if_needed "$COMMAND_DIR/patches/external_audio_source.patch"

cd ..

Expand Down Expand Up @@ -111,7 +124,6 @@ gn gen "$OUTPUT_DIR" --root="src" \
rtc_enable_objc_symbol_export=false \
rtc_use_h264=false \
use_custom_libcxx=false \
use_clang_modules=false \
clang_use_chrome_plugins=false \
use_rtti=true \
use_lld=false"
Expand All @@ -130,13 +142,14 @@ ninja -C "$OUTPUT_DIR" :default \
# don't include nasm
ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"`

python3 "./src/tools_webrtc/libs/generate_licenses.py" \
--target :webrtc "$OUTPUT_DIR" "$OUTPUT_DIR"
# License generation is optional - may fail with some Python versions
# Use vpython3 from depot_tools for consistent Python version
vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \
--target :webrtc "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed (non-critical)"

cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR" 2>/dev/null || true
cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR"

cd src
find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include"
Expand Down
10 changes: 6 additions & 4 deletions webrtc-sys/libwebrtc/build_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ git apply "$COMMAND_DIR/patches/add_licenses.patch" -v --ignore-space-change --i
git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
git apply "$COMMAND_DIR/patches/fix_desktop_capture_compile.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
git apply "$COMMAND_DIR/patches/external_audio_source.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn

# Disable CREL (compact relocations). Chromium's build enables experimental
# CREL via -Wa,--crel which causes segfaults on aarch64-linux (and is known
Expand Down Expand Up @@ -105,7 +106,6 @@ args="is_debug=$debug \
treat_warnings_as_errors=false \
use_llvm_libatomic=false \
use_custom_libcxx=false \
use_clang_modules=false \
use_custom_libcxx_for_host=false \
rtc_include_tests=false \
rtc_build_tools=false \
Expand Down Expand Up @@ -134,13 +134,15 @@ ninja -C "$OUTPUT_DIR" :default
ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"`
src/third_party/llvm-build/Release+Asserts/bin/llvm-objcopy --redefine-syms="$COMMAND_DIR/boringssl_prefix_symbols.txt" "$ARTIFACTS_DIR/lib/libwebrtc.a"

python3 "./src/tools_webrtc/libs/generate_licenses.py" \
--target :default "$OUTPUT_DIR" "$OUTPUT_DIR"
# License generation is optional - may fail with some Python versions
# Use vpython3 from depot_tools for consistent Python version
vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \
--target :default "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed (non-critical)"

cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR" 2>/dev/null || echo "Warning: LICENSE.md not found (non-critical)"

cd src
find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include"
Expand Down
36 changes: 26 additions & 10 deletions webrtc-sys/libwebrtc/build_macos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,22 @@ then
fi

cd src
git apply "$COMMAND_DIR/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn

# Apply patches only if not already applied (check with --reverse --check)
apply_patch_if_needed() {
local patch="$1"
if git apply --reverse --check "$patch" 2>/dev/null; then
echo "Patch already applied: $(basename "$patch")"
else
echo "Applying patch: $(basename "$patch")"
git apply "$patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn || true
fi
}

apply_patch_if_needed "$COMMAND_DIR/patches/add_licenses.patch"
apply_patch_if_needed "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch"
apply_patch_if_needed "$COMMAND_DIR/patches/add_deps.patch"
apply_patch_if_needed "$COMMAND_DIR/patches/external_audio_source.patch"

cd ..

Expand Down Expand Up @@ -103,10 +116,10 @@ gn gen "$OUTPUT_DIR" --root="src" \
rtc_use_h264=true \
rtc_use_h265=true \
use_custom_libcxx=false \
use_clang_modules=false \
clang_use_chrome_plugins=false \
use_rtti=true \
use_lld=false"
use_lld=false \
rtc_include_internal_audio_device=true"

# build static library
ninja -C "$OUTPUT_DIR" :default \
Expand All @@ -117,19 +130,22 @@ ninja -C "$OUTPUT_DIR" :default \
pc:peer_connection \
sdk:videocapture_objc \
sdk:mac_framework_objc \
desktop_capture_objc
desktop_capture_objc \
modules/audio_device:audio_device

# make libwebrtc.a
# don't include nasm
ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"`

python3 "./src/tools_webrtc/libs/generate_licenses.py" \
--target :webrtc "$OUTPUT_DIR" "$OUTPUT_DIR"
# License generation is optional - may fail with some Python versions
# Use vpython3 from depot_tools for consistent Python version
vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \
--target :webrtc "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed (non-critical)"

cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR" 2>/dev/null || true
cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR"
cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR" 2>/dev/null || echo "Warning: LICENSE.md not found (non-critical)"

cd src
find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include"
Expand Down
142 changes: 142 additions & 0 deletions webrtc-sys/libwebrtc/patches/external_audio_source.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
diff --git a/api/media_stream_interface.h b/api/media_stream_interface.h
index fb1cc4e58e..85062ba60e 100644
--- a/api/media_stream_interface.h
+++ b/api/media_stream_interface.h
@@ -267,6 +267,11 @@ class RTC_EXPORT AudioSourceInterface : public MediaSourceInterface {
// (for some of the settings this approach is broken, e.g. setting
// audio network adaptation on the source is the wrong layer of abstraction).
virtual const AudioOptions options() const;
+
+ // 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
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index 76156ce830..10b59d3ff6 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -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 external source.
+ // External sources (like NativeAudioSource) deliver audio directly via AddSink,
+ // so we don't want AudioState to also send device audio to this stream.
+ if (!config_.external_source) {
+ audio_state()->AddSendingStream(this, encoder_sample_rate_hz_,
+ encoder_num_channels_);
+ }
}

void AudioSendStream::Stop() {
@@ -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 external source).
+ if (!config_.external_source) {
+ audio_state()->RemoveSendingStream(this);
+ }
}

void AudioSendStream::SendAudioData(std::unique_ptr<AudioFrame> audio_frame) {
diff --git a/call/audio_send_stream.h b/call/audio_send_stream.h
index 84341b5cb1..9359777bc9 100644
--- a/call/audio_send_stream.h
+++ b/call/audio_send_stream.h
@@ -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;
diff --git a/media/base/audio_source.h b/media/base/audio_source.h
index 04a7d19dfa..9f513f3c75 100644
--- a/media/base/audio_source.h
+++ b/media/base/audio_source.h
@@ -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() {}
};
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 762f9d584c..4ce07ddc9d 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -1017,6 +1017,14 @@ class WebRtcVoiceSendChannel::WebRtcAudioSendStream : public AudioSource::Sink {
RTC_DCHECK(source_ == source);
return;
}
+
+ // Check if this is an external audio source (delivers audio via AddSink).
+ // If so, mark the config so AudioState doesn't send device audio to this
+ // stream. This must be done before UpdateSendState() calls Start().
+ if (source->is_external_source() && !config_.external_source) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QQ: This method (AudioSource::is_external_source() const) is looks like not overridden in any inherited class.

In another Rust PR, is_external_source is overridden to webrtc::AudioSourceInterface, but it doesn't seem to be associated with webrtc::AudioSource. I suspect that source->is_external_source() here will always return false.

Perhaps we're missing a copy of the is_external_source state from webrtc::AudioSourceInterface to webrtc::AudioSrouce? The class associated with webrtc::AudioSource is LocalAudioSinkAdapter.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I think the problem with the current approach is that it will send both the external source file and mic source to one track.
which will result in duplicating the mic data with some drift there.

I try patching it, can you check it again ?

It might worth coming up with some integration tests, let me work on it

+ config_.external_source = true;
+ stream_->Reconfigure(config_, nullptr);
+ }
source->SetSink(this);
source_ = source;
UpdateSendState();
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc
index d5edbbf0ed..c14ddfe868 100644
--- a/pc/rtp_sender.cc
+++ b/pc/rtp_sender.cc
@@ -786,6 +786,13 @@ void AudioRtpSender::SetSend() {
RTC_DCHECK_RUN_ON(signaling_thread_);
RTC_DCHECK(!stopped_);
RTC_DCHECK(can_send_track());
+
+ // Propagate is_external_source from AudioSourceInterface to the sink adapter.
+ // This ensures the voice engine knows not to mix ADM audio with external sources.
+ if (audio_track()->GetSource() && audio_track()->GetSource()->is_external_source()) {
+ sink_adapter_->set_is_external_source(true);
+ }
+
if (!media_channel_) {
RTC_LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
return;
diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h
index eaffd4ef0f..d0489df9e7 100644
--- a/pc/rtp_sender.h
+++ b/pc/rtp_sender.h
@@ -307,6 +307,12 @@ class LocalAudioSinkAdapter : public AudioTrackSinkInterface,
LocalAudioSinkAdapter();
virtual ~LocalAudioSinkAdapter();

+ // Set whether the original AudioSourceInterface is an external source.
+ // This propagates the is_external_source state from AudioSourceInterface
+ // to this AudioSource adapter.
+ void set_is_external_source(bool value) { is_external_source_ = value; }
+ bool is_external_source() const override { return is_external_source_; }
+
private:
// AudioSinkInterface implementation.
void OnData(const void* audio_data,
@@ -337,6 +343,7 @@ class LocalAudioSinkAdapter : public AudioTrackSinkInterface,
// Critical section protecting `sink_`.
Mutex lock_;
int num_preferred_channels_ = -1;
+ bool is_external_source_ = false;
};

class AudioRtpSender : public DtmfProviderInterface, public RtpSenderBase {
Loading