From fba40393c29bc5e18fefe1080e9051e27bda5f2b Mon Sep 17 00:00:00 2001 From: Del Wang Date: Sun, 28 Dec 2025 20:17:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8A=A8=E6=80=81=E9=93=BE=E6=8E=A5?= =?UTF-8?q?=E5=88=B0=20opus=20=E5=92=8C=20alsa=20=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E5=BA=93=EF=BC=8C=E5=AE=8C=E6=88=90=E9=9F=B3=E9=A2=91=E5=BD=95?= =?UTF-8?q?=E5=88=B6=E6=92=AD=E6=94=BE=E5=92=8C=E7=BC=96=E8=A7=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/hello/Cargo.lock | 115 +++++++++++++++++++++++++++++++ apps/hello/Cargo.toml | 3 + apps/hello/src/audio/codec.rs | 31 +++++++++ apps/hello/src/audio/mod.rs | 8 +++ apps/hello/src/audio/player.rs | 54 +++++++++++++++ apps/hello/src/audio/recorder.rs | 37 ++++++++++ apps/hello/src/config.rs | 25 +++++++ apps/hello/src/main.rs | 68 ++++++++++++++++-- apps/runtime/Dockerfile | 96 ++++++++++++++++++++++++-- apps/runtime/build.sh | 22 +++--- apps/runtime/run.sh | 18 ----- 11 files changed, 431 insertions(+), 46 deletions(-) create mode 100644 apps/hello/src/audio/codec.rs create mode 100644 apps/hello/src/audio/mod.rs create mode 100644 apps/hello/src/audio/player.rs create mode 100644 apps/hello/src/audio/recorder.rs create mode 100644 apps/hello/src/config.rs diff --git a/apps/hello/Cargo.lock b/apps/hello/Cargo.lock index bdfea16..1424177 100644 --- a/apps/hello/Cargo.lock +++ b/apps/hello/Cargo.lock @@ -2,6 +2,121 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "alsa" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812947049edcd670a82cd5c73c3661d2e58468577ba8489de58e1a73c04cbd5d" +dependencies = [ + "alsa-sys", + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "alsa-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad7569085a265dd3f607ebecce7458eaab2132a84393534c95b18dcbc3f31e04" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "audiopus_sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62314a1546a2064e033665d658e88c620a62904be945f8147e6b16c3db9f8651" +dependencies = [ + "cmake", + "log", + "pkg-config", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "cc" +version = "1.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" + [[package]] name = "hello" version = "0.1.0" +dependencies = [ + "alsa", + "anyhow", + "opus", +] + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "opus" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6526409b274a7e98e55ff59d96aafd38e6cd34d46b7dbbc32ce126dffcd75e8e" +dependencies = [ + "audiopus_sys", + "libc", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" diff --git a/apps/hello/Cargo.toml b/apps/hello/Cargo.toml index f6f3649..f283eaa 100644 --- a/apps/hello/Cargo.toml +++ b/apps/hello/Cargo.toml @@ -4,3 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] +alsa = "0.11" +opus = "0.3" +anyhow = "1.0" diff --git a/apps/hello/src/audio/codec.rs b/apps/hello/src/audio/codec.rs new file mode 100644 index 0000000..9b1e4b9 --- /dev/null +++ b/apps/hello/src/audio/codec.rs @@ -0,0 +1,31 @@ +use opus::{Encoder, Decoder, Application, Bitrate, Channels}; +use anyhow::{Result, Context}; +use crate::config::AudioConfig; + +pub struct OpusCodec { + encoder: Encoder, + decoder: Decoder, +} + +impl OpusCodec { + pub fn new(config: &AudioConfig) -> Result { + let channels = if config.channels == 1 { Channels::Mono } else { Channels::Stereo }; + let mut encoder = Encoder::new(config.sample_rate, channels, Application::Voip) + .context("Failed to create Opus encoder")?; + encoder.set_bitrate(Bitrate::Bits(config.bitrate))?; + + let decoder = Decoder::new(config.sample_rate, channels) + .context("Failed to create Opus decoder")?; + + Ok(Self { encoder, decoder }) + } + + pub fn encode(&mut self, pcm: &[i16], out: &mut [u8]) -> Result { + self.encoder.encode(pcm, out).context("Opus encoding failed") + } + + pub fn decode(&mut self, opus: &[u8], out: &mut [i16]) -> Result { + self.decoder.decode(opus, out, false).context("Opus decoding failed") + } +} + diff --git a/apps/hello/src/audio/mod.rs b/apps/hello/src/audio/mod.rs new file mode 100644 index 0000000..23d9a65 --- /dev/null +++ b/apps/hello/src/audio/mod.rs @@ -0,0 +1,8 @@ +pub mod recorder; +pub mod player; +pub mod codec; + +pub use recorder::AudioRecorder; +pub use player::AudioPlayer; +pub use codec::OpusCodec; + diff --git a/apps/hello/src/audio/player.rs b/apps/hello/src/audio/player.rs new file mode 100644 index 0000000..5b04ba7 --- /dev/null +++ b/apps/hello/src/audio/player.rs @@ -0,0 +1,54 @@ +use crate::config::AudioConfig; +use alsa::pcm::{Access, Format, HwParams, PCM}; +use alsa::Direction; +use anyhow::{Context, Result}; + +pub struct AudioPlayer { + pcm: PCM, +} + +impl AudioPlayer { + pub fn new(config: &AudioConfig) -> Result { + let pcm = PCM::new(&config.playback_device, Direction::Playback, false) + .context("Failed to open playback PCM device")?; + + setup_pcm(&pcm, config.sample_rate, config.channels)?; + Ok(Self { pcm }) + } + + pub fn write(&self, buffer: &[i16]) -> Result { + let res = self.pcm.io_i16()?.writei(buffer); + + match res { + Ok(written) => Ok(written), + Err(e) => { + // Buffer Underrun,即播放缓冲区的数据被耗尽,导致音频流中断 + if e.errno() == 32 { + // 恢复音频流状态 + self.pcm.prepare()?; + // 重新获取 IO 对象并尝试写入数据 + self.pcm + .io_i16()? + .writei(buffer) + .context("Failed to write to playback device after recovery") + } else { + Err(e).context("Failed to write to playback device") + } + } + } + } +} + +fn setup_pcm(pcm: &PCM, sample_rate: u32, channels: u16) -> Result<()> { + let hwp = HwParams::any(pcm).context("Failed to get HwParams")?; + hwp.set_access(Access::RWInterleaved)?; + hwp.set_format(Format::s16())?; + hwp.set_rate(sample_rate, alsa::ValueOr::Nearest)?; + hwp.set_channels(channels as u32)?; + pcm.hw_params(&hwp).context("Failed to set HwParams")?; + + let swp = pcm.sw_params_current()?; + pcm.sw_params(&swp)?; + pcm.prepare()?; + Ok(()) +} diff --git a/apps/hello/src/audio/recorder.rs b/apps/hello/src/audio/recorder.rs new file mode 100644 index 0000000..4156518 --- /dev/null +++ b/apps/hello/src/audio/recorder.rs @@ -0,0 +1,37 @@ +use alsa::pcm::{PCM, HwParams, Format, Access}; +use alsa::Direction; +use anyhow::{Result, Context}; +use crate::config::AudioConfig; + +pub struct AudioRecorder { + pcm: PCM, +} + +impl AudioRecorder { + pub fn new(config: &AudioConfig) -> Result { + let pcm = PCM::new(&config.capture_device, Direction::Capture, false) + .context("Failed to open capture PCM device")?; + + setup_pcm(&pcm, config.sample_rate, config.channels)?; + Ok(Self { pcm }) + } + + pub fn read(&self, buffer: &mut [i16]) -> Result { + self.pcm.io_i16()?.readi(buffer).context("Failed to read from capture device") + } +} + +fn setup_pcm(pcm: &PCM, sample_rate: u32, channels: u16) -> Result<()> { + let hwp = HwParams::any(pcm).context("Failed to get HwParams")?; + hwp.set_access(Access::RWInterleaved)?; + hwp.set_format(Format::s16())?; + hwp.set_rate(sample_rate, alsa::ValueOr::Nearest)?; + hwp.set_channels(channels as u32)?; + pcm.hw_params(&hwp).context("Failed to set HwParams")?; + + let swp = pcm.sw_params_current()?; + pcm.sw_params(&swp)?; + pcm.prepare()?; + Ok(()) +} + diff --git a/apps/hello/src/config.rs b/apps/hello/src/config.rs new file mode 100644 index 0000000..28ba5c1 --- /dev/null +++ b/apps/hello/src/config.rs @@ -0,0 +1,25 @@ +pub struct AudioConfig { + pub capture_device: String, + pub playback_device: String, + pub sample_rate: u32, + pub channels: u16, + pub frame_size: usize, + pub bitrate: i32, +} + +impl Default for AudioConfig { + fn default() -> Self { + Self { + // 根据 apps/runtime/squashfs-root/etc/asound.conf 分析: + // 录音设备对应 pcm.Capture -> hw:0,3 (S32_LE, 48000Hz) + // 播放设备对应 pcm.dmixer -> hw:0,2 (S16_LE, 48000Hz) + // 使用 "default" 或 "plug" 设备可以由 ALSA 自动处理采样率和格式转换 + capture_device: "plug:Capture".to_string(), + playback_device: "default".to_string(), + sample_rate: 16000, // Opus 常用 16k,ALSA plug 会自动从 48k 转换 + channels: 1, + frame_size: 320, // 20ms at 16kHz + bitrate: 16000, + } + } +} diff --git a/apps/hello/src/main.rs b/apps/hello/src/main.rs index dfdb1fc..5e12891 100644 --- a/apps/hello/src/main.rs +++ b/apps/hello/src/main.rs @@ -1,9 +1,63 @@ -fn main() { - println!("Hello from OpenWrt (ARMhf)!"); - println!("System information (via /proc/version):"); - if let Ok(version) = std::fs::read_to_string("/proc/version") { - println!("{}", version); - } else { - println!("Could not read /proc/version (running in simulation?)"); +mod audio; +mod config; + +use crate::audio::{AudioPlayer, AudioRecorder, OpusCodec}; +use crate::config::AudioConfig; +use anyhow::Result; +use std::collections::VecDeque; +use std::time::{Duration, Instant}; + +fn main() -> Result<()> { + let config = AudioConfig::default(); + println!("Starting Audio Modular Demo with 1s delay"); + println!( + "Config: capture={}, playback={}, rate={}, channels={}, frame_size={}", + config.capture_device, + config.playback_device, + config.sample_rate, + config.channels, + config.frame_size + ); + + let recorder = AudioRecorder::new(&config)?; + let player = AudioPlayer::new(&config)?; + let mut codec = OpusCodec::new(&config)?; + + // 使用队列存储编码后的 Opus 数据块及录制时间 + let mut delay_queue: VecDeque<(Instant, Vec)> = VecDeque::new(); + let delay_duration = Duration::from_secs(1); + + let mut pcm_in = vec![0i16; config.frame_size * config.channels as usize]; + let mut opus_buf = vec![0u8; 1024]; + + println!("Recording... Delaying playback by 1s. Press Ctrl+C to stop."); + + loop { + // 1. 录制原始 PCM + let read = recorder.read(&mut pcm_in)?; + if read == config.frame_size { + // 2. 编码并存入队列 + let opus_len = codec.encode(&pcm_in, &mut opus_buf)?; + let opus_data = opus_buf[..opus_len].to_vec(); + delay_queue.push_back((Instant::now(), opus_data)); + } + + // 3. 处理延迟播放:检查队列头部数据是否已等待超过 1s + while let Some((timestamp, _)) = delay_queue.front() { + if timestamp.elapsed() >= delay_duration { + let (_, opus_data) = delay_queue.pop_front().unwrap(); + + let mut pcm_out = vec![0i16; config.frame_size * config.channels as usize]; + let decoded_len = codec.decode(&opus_data, &mut pcm_out)?; + + if decoded_len == config.frame_size { + // 4. 播放还原后的 PCM + player.write(&pcm_out)?; + } + } else { + // 头部数据还未到 1s,由于队列是按时间排序的,后面的肯定也没到 + break; + } + } } } diff --git a/apps/runtime/Dockerfile b/apps/runtime/Dockerfile index 3562e90..bf2876a 100644 --- a/apps/runtime/Dockerfile +++ b/apps/runtime/Dockerfile @@ -31,17 +31,99 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y ENV PATH="/root/.cargo/bin:${PATH}" RUN rustup target add armv7-unknown-linux-gnueabihf -# 配置 Cargo -ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc -ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUSTFLAGS="-C link-arg=-Wl,-dynamic-linker,/lib/ld-linux-armhf.so.3" - -# 设置 CC -ENV CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc - WORKDIR /app COPY run.sh /usr/local/bin/run COPY runtime.sh /usr/local/bin/runtime RUN chmod +x /usr/local/bin/run /usr/local/bin/runtime +# 解压 root.squashfs +COPY root.squashfs /app/root.squashfs +RUN mkdir -p /opt/sysroot \ + && unsquashfs -d /opt/sysroot -f /app/root.squashfs \ + && rm -rf /app/root.squashfs + +# 创建专门针对 armhf 的源文件 +# 注意:ubuntu:20.04 默认源里没有 armhf,必须添加 ubuntu-ports 源 +RUN echo "deb [arch=armhf] http://mirrors.aliyun.com/ubuntu-ports/ focal main restricted universe multiverse" > /etc/apt/sources.list.d/armhf.list && \ + echo "deb [arch=armhf] http://mirrors.aliyun.com/ubuntu-ports/ focal-updates main restricted universe multiverse" >> /etc/apt/sources.list.d/armhf.list && \ + # 限制原有的源只针对 amd64,防止冲突 + sed -i 's/deb http/deb [arch=amd64] http/g' /etc/apt/sources.list + +# 安装 armhf 架构的开发库 +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + dpkg --add-architecture armhf && \ + apt-get update && apt-get install -y \ + crossbuild-essential-armhf \ + libasound2-dev:armhf \ + libopus-dev:armhf \ + libssl-dev:armhf + +# -------------------------------------------------------------------------------- +# 构建混合 Sysroot +# 目标:结合 OpenWrt 的运行时库 (root.squashfs) 和 Toolchain/Ubuntu 的开发文件 +# -------------------------------------------------------------------------------- + +# 修复 OpenWrt sysroot:自动为所有共享库创建 .so 符号链接 +# OpenWrt 只有 libfoo.so.1,编译链接需要 libfoo.so +RUN find /opt/sysroot/lib /opt/sysroot/usr/lib -name "lib*.so.*" | while read src; do \ + # 获取库名 (例如 libopus.so.0.8.0 -> libopus.so) + link_name=$(echo "$src" | sed 's/\.so\..*$/.so/'); \ + if [ ! -e "$link_name" ]; then \ + ln -s "$(basename "$src")" "$link_name"; \ + fi \ + done + +# 注入 Toolchain 的 C 运行时启动文件 (Scrt1.o, crti.o 等) +RUN cp -r /opt/toolchain/arm-linux-gnueabihf/libc/usr/lib/*crt*.o /opt/sysroot/usr/lib/ 2>/dev/null || \ + cp -r /opt/toolchain/arm-linux-gnueabihf/libc/lib/*crt*.o /opt/sysroot/usr/lib/ 2>/dev/null || true + +# 特殊处理 glibc 的 linker script +# OpenWrt rootfs 中没有开发用的 libc.so (linker script) 和 libc_nonshared.a +# 我们需要从 toolchain 中复制 libc_nonshared.a,并手动创建 libc.so 脚本 +# 这样 linker 才能正确处理 libc.so.6 和 ld-linux-armhf.so.3 的依赖关系 (解决 __tls_get_addr 错误) +RUN cp /opt/toolchain/arm-linux-gnueabihf/libc/usr/lib/libc_nonshared.a /opt/sysroot/usr/lib/ && \ + # 删除之前步骤可能自动生成的软链接 + rm -f /opt/sysroot/usr/lib/libc.so && \ + # 创建 linker script + echo 'OUTPUT_FORMAT(elf32-littlearm)' > /opt/sysroot/usr/lib/libc.so && \ + echo 'GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a AS_NEEDED ( /lib/ld-linux-armhf.so.3 ) )' >> /opt/sysroot/usr/lib/libc.so && \ + # 同理处理 libpthread.so (如果存在 libpthread.so.0) + if [ -f /opt/sysroot/lib/libpthread.so.0 ]; then \ + cp /opt/toolchain/arm-linux-gnueabihf/libc/usr/lib/libpthread_nonshared.a /opt/sysroot/usr/lib/ 2>/dev/null || true; \ + rm -f /opt/sysroot/usr/lib/libpthread.so; \ + echo 'OUTPUT_FORMAT(elf32-littlearm)' > /opt/sysroot/usr/lib/libpthread.so; \ + echo 'GROUP ( /lib/libpthread.so.0 /usr/lib/libpthread_nonshared.a )' >> /opt/sysroot/usr/lib/libpthread.so; \ + fi + +# 注入 Ubuntu 的开发头文件和 pkg-config 定义 (用于 alsa, opus 等) +# 我们只取头文件和 .pc 文件,不取库文件,确保链接的是 OpenWrt 的库 +RUN mkdir -p /opt/sysroot/usr/include /opt/sysroot/usr/lib/pkgconfig && \ + cp -r /usr/include/alsa /opt/sysroot/usr/include/ 2>/dev/null || true && \ + cp -r /usr/include/opus /opt/sysroot/usr/include/ 2>/dev/null || true && \ + cp -r /usr/include/arm-linux-gnueabihf/* /opt/sysroot/usr/include/ 2>/dev/null || true && \ + cp -r /usr/lib/arm-linux-gnueabihf/pkgconfig/* /opt/sysroot/usr/lib/pkgconfig/ 2>/dev/null || true + +# 优化 Rust 链接参数 +# 1. 优先使用 -L 指定 sysroot 路径 +# 2. 使用 -rpath-link 解决库的间接依赖问题 +# 3. 确保 dynamic-linker 路径正确 (OpenWrt glibc 常用路径) +ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc +ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUSTFLAGS="\ + -C link-arg=--sysroot=/opt/sysroot \ + -C link-arg=-Wl,-dynamic-linker,/lib/ld-linux-armhf.so.3 \ + -C link-arg=-L/opt/sysroot/usr/lib \ + -C link-arg=-L/opt/sysroot/lib \ + -C link-arg=-Wl,-rpath-link,/opt/sysroot/usr/lib \ + -C link-arg=-Wl,-rpath-link,/opt/sysroot/lib" + +# 强制 pkg-config 只在 sysroot 中寻找,忽略宿主机容器路径 +ENV PKG_CONFIG_ALLOW_CROSS=1 +ENV PKG_CONFIG_SYSROOT_DIR=/opt/sysroot +ENV PKG_CONFIG_LIBDIR=/opt/sysroot/usr/lib/pkgconfig:/opt/sysroot/lib/pkgconfig + +# 设置编译器,强制指定 sysroot +ENV CC_armv7_unknown_linux_gnueabihf="arm-linux-gnueabihf-gcc --sysroot=/opt/sysroot" + CMD ["/bin/bash"] diff --git a/apps/runtime/build.sh b/apps/runtime/build.sh index b4672ab..ef2a04d 100755 --- a/apps/runtime/build.sh +++ b/apps/runtime/build.sh @@ -12,25 +12,19 @@ PROXY="http://host.docker.internal:7890" # --build-arg https_proxy=$PROXY \ # -t open-xiaoai-runtime . -# # 2. 编译 hello world demo +# 2. 编译 hello world demo echo "Compiling hello demo..." -rm -rf ../hello/target # 先清理旧的构建产物,防止缓存干扰 docker run --rm -v $(pwd)/../hello:/app/hello open-xiaoai-runtime \ cargo build --manifest-path hello/Cargo.toml --target armv7-unknown-linux-gnueabihf --release -# # 3. 运行编译出的二进制文件 (通过 QEMU 模拟) -echo "Running compiled binary via QEMU..." -docker run --rm -v $(pwd)/../hello/target/armv7-unknown-linux-gnueabihf/release/hello:/app/hello \ - -v $(pwd)/root.squashfs:/app/root.squashfs open-xiaoai-runtime \ - run /app/hello +# 3. 运行编译出的二进制文件 (通过 QEMU 模拟) +# echo "Running compiled binary via QEMU..." +# docker run --rm -v $(pwd)/../hello/target/armv7-unknown-linux-gnueabihf/release/hello:/app/hello open-xiaoai-runtime \ +# run /app/hello # 4. 交互模式运行 -# docker run --rm -it --privileged \ -# -v $(pwd)/root.squashfs:/app/root.squashfs \ -# open-xiaoai-runtime \ -# run +# docker run --rm -it --privileged open-xiaoai-runtime run # 5. 上传二进制文件到小爱音箱 -# dd if=target/armv7-unknown-linux-gnueabihf/release/hello \ -# | ssh -o HostKeyAlgorithms=+ssh-rsa root@192.168.31.235 "dd of=/data/hello" -# ssh -o HostKeyAlgorithms=+ssh-rsa root@192.168.31.235 "dd if=/data/hello" | hello \ No newline at end of file +# dd if=apps/hello/target/armv7-unknown-linux-gnueabihf/release/hello \ +# | ssh -o HostKeyAlgorithms=+ssh-rsa root@192.168.31.235 "dd of=/data/hello" \ No newline at end of file diff --git a/apps/runtime/run.sh b/apps/runtime/run.sh index 78cda98..bd1450e 100644 --- a/apps/runtime/run.sh +++ b/apps/runtime/run.sh @@ -5,24 +5,6 @@ set -e # 定义 sysroot 目录 SYSROOT_DIR="/opt/sysroot" -# 如果 /opt/sysroot 已经有内容(通过 -v 挂载了 squashfs-root),则跳过解压 -if [ -d "$SYSROOT_DIR/bin" ] || [ -d "$SYSROOT_DIR/lib" ]; then - echo "Using existing sysroot at $SYSROOT_DIR" -else - mkdir -p "$SYSROOT_DIR" - # 优先使用环境变量指定的 squashfs 文件,否则使用默认的 root.squashfs - ROOTFS_FILE="${SYSROOT_FILE:-root.squashfs}" - - if [ -f "$ROOTFS_FILE" ]; then - echo -e "\n🔥 Unsquashing $ROOTFS_FILE to $SYSROOT_DIR...\n" - unsquashfs -d "$SYSROOT_DIR" -f "$ROOTFS_FILE" - echo -e "\n✅ Unsquashed rootfs\n" - else - echo "Error: Sysroot file $ROOTFS_FILE not found and $SYSROOT_DIR is empty." - exit 1 - fi -fi - # 检查第一个参数。如果在 sysroot 下存在该文件,则自动补全路径 # 比如 `run /bin/sh` 会被转换为 `run /opt/sysroot/bin/sh` if [ -n "$1" ] && [ -f "$SYSROOT_DIR$1" ]; then