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
|
/*
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>
*/
#![feature(random, exit_status_error)]
pub mod extract;
pub mod fragment;
pub mod metadata;
pub mod matroska_to_mpeg4;
pub mod remux;
pub mod seek_index;
pub mod segment_extractor;
pub mod trim_writer;
pub mod matroska_to_webm;
use ebml_struct::matroska::TrackEntry;
pub use fragment::write_fragment_into;
use jellymatroska::{Master, MatroskaTag};
pub use matroska_to_mpeg4::matroska_to_mpeg4;
pub use remux::remux_stream_into;
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, track: &TrackEntry) -> MatroskaTag {
let mut els = vec![
MatroskaTag::TrackNumber(number),
MatroskaTag::FlagLacing(track.flag_lacing),
MatroskaTag::Language(track.language.clone()),
MatroskaTag::CodecID(track.codec_id.clone()),
MatroskaTag::CodecDelay(track.codec_delay),
MatroskaTag::SeekPreRoll(track.seek_pre_roll),
];
if let Some(d) = &track.default_duration {
els.push(MatroskaTag::DefaultDuration(*d));
}
match track.track_type {
1 => {
let video = track.video.as_ref().unwrap();
els.push(MatroskaTag::TrackType(1));
let mut props = vec![
MatroskaTag::PixelWidth(video.pixel_width),
MatroskaTag::PixelHeight(video.pixel_height),
];
props.push(MatroskaTag::DisplayWidth(
video.display_width.unwrap_or(video.pixel_width),
));
props.push(MatroskaTag::DisplayHeight(
video.display_height.unwrap_or(video.pixel_height),
));
props.push(MatroskaTag::DisplayUnit(video.display_unit));
if let Some(fps) = video.frame_rate {
props.push(MatroskaTag::FrameRate(fps))
}
els.push(MatroskaTag::Video(Master::Collected(props)))
}
2 => {
let audio = track.audio.as_ref().unwrap();
els.push(MatroskaTag::TrackType(2));
let mut props = vec![
MatroskaTag::SamplingFrequency(audio.sampling_frequency),
MatroskaTag::Channels(audio.channels),
];
if let Some(bit_depth) = audio.bit_depth {
props.push(MatroskaTag::BitDepth(bit_depth.try_into().unwrap()));
}
els.push(MatroskaTag::Audio(Master::Collected(props)));
}
17 => {
els.push(MatroskaTag::TrackType(17));
}
_ => unreachable!(),
}
if let Some(d) = &track.codec_private {
els.push(MatroskaTag::CodecPrivate(d.clone()));
}
MatroskaTag::TrackEntry(Master::Collected(els))
}
|