将Rodio的Sink包装进结构体时,如何避免其被自动析构?
问题分析与解决方案
核心问题根源
你当前遇到的音频意外停止,并非是Sink被析构导致的,而是函数中创建的OutputStream(即原代码里的_stream变量)在try_new_from_haudio返回时被自动析构了。根据Rodio的设计,OutputStreamHandle的有效性完全依赖于对应的OutputStream实例存活,一旦OutputStream被销毁,整个音频输出通道会关闭,所有关联的Sink播放都会终止。
你之前误以为是Sink被析构,是因为把sleep_until_end放在返回前时,_stream还未被析构,音频能正常播放;返回后_stream被销毁,播放就停止了。
正确解决方案:保存OutputStream到HAudioSink
修改HAudioSink结构体,将OutputStream和Sink一起保存,确保OutputStream在HAudioSink的生命周期内始终存活:
struct HAudioSink { stream: OutputStream, // 新增:保存OutputStream实例 sink: Sink, } impl HAudioSink { pub fn try_new_from_haudio<T>(haudio: HAudio<T>) -> HResult<Self> where T: NativeType + Float + ToPrimitive, { let (stream, stream_handle) = OutputStream::try_default()?; // 去掉变量名前的下划线 let sink = Sink::try_new(&stream_handle).unwrap(); // 后续音频数据处理逻辑保持不变 let nchannels = haudio.nchannels(); let nframes = haudio.nframes(); let sr = haudio.sr(); let mut data_interleaved: Vec<f32> = Vec::with_capacity(nchannels * nframes); let values = haudio .inner() .inner() .values() .as_any() .downcast_ref::<PrimitiveArray<T>>() .unwrap(); for f in 0..nframes { for ch in 0..nchannels { data_interleaved.push(values.value(f + ch * nframes).to_f32().unwrap()); } } let source = SamplesBuffer::new(u16::try_from(nchannels).unwrap(), sr, data_interleaved); sink.append(source); Ok(HAudioSink { stream, sink }) // 将stream和sink一起封装返回 } // Sleeps the current thread until the sound ends. pub fn sleep_until_end(&self) { self.sink.sleep_until_end(); } }
这样,当HAudioSink实例存活时,OutputStream和Sink都会保持有效,音频播放不会被意外终止。
关于ManuallyDrop的疑问
ManuallyDrop在这里不是正确的解决方案。它的作用是手动控制值的析构时机,而你的问题本质是需要让依赖对象(OutputStream)存活,而非阻止Sink的正常析构。滥用ManuallyDrop反而会引入内存安全风险,比如忘记手动销毁导致资源泄漏。
内容的提问来源于stack exchange,提问作者daniellga
相关产品推荐
相关产品推荐

