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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
/*
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>
*/
pub mod extract;
pub mod fragment;
pub mod remux;
pub mod seek_index;
pub mod segment_extractor;
pub mod trim_writer;
pub use fragment::write_fragment_into;
pub use remux::remux_stream_into;
use jellybase::common::{SourceTrack, SourceTrackKind};
use jellymatroska::{Master, MatroskaTag};
pub fn ebml_header(webm: bool) -> MatroskaTag {
MatroskaTag::Ebml(Master::Collected(vec![
MatroskaTag::EbmlVersion(1),
MatroskaTag::EbmlReadVersion(1),
MatroskaTag::EbmlMaxIdLength(4),
MatroskaTag::EbmlMaxSizeLength(8),
MatroskaTag::DocType(if webm {
"webm".to_string()
} else {
"matroska".to_string()
}),
MatroskaTag::DocTypeVersion(4),
MatroskaTag::DocTypeReadVersion(2),
]))
}
pub fn ebml_segment_info(title: String, duration: f64) -> MatroskaTag {
MatroskaTag::Info(Master::Collected(vec![
MatroskaTag::TimestampScale(1_000_000),
MatroskaTag::Duration(duration * 1000.0),
MatroskaTag::Title(title),
MatroskaTag::MuxingApp("jellyremux".to_string()),
MatroskaTag::WritingApp("jellything".to_string()),
]))
}
pub fn ebml_track_entry(
number: u64,
uid: u64,
track: &SourceTrack,
codec_private: Option<Vec<u8>>,
) -> MatroskaTag {
let mut els = vec![
MatroskaTag::TrackNumber(number),
MatroskaTag::TrackUID(uid),
MatroskaTag::FlagLacing(0),
MatroskaTag::Language(track.language.clone()),
MatroskaTag::CodecID(track.codec.clone()),
MatroskaTag::CodecDelay(track.codec_delay.clone()),
MatroskaTag::SeekPreRoll(track.seek_pre_roll.clone()),
];
if let Some(d) = &track.default_duration {
els.push(MatroskaTag::DefaultDuration(*d));
}
match track.kind {
SourceTrackKind::Video {
width,
height,
display_height,
display_width,
display_unit,
fps,
} => {
els.push(MatroskaTag::TrackType(1));
let mut props = vec![
MatroskaTag::PixelWidth(width),
MatroskaTag::PixelHeight(height),
];
props.push(MatroskaTag::DisplayWidth(display_width.unwrap_or(width)));
props.push(MatroskaTag::DisplayHeight(display_height.unwrap_or(height)));
props.push(MatroskaTag::DisplayUnit(display_unit));
if let Some(fps) = fps {
props.push(MatroskaTag::FrameRate(fps))
}
els.push(MatroskaTag::Video(Master::Collected(props)))
}
SourceTrackKind::Audio {
channels,
sample_rate,
bit_depth,
} => {
els.push(MatroskaTag::TrackType(2));
let mut props = vec![
MatroskaTag::SamplingFrequency(sample_rate),
MatroskaTag::Channels(channels.try_into().unwrap()),
];
if let Some(bit_depth) = bit_depth {
props.push(MatroskaTag::BitDepth(bit_depth.try_into().unwrap()));
}
els.push(MatroskaTag::Audio(Master::Collected(props)));
}
SourceTrackKind::Subtitles => {
els.push(MatroskaTag::TrackType(17));
}
}
if let Some(d) = &codec_private {
els.push(MatroskaTag::CodecPrivate(d.clone()));
}
MatroskaTag::TrackEntry(Master::Collected(els))
}
|