diff options
Diffstat (limited to 'stream/src')
-rw-r--r-- | stream/src/cues.rs | 2 | ||||
-rw-r--r-- | stream/src/fragment.rs | 160 |
2 files changed, 51 insertions, 111 deletions
diff --git a/stream/src/cues.rs b/stream/src/cues.rs index b486a6f..646db5b 100644 --- a/stream/src/cues.rs +++ b/stream/src/cues.rs @@ -16,7 +16,7 @@ pub struct TrackStat { pub total_size: u64, } -#[derive(Serialize, Deserialize)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub struct GeneratedCue { pub position: u64, pub time: u64, diff --git a/stream/src/fragment.rs b/stream/src/fragment.rs index a14732b..4cdc55e 100644 --- a/stream/src/fragment.rs +++ b/stream/src/fragment.rs @@ -3,12 +3,19 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2025 metamuffin <metamuffin.org> */ -use crate::{cues::generate_cues, stream_info, SMediaInfo}; +use crate::{ + cues::{generate_cues, GeneratedCue}, + stream_info, SMediaInfo, +}; use anyhow::{anyhow, Result}; use jellyremuxer::{ - demuxers::create_demuxer_autodetect, matroska, muxers::write_fragment, ContainerFormat, + demuxers::create_demuxer_autodetect, + matroska::{self, Segment}, + muxers::write_fragment, + ContainerFormat, }; use jellystream_types::{FormatNum, IndexNum, StreamContainer, TrackNum}; +use jellytranscoder::fragment::transcode; use std::{ fs::File, io::{Cursor, Read}, @@ -44,24 +51,35 @@ pub fn fragment_stream( .find(|t| t.track_number == track_num) .unwrap(); + let timestamp_scale = iinfo.metadata[file_index].info.timestamp_scale; + let total_duration = iinfo.metadata[file_index].info.duration; + let cue_stat = generate_cues(&media_path)?; + let start_cue = cue_stat + .cues + .get(index) + .ok_or(anyhow!("fragment index out of range"))?; + let end_cue = cue_stat + .cues + .get(index + 1) + .copied() + .unwrap_or(GeneratedCue { + position: 0, + time: total_duration.unwrap_or_default() as u64 * timestamp_scale, // TODO rounding? + }); + let cluster_offset = start_cue.position; + let duration = (end_cue.time - start_cue.time) as f64 / timestamp_scale as f64; + let mk_info = matroska::Info { - duration: Some(info.duration), - timestamp_scale: iinfo.metadata[file_index].info.timestamp_scale, + duration: Some(duration), + timestamp_scale, ..Default::default() }; let mk_tracks = matroska::Tracks { entries: vec![mk_track.to_owned()], }; - let cue_stat = generate_cues(&media_path)?; - let cluster_offset = cue_stat - .cues - .get(index) - .ok_or(anyhow!("fragment index out of range"))? - .position; - let mut cluster = { - let media_file = File::open(media_path)?; + let media_file = File::open(&media_path)?; let mut media = create_demuxer_autodetect(Box::new(media_file))? .ok_or(anyhow!("media container unknown"))?; media.seek_cluster(Some(cluster_offset))?; @@ -78,107 +96,29 @@ pub fn fragment_stream( StreamContainer::WebM => ContainerFormat::Webm, StreamContainer::Matroska => ContainerFormat::Matroska, StreamContainer::WebVTT => todo!(), - StreamContainer::MPEG4 => todo!(), + StreamContainer::MPEG4 => ContainerFormat::Mpeg4, StreamContainer::JVTT => todo!(), }; - if format.remux { - let mut out = Vec::new(); - write_fragment(jr_container, &mut out, mk_info, mk_tracks, cluster)?; - Ok(Box::new(Cursor::new(out))) - } else { - todo!() - } + let mut segment = Segment { + info: mk_info, + tracks: Some(mk_tracks), + clusters: vec![cluster], + ..Default::default() + }; + segment.info.writing_app = + concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")).to_string(); - // if format.remux { - // match container { - // StreamContainer::WebM | StreamContainer::Matroska => { - // tokio::task::spawn_blocking(move || { - // if let Err(err) = jellyremuxer::write_fragment_into( - // SyncIoBridge::new(b), - // &path, - // track_num, - // container == StreamContainer::WebM, - // &info.name.unwrap_or_default(), - // index, - // ) { - // warn!("segment stream error: {err}"); - // } - // }); - // } - // StreamContainer::MPEG4 => { - // tokio::task::spawn_blocking(move || { - // let mut buf = Cursor::new(Vec::new()); - // if let Err(err) = jellyremuxer::write_fragment_into( - // &mut buf, - // &path, - // track_num, - // false, - // &info.name.unwrap_or_default(), - // index, - // ) { - // warn!("segment stream error: {err}"); - // } - // buf.seek(SeekFrom::Start(0)).unwrap(); - // if let Err(err) = matroska_to_mpeg4(buf, SyncIoBridge::new(b)) { - // warn!("mpeg4 transmux failed: {err}"); - // } - // }); - // } - // StreamContainer::JVTT => {} - // _ => bail!("not yet supported"), - // } - // } else { - // let location = transcode( - // track.kind, - // orig_track, - // format, - // &format!("{path:?} {track_num} {index}"), - // move |b| { - // tokio::task::spawn_blocking(move || { - // if let Err(err) = jellyremuxer::write_fragment_into( - // SyncIoBridge::new(b), - // &path, - // track_num, - // false, - // &info.name.unwrap_or_default(), - // index, - // ) { - // warn!("segment stream error: {err}"); - // } - // }); - // }, - // ) - // .await?; + if !format.remux { + segment = transcode( + track.kind, + &format!("{media_path:?} {track_num} {index}"), + format, + segment, + )?; + } - // let mut frag = File::open(location.abs()).await?; - // match container { - // 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 { - // warn!("cannot write stream: {err}") - // } - // }); - // } - // StreamContainer::MPEG4 => { - // tokio::task::spawn_blocking(move || { - // if let Err(err) = - // matroska_to_mpeg4(SyncIoBridge::new(frag), SyncIoBridge::new(b)) - // { - // warn!("mpeg4 transmux failed: {err}"); - // } - // }); - // } - // _ => bail!("unsupported"), - // } - // } + let mut out = Vec::new(); + write_fragment(jr_container, &mut out, segment)?; + Ok(Box::new(Cursor::new(out))) } |