You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Rodio的Sink类型在结构体中使用时无法播放音频?

问题分析与解决:Rodio结构体包含Sink时无法播放音频

核心问题

你的代码中,MusicStruct::new()方法内创建的OutputStream和StreamHandle是局部变量,方法执行完毕后这两个变量会被立即销毁。而rodio::Sink的正常运行完全依赖于关联的OutputStream和StreamHandle处于存活状态——当这两个核心对象被销毁后,Sink对应的音频输出流已关闭,自然无法播放音频,调用sleep_until_end()就会无限挂起。

反观文档推荐的写法,_stream和stream_handle的生命周期覆盖了Sink从创建到播放结束的全过程,音频流始终有效,因此能正常播放。

修正方案

方案1:让结构体持有音频流相关对象

修改结构体,将OutputStream和StreamHandle也纳入其中,确保它们的生命周期与Sink一致:

use rodio::{OutputStream, StreamHandle, Source, Sink, Decoder};
use std::fs::File;
use std::io::BufReader;

pub struct MusicStruct {
    sink: Sink,
    _stream: OutputStream,
    stream_handle: StreamHandle,
}

fn main() {
    // 修正后的调用:结构体内部持有流对象,可正常播放
    let mut music = MusicStruct::new();
    music.test_play();

    // 原文档推荐写法(保留对比)
    let (_stream, stream_handle) = OutputStream::try_default().unwrap();
    let sink = Sink::try_new(&stream_handle).unwrap();
    sink.append(read_file_from_beginning("my_mp3.mp3".to_string()));
    sink.play();
    sink.sleep_until_end()
}

fn read_file_from_beginning(file: String) -> Decoder<BufReader<File>> {
    let reader = BufReader::new(File::open(file).unwrap());
    Decoder::new(reader).unwrap()
}

impl MusicStruct {
    pub fn test_play(&mut self) {
        let file = read_file_from_beginning("my_mp3.mp3".to_string());
        self.sink.append(file);
        self.sink.play();
        self.sink.sleep_until_end()
    }

    pub fn new() -> MusicStruct {
        let (stream, stream_handle) = OutputStream::try_default().unwrap();
        let sink = Sink::try_new(&stream_handle).unwrap();
        MusicStruct {
            sink,
            _stream: stream,
            stream_handle,
        }
    }
}

方案2:外部创建流并传递给结构体

如果不想让结构体持有流对象,也可以在外部创建OutputStream和StreamHandle,确保它们的生命周期覆盖结构体的使用周期:

use rodio::{OutputStream, StreamHandle, Source, Sink, Decoder};
use std::fs::File;
use std::io::BufReader;

pub struct MusicStruct {
    sink: Sink,
}

fn main() {
    // 外部创建流,确保生命周期覆盖结构体使用
    let (_stream, stream_handle) = OutputStream::try_default().unwrap();
    let mut music = MusicStruct::new(&stream_handle);
    music.test_play();

    // 原文档推荐写法(保留对比)
    let (_stream, stream_handle) = OutputStream::try_default().unwrap();
    let sink = Sink::try_new(&stream_handle).unwrap();
    sink.append(read_file_from_beginning("my_mp3.mp3".to_string()));
    sink.play();
    sink.sleep_until_end()
}

fn read_file_from_beginning(file: String) -> Decoder<BufReader<File>> {
    let reader = BufReader::new(File::open(file).unwrap());
    Decoder::new(reader).unwrap()
}

impl MusicStruct {
    pub fn test_play(&mut self) {
        let file = read_file_from_beginning("my_mp3.mp3".to_string());
        self.sink.append(file);
        self.sink.play();
        self.sink.sleep_until_end()
    }

    pub fn new(stream_handle: &StreamHandle) -> MusicStruct {
        let sink = Sink::try_new(stream_handle).unwrap();
        MusicStruct { sink }
    }
}

额外优化点

原代码中test_play方法拿走了MusicStruct的所有权,这会导致结构体被消耗后无法复用。改用&mut self可变引用的方式,既能操作内部的Sink,又能保留结构体的所有权,方便后续复用。

内容的提问来源于stack exchange,提问作者NedNoodleHead

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 00:53:16