aboutsummaryrefslogtreecommitdiff
path: root/stream/src/fragment.rs
diff options
context:
space:
mode:
Diffstat (limited to 'stream/src/fragment.rs')
-rw-r--r--stream/src/fragment.rs160
1 files changed, 50 insertions, 110 deletions
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)))
}