fix(flv): 使用 write_all 保证 FLV 标签与 JSON 输出完整写入 (#1676)

flv_writer 中三处使用 io::Write::write 写入数据:write 允许部分
写入且把已写字节数作为 Ok 返回,短写时 previous_tag_size 等
字段会静默缺字节,产出的 FLV 标签结构损坏,播放器与解析器
在该点之后无法继续解析。

将 write_tag 的 previous_tag_size 写入、write_previous_tag_size
与 to_json 的换行写入全部改为 write_all,公开签名保持不变。
新增短写 Writer 回归测试,验证 4 字节 previous_tag_size 在部分
写入场景下仍被完整写出。
This commit is contained in:
Noah authored and GitHub committed 2026-08-30 14:54:09 +00:00
1 parent 2c3592f117
commit 631e1160b1
1 file changed
+44 -3
+44 -3
View File
@@ -68,7 +68,9 @@ impl<'a> FlvFile<'a> {
) -> std::io::Result<usize> {
self.write_tag_header(tag_header)?;
self.buf_writer.write_all(body)?;
self.buf_writer.write(previous_tag_size)
// write 允许部分写入,短写会静默丢字节并破坏 FLV 结构,必须用 write_all
self.buf_writer.write_all(previous_tag_size)?;
Ok(previous_tag_size.len())
}
pub fn write_tag_header(&mut self, tag_header: &TagHeader) -> std::io::Result<()> {
@@ -86,7 +88,9 @@ impl<'a> FlvFile<'a> {
writer: &mut impl Write,
previous_tag_size: u32,
) -> std::io::Result<usize> {
writer.write(&previous_tag_size.to_be_bytes())
let bytes = previous_tag_size.to_be_bytes();
writer.write_all(&bytes)?;
Ok(bytes.len())
}
}
@@ -104,7 +108,8 @@ pub struct FlvTag<'a> {
pub fn to_json<T: ?Sized + Serialize>(mut writer: impl Write, t: &T) -> std::io::Result<usize> {
serde_json::to_writer(&mut writer, t)?;
writer.write("\n".as_ref())
writer.write_all(b"\n")?;
Ok(1)
}
#[derive(Debug, PartialEq, Serialize)]
@@ -124,3 +129,39 @@ pub enum TagDataHeader<'a> {
},
Script(ScriptData<'a>),
}
#[cfg(test)]
mod tests {
use super::*;
/// 模拟每次调用最多接受 1 字节的 Writer。
/// 对这类 Writer,`write` 只写入部分数据也返回 Ok,必须用 `write_all` 才能保证完整写入。
struct ShortWriter(Vec<u8>);
impl Write for ShortWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let take = buf.len().min(1);
self.0.extend_from_slice(&buf[..take]);
Ok(take)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[test]
fn previous_tag_size_is_fully_written_even_on_short_writes() {
let mut writer = ShortWriter(Vec::new());
let written = FlvFile::write_previous_tag_size(&mut writer, 0x0102_0304).unwrap();
assert_eq!(written, 4);
assert_eq!(writer.0, [0x01, 0x02, 0x03, 0x04]);
}
#[test]
fn to_json_writes_trailing_newline_even_on_short_writes() {
let mut writer = ShortWriter(Vec::new());
to_json(&mut writer, &serde_json::json!({"k": "v"})).unwrap();
assert!(writer.0.ends_with(b"\n"));
}
}