diff options
author | metamuffin <metamuffin@disroot.org> | 2025-04-16 17:24:08 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-04-16 17:24:08 +0200 |
commit | cdf95d7b80bd2b78895671da8f462145bb5db522 (patch) | |
tree | f7f8377ee6352b313a45cb13362bbd7143fddccd | |
parent | ad8016d8014af1e8dfb267fcdb51da63ab8ca4a9 (diff) | |
download | jellything-cdf95d7b80bd2b78895671da8f462145bb5db522.tar jellything-cdf95d7b80bd2b78895671da8f462145bb5db522.tar.bz2 jellything-cdf95d7b80bd2b78895671da8f462145bb5db522.tar.zst |
webm and mpeg4 fragments semi fixedrewrite-stream
-rw-r--r-- | remuxer/src/lib.rs | 5 | ||||
-rw-r--r-- | remuxer/src/matroska_to_mpeg4.rs (renamed from remuxer/src/mpeg4.rs) | 4 | ||||
-rw-r--r-- | remuxer/src/matroska_to_webm.rs | 84 | ||||
-rw-r--r-- | stream/src/fragment.rs | 14 | ||||
m--------- | web/script/jshelper | 0 |
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 |