#webm #matroska #parser #serialization #encoding #source-file

webm-iterable

此crate扩展了ebml-iterable库,以提供webm数据的迭代器。它提供了一种Matroska规范实现,用于读取webm文件所需的特性。

16次发布

0.6.2 2024年4月4日
0.6.1 2024年2月27日
0.6.0 2023年10月20日
0.5.4 2023年2月13日
0.3.0 2021年7月15日

#30 in 视频

Download history 73/week @ 2024-04-24 17/week @ 2024-05-01 12/week @ 2024-05-08 21/week @ 2024-05-15 79/week @ 2024-05-22 55/week @ 2024-05-29 19/week @ 2024-06-05 35/week @ 2024-06-12 8/week @ 2024-06-19 13/week @ 2024-06-26 9/week @ 2024-07-03 12/week @ 2024-07-10 6/week @ 2024-07-17 85/week @ 2024-07-24 34/week @ 2024-07-31 55/week @ 2024-08-07

每月下载 181次

MIT许可证

73KB
880

此crate是为了简化解析Matroska容器编码的文件而构建的,例如WebMsMKVs

[dependencies]
webm-iterable = "0.6.2"

使用方法

WebmIterator类型是使用MatroskaSpec作为泛型类型,将ebml-iterableTagIterator作为别名,并实现了Rust的标准Iterator特性。可以使用任何实现了标准Read特性的源上的new函数创建此结构。迭代器输出包含标签数据的MatroskaSpec变体。

注意:可以使用with_capacity方法构建具有指定默认缓冲区大小的WebmIterator。如果知道要读取的文件的最大标签大小,这仅作为内存管理的微优化是有用的。

然后可以根据需要修改标签中的数据(加密、压缩等),并使用WebmWriter类型重新编码。 WebmWriter简单地封装了ebml-iterableTagWriter。可以使用任何实现了标准Write特性的源上的new函数创建此结构。

有关迭代ebml数据的更多信息,请参阅ebml-iterable文档。

Matroska特定类型

此crate为特殊Matroska数据标签提供了三个附加结构

Block

pub struct Block {
    pub track: u64,
    pub timestamp: i16,

    pub invisible: bool,
    pub lacing: BlockLacing,
}

impl Block {
    pub fn read_frame_data(&self) -> Result<Vec<Frame>, WebmCoercionError>
    pub fn raw_frame_data(&self) -> &[u8]
}

这些属性是针对由Block元素定义的,该元素由Matroska定义。`Block`结构体实现了`TryFrom<&MatroskaSpec>`和`Into`,以简化向常规变体和从常规变体的转换。

SimpleBlock

pub struct SimpleBlock {
    pub track: u64,
    pub timestamp: i16,

    pub invisible: bool,
    pub lacing: Option<BlockLacing>,
    pub discardable: bool,
    pub keyframe: bool,
}

impl SimpleBlock {
    pub fn read_frame_data(&self) -> Result<Vec<Frame>, WebmCoercionError>
    pub fn raw_frame_data(&self) -> &[u8]
}

这些属性是针对由SimpleBlock元素定义的,该元素由Matroska定义。`SimpleBlock`结构体也实现了`TryFrom<&MatroskaSpec>`和`Into`,以简化向常规变体和从常规变体的转换。

示例

此示例将媒体文件读入内存并解码。

use std::fs::File;
use webm_iterable::WebmIterator;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut src = File::open("media/test.webm").unwrap();
    let tag_iterator = WebmIterator::new(&mut src, &[]);

    for tag in tag_iterator {
        println!("[{:?}]", tag?);
    }

    Ok(())
}

此示例执行相同操作,但记录每个标签在文件中出现的次数。

use std::fs::File;
use std::collections::HashMap;
use webm_iterable::WebmIterator;
use webm_iterable::matroska_spec::EbmlTag;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut src = File::open("media/test.webm").unwrap();
    let tag_iterator = WebmIterator::new(&mut src, &[]);
    let mut tag_counts = HashMap::new();

    for tag in tag_iterator {
        let count = tag_counts.entry(tag?.get_id()).or_insert(0);
        *count += 1;
    }
    
    println!("{:?}", tag_counts);
    Ok(())
}

此示例从webm文件中抓取音频并将结果存储在新文件中。此示例中的逻辑相当复杂 - 代码后的解释。

use std::fs::File;
use std::convert::TryInto;

use webm_iterable::{
    WebmIterator, 
    WebmWriter,
    matroska_spec::{MatroskaSpec, Master, Block, EbmlSpecification, EbmlTag},
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1
    let mut src = File::open("media/audiosample.webm").unwrap();
    let tag_iterator = WebmIterator::new(&mut src, &[MatroskaSpec::TrackEntry(Master::Start)]);
    let mut dest = File::create("media/audioout.webm").unwrap();
    let mut tag_writer = WebmWriter::new(&mut dest);
    let mut stripped_tracks = Vec::new();

    // 2
    for tag in tag_iterator {
        let tag = tag?;
        match tag {
            // 3
            MatroskaSpec::TrackEntry(master) => {
                let children = master.get_children();
                let is_audio_track = |tag: &MatroskaSpec| {
                    if let MatroskaSpec::TrackType(val) = tag {
                        return *val != 2;
                    } else {
                        false
                    }
                };

                if children.iter().any(is_audio_track) {
                    let track_number_variant = children.iter().find(|c| matches!(c, MatroskaSpec::TrackNumber(_))).expect("should have a k number child");
                    let track_number = track_number_variant.as_unsigned_int().expect("TrackNumber is an unsigned int variant");
                    stripped_tracks.push(*track_number);
                } else {
                    tag_writer.write(&MatroskaSpec::TrackEntry(Master::Full(children)))?;
                }
            },
            // 4
            MatroskaSpec::Block(ref data) => {
                let block: Block = data.try_into()?;
                if !stripped_tracks.iter().any(|t| *t == block.track) {
                    tag_writer.write(&tag)?;
                }
            },
            MatroskaSpec::SimpleBlock(ref data) => {
                let block: Block = data.try_into()?;
                if !stripped_tracks.iter().any(|t| *t == block.track) {
                    tag_writer.write(&tag)?;
                }
            },
            // 5
            _ => {
                tag_writer.write(&tag)?;
            }
        }
    }
    
    Ok(())
}

在上面的示例中,我们(1)根据本地文件路径构建我们的迭代器和写入器,并声明有用的局部变量,(2)遍历webm文件中的标签,(3)识别任何非音频轨道并将它们的编号存储在`stripped_tracks`变量中;如果是音频,我们将其"TrackEntry"写入,(4)仅写入音频轨道的数据块,并将所有其他标签写入输出目的地。

注意

  • 注意传递给`WebmIterator::new()`函数的第二个参数。此参数告诉解码器哪些"主"标签应作为`Master::Full`变体而不是标准`Master::Start`和`Master::End`变体读取。这极大地简化了我们的迭代循环逻辑,因为我们不需要维护一个内部缓冲区来存储我们感兴趣处理的"TrackEntry"标签。

本项目状态

解析和写入完整文件都应正常工作。从版本0.4.0开始,现在也支持流(使用未知大小的标签)。如果有什么问题,请创建一个问题

任何额外的功能请求也可以作为一个问题提交。

作者

Austin Blake

依赖项

~2.5MB
~52K SLoC