1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
/*
This file is part of jellything (https://codeberg.org/metamuffin/jellything)
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use crate::ebml_track_entry;
use anyhow::Context;
use ebml_struct::{
ids::*,
matroska::{Cluster, Ebml, Info, Tracks},
read::{EbmlReadExt, TagRead},
write::TagWrite,
};
use jellymatroska::{read::EbmlReader, write::EbmlWriter, Master, MatroskaTag};
use log::warn;
use std::io::{BufReader, BufWriter, ErrorKind, Read, Seek, Write};
pub fn matroska_to_webm(
input: impl Read + Seek + 'static,
output: impl Write,
) -> anyhow::Result<()> {
let mut output = EbmlWriter::new(BufWriter::new(output), 0);
let mut input = EbmlReader::new(BufReader::new(input));
Ebml {
ebml_version: 1,
ebml_read_version: 1,
ebml_max_id_length: 4,
ebml_max_size_length: 8,
doc_type: "webm".to_string(),
doc_type_version: 4,
doc_type_read_version: 2,
doc_type_extensions: vec![],
}
.write(&mut output)?;
output.write_tag(&MatroskaTag::Segment(Master::Start))?;
let (x, mut ebml) = input.read_tag()?;
assert_eq!(x, EL_EBML);
let ebml = Ebml::read(&mut ebml).unwrap();
assert!(ebml.doc_type == "matroska" || ebml.doc_type == "webm");
let (x, mut segment) = input.read_tag()?;
assert_eq!(x, EL_SEGMENT);
loop {
let (x, mut seg) = match segment.read_tag() {
Ok(o) => o,
Err(e) if e.kind() == ErrorKind::UnexpectedEof => break,
Err(e) => return Err(e.into()),
};
match x {
EL_INFO => {
let info = Info::read(&mut seg).context("info")?;
output.write_tag(&{
MatroskaTag::Info(Master::Collected(vec![
MatroskaTag::TimestampScale(info.timestamp_scale),
MatroskaTag::Duration(info.duration.unwrap_or_default()),
MatroskaTag::Title(info.title.unwrap_or_default()),
MatroskaTag::MuxingApp("jellyremux".to_string()),
MatroskaTag::WritingApp("jellything".to_string()),
]))
})?;
}
EL_TRACKS => {
let tracks = Tracks::read(&mut seg).context("tracks")?;
output.write_tag(&MatroskaTag::Tracks(Master::Collected(
tracks
.entries
.into_iter()
.map(|t| ebml_track_entry(t.track_number, &t))
.collect(),
)))?;
}
EL_VOID | EL_CRC32 | EL_CUES | EL_SEEKHEAD | EL_ATTACHMENTS | EL_TAGS => {
seg.consume()?;
}
EL_CLUSTER => {
let cluster = Cluster::read(&mut seg).context("cluster")?;
// TODO mixing both ebml libraries :)))
cluster.write(&mut output)?;
}
id => {
warn!("unknown top-level element {id:x}");
seg.consume()?;
}
}
}
Ok(())
}
|