为何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
相关产品推荐
相关产品推荐

