aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-04-16 17:24:08 +0200
committermetamuffin <metamuffin@disroot.org>2025-04-16 17:24:08 +0200
commitcdf95d7b80bd2b78895671da8f462145bb5db522 (patch)
treef7f8377ee6352b313a45cb13362bbd7143fddccd
parentad8016d8014af1e8dfb267fcdb51da63ab8ca4a9 (diff)
downloadjellything-cdf95d7b80bd2b78895671da8f462145bb5db522.tar
jellything-cdf95d7b80bd2b78895671da8f462145bb5db522.tar.bz2
jellything-cdf95d7b80bd2b78895671da8f462145bb5db522.tar.zst
webm and mpeg4 fragments semi fixedrewrite-stream
-rw-r--r--remuxer/src/lib.rs5
-rw-r--r--remuxer/src/matroska_to_mpeg4.rs (renamed from remuxer/src/mpeg4.rs)4
-rw-r--r--remuxer/src/matroska_to_webm.rs84
-rw-r--r--stream/src/fragment.rs14
m---------web/script/jshelper0
5 files changed, 101 insertions, 6 deletions
diff --git a/remuxer/src/lib.rs b/remuxer/src/lib.rs
index c20197f..931d5e6 100644
--- a/remuxer/src/lib.rs
+++ b/remuxer/src/lib.rs
@@ -7,16 +7,17 @@
pub mod extract;
pub mod fragment;
pub mod metadata;
-pub mod mpeg4;
+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 mpeg4::matroska_to_mpeg4;
+pub use matroska_to_mpeg4::matroska_to_mpeg4;
pub use remux::remux_stream_into;
pub fn ebml_header(webm: bool) -> MatroskaTag {
diff --git a/remuxer/src/mpeg4.rs b/remuxer/src/matroska_to_mpeg4.rs
index da66fe2..e8268e7 100644
--- a/remuxer/src/mpeg4.rs
+++ b/remuxer/src/matroska_to_mpeg4.rs
@@ -16,7 +16,9 @@ pub fn matroska_to_mpeg4(
mut output: impl Write,
) -> Result<()> {
let path = format!("/tmp/jellything-tc-hack-{:016x}", random::<u64>());
- let args = format!("-f matroska -i pipe:0 -copyts -c copy -f mp4 {path}");
+ let args = format!(
+ "-hide_banner -loglevel warning -f matroska -i pipe:0 -copyts -c copy -f mp4 -movflags frag_keyframe+empty_moov {path}"
+ );
let mut child = Command::new("ffmpeg")
.args(args.split(" "))
.stdin(Stdio::piped())
diff --git a/remuxer/src/matroska_to_webm.rs b/remuxer/src/matroska_to_webm.rs
new file mode 100644
index 0000000..b9a1819
--- /dev/null
+++ b/remuxer/src/matroska_to_webm.rs
@@ -0,0 +1,84 @@
+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(())
+}
diff --git a/stream/src/fragment.rs b/stream/src/fragment.rs
index 2ce3c78..dfe101e 100644
--- a/stream/src/fragment.rs
+++ b/stream/src/fragment.rs
@@ -6,7 +6,7 @@
use crate::{stream_info, SMediaInfo};
use anyhow::{anyhow, bail, Result};
use jellybase::common::stream::StreamContainer;
-use jellyremuxer::matroska_to_mpeg4;
+use jellyremuxer::{matroska_to_mpeg4, matroska_to_webm::matroska_to_webm};
use jellytranscoder::fragment::transcode;
use log::warn;
use std::sync::Arc;
@@ -72,10 +72,18 @@ pub async fn fragment_stream(
},
)
.await?;
- eprintln!("{:?}", location.abs());
+
let mut frag = File::open(location.abs()).await?;
match container {
- StreamContainer::WebM => {}
+ StreamContainer::WebM => {
+ tokio::task::spawn_blocking(move || {
+ if let Err(err) =
+ matroska_to_webm(SyncIoBridge::new(frag), SyncIoBridge::new(b))
+ {
+ warn!("webm transmux failed: {err}");
+ }
+ });
+ }
StreamContainer::Matroska => {
tokio::task::spawn(async move {
if let Err(err) = tokio::io::copy(&mut frag, &mut b).await {
diff --git a/web/script/jshelper b/web/script/jshelper
-Subproject b2bcdcc99e42015085b4d0d63e7c94b2d4f84e2
+Subproject ef36d50d7858a56cbc08bfb4f272bab9476bb97