diff options
author | metamuffin <metamuffin@disroot.org> | 2024-01-28 17:55:11 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-01-28 17:55:11 +0100 |
commit | 727be96686a2c6c5747b26be15933e11c9cab9c6 (patch) | |
tree | 817accf6c4965e814cc8736fdbd17b423fae0669 | |
parent | 5b587f2914908daa804bb643ac216001290077ab (diff) | |
download | jellything-727be96686a2c6c5747b26be15933e11c9cab9c6.tar jellything-727be96686a2c6c5747b26be15933e11c9cab9c6.tar.bz2 jellything-727be96686a2c6c5747b26be15933e11c9cab9c6.tar.zst |
clean up some code + subrip support?
-rw-r--r-- | common/src/impl.rs | 11 | ||||
-rw-r--r-- | common/src/lib.rs | 6 | ||||
-rw-r--r-- | remuxer/src/import/mod.rs | 18 | ||||
-rw-r--r-- | remuxer/src/lib.rs | 27 | ||||
-rw-r--r-- | stream/src/webvtt.rs | 71 | ||||
-rw-r--r-- | transcoder/src/subtitles.rs | 29 |
6 files changed, 86 insertions, 76 deletions
diff --git a/common/src/impl.rs b/common/src/impl.rs index 009b7c7..25cc47d 100644 --- a/common/src/impl.rs +++ b/common/src/impl.rs @@ -27,14 +27,19 @@ impl AssetRole { impl std::fmt::Display for SourceTrack { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let kspec = match &self.kind { - SourceTrackKind::Video { width, height, fps } => { - format!("Video: {width}x{height} {fps}fps ") + SourceTrackKind::Video { + width, height, fps, .. + } => { + format!("Video: {width}x{height} {}fps ", fps.unwrap_or(0.)) } SourceTrackKind::Audio { channels, sample_rate, bit_depth, - } => format!("Audio: {channels}ch {sample_rate}Hz {bit_depth}bits "), + } => format!( + "Audio: {channels}ch {sample_rate}Hz {}bits ", + bit_depth.unwrap_or(0) + ), SourceTrackKind::Subtitles => "Subtitles: ".to_string(), }; f.write_fmt(format_args!( diff --git a/common/src/lib.rs b/common/src/lib.rs index 56d0d2e..8878edc 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -224,12 +224,14 @@ pub enum SourceTrackKind { Video { width: u64, height: u64, - fps: f64, + display_width: Option<u64>, + display_height: Option<u64>, + fps: Option<f64>, }, Audio { channels: usize, sample_rate: f64, - bit_depth: usize, + bit_depth: Option<usize>, }, Subtitles, } diff --git a/remuxer/src/import/mod.rs b/remuxer/src/import/mod.rs index d6fa2d0..e26b575 100644 --- a/remuxer/src/import/mod.rs +++ b/remuxer/src/import/mod.rs @@ -263,6 +263,8 @@ fn import_read_segment(segment: &mut Unflatten) -> Result<MatroskaMetadata> { mut channels, mut width, mut height, + mut display_width, + mut display_height, mut name, mut fps, mut bit_depth, @@ -270,7 +272,7 @@ fn import_read_segment(segment: &mut Unflatten) -> Result<MatroskaMetadata> { mut default_duration, ) = ( None, None, None, None, None, None, None, None, None, None, None, - None, None, + None, None, None, None, ); while let Some(Ok(Unflat { children, item, .. })) = children.n() { match item { @@ -302,6 +304,12 @@ fn import_read_segment(segment: &mut Unflatten) -> Result<MatroskaMetadata> { match item { MatroskaTag::PixelWidth(v) => width = Some(v), MatroskaTag::PixelHeight(v) => height = Some(v), + MatroskaTag::DisplayWidth(v) => { + display_width = Some(v) + } + MatroskaTag::DisplayHeight(v) => { + display_height = Some(v) + } MatroskaTag::FrameRate(v) => fps = Some(v), _ => (), } @@ -313,13 +321,15 @@ fn import_read_segment(segment: &mut Unflatten) -> Result<MatroskaMetadata> { let track_index = index.unwrap(); let kind = match kind.ok_or(anyhow!("track type required"))? { 1 => SourceTrackKind::Video { - fps: fps.unwrap_or(0.0), // TODO + fps, width: width.unwrap(), height: height.unwrap(), + display_width, + display_height, }, 2 => SourceTrackKind::Audio { - bit_depth: bit_depth.unwrap_or(0) as usize, // TODO - channels: channels.unwrap_or(1), // TODO + bit_depth: bit_depth.map(|x| x as usize), + channels: channels.unwrap_or(1), // TODO sample_rate: sample_rate.unwrap_or(41_100.0), // TODO }, 17 => SourceTrackKind::Subtitles, diff --git a/remuxer/src/lib.rs b/remuxer/src/lib.rs index 96aeca1..b49dedc 100644 --- a/remuxer/src/lib.rs +++ b/remuxer/src/lib.rs @@ -61,13 +61,25 @@ pub fn ebml_track_entry( SourceTrackKind::Video { width, height, - fps: _, + display_height, + display_width, + fps, } => { els.push(MatroskaTag::TrackType(1)); - els.push(MatroskaTag::Video(Master::Collected(vec![ + let mut props = vec![ MatroskaTag::PixelWidth(width), MatroskaTag::PixelHeight(height), - ]))) + ]; + if let Some(display_width) = display_width { + props.push(MatroskaTag::DisplayWidth(display_width)) + } + if let Some(display_height) = display_height { + props.push(MatroskaTag::DisplayHeight(display_height)) + } + if let Some(fps) = fps { + props.push(MatroskaTag::FrameRate(fps)) + } + els.push(MatroskaTag::Video(Master::Collected(props))) } SourceTrackKind::Audio { channels, @@ -75,11 +87,14 @@ pub fn ebml_track_entry( bit_depth, } => { els.push(MatroskaTag::TrackType(2)); - els.push(MatroskaTag::Audio(Master::Collected(vec![ + let mut props = vec![ MatroskaTag::SamplingFrequency(sample_rate), MatroskaTag::Channels(channels.try_into().unwrap()), - ]))); - els.push(MatroskaTag::BitDepth(bit_depth.try_into().unwrap())); + ]; + if let Some(bit_depth) = bit_depth { + props.push(MatroskaTag::BitDepth(bit_depth.try_into().unwrap())); + } + els.push(MatroskaTag::Audio(Master::Collected(props))); } SourceTrackKind::Subtitles => { els.push(MatroskaTag::TrackType(17)); diff --git a/stream/src/webvtt.rs b/stream/src/webvtt.rs index e720800..3c9ec41 100644 --- a/stream/src/webvtt.rs +++ b/stream/src/webvtt.rs @@ -3,13 +3,11 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2024 metamuffin <metamuffin.org> */ -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{anyhow, Context, Result}; use jellybase::CONF; -use jellycommon::{jhls::SubtitleCue, stream::StreamSpec, LocalTrack, Node}; +use jellycommon::{stream::StreamSpec, LocalTrack, Node}; use jellyremuxer::extract::extract_track; -use jellytranscoder::subtitles::{ - parse_ass_blocks, parse_pgs_blocks, parse_webvtt_blocks, write_webvtt, -}; +use jellytranscoder::subtitles::{parse_subtitles, write_webvtt}; use tokio::io::{AsyncWriteExt, DuplexStream}; pub async fn vtt_stream( @@ -26,60 +24,21 @@ pub async fn vtt_stream( let tracki = *spec.tracks.get(0).ok_or(anyhow!("no track selected"))?; let local_track = local_tracks.get(0).ok_or(anyhow!("no tracks"))?.clone(); let track = &node.public.media.unwrap().tracks[tracki]; + let cp = local_track.codec_private.clone(); - let write = |blocks: Vec<SubtitleCue>| -> anyhow::Result<()> { - let output = if json { - serde_json::to_string(&blocks)? - } else { - write_webvtt(node.public.title.clone().unwrap_or_default(), blocks) - .context("writing webvtt")? - }; - tokio::task::spawn(async move { - let _ = b.write_all(output.as_bytes()).await; - }); - Ok(()) - }; - - match track.codec.as_str() { - "D_WEBVTT/SUBTITLES" => { - let webvtt_blocks = tokio::task::spawn_blocking(move || { - extract_track(CONF.media_path.clone(), local_track) - }) - .await??; - - let subtitles = parse_webvtt_blocks(webvtt_blocks).context("parsing subtitles")?; - write(subtitles)?; - } - "S_HDMV/PGS" => { - let webvtt_blocks = tokio::task::spawn_blocking(move || { - extract_track(CONF.media_path.clone(), local_track) - }) + let blocks = + tokio::task::spawn_blocking(move || extract_track(CONF.media_path.clone(), local_track)) .await??; - let subtitles = parse_pgs_blocks(webvtt_blocks).context("parsing subtitles")?; - write(subtitles)?; - } - "S_HDMV/TEXTST" => bail!("no HDMV/PGSs yet"), - "S_ARISUB" => bail!("no arisub yet"), - "S_TEXT/UTF8" => bail!("no subrip yet"), - "S_VOBSUB" => bail!("no vobsub yet"), - "S_TEXT/ASS" => { - let codec_private = local_track - .codec_private - .clone() - .ok_or(anyhow!("ASS is missing required codec private data"))?; - - let ass_blocks = tokio::task::spawn_blocking(move || { - extract_track(CONF.media_path.clone(), local_track) - }) - .await??; - - let subtitles = - parse_ass_blocks(codec_private, ass_blocks).context("parsing subtitles")?; - write(subtitles)?; - } - - x => bail!("unknown sub codec {x:?}"), + let subtitles = parse_subtitles(&track.codec, cp, blocks)?; + let output = if json { + serde_json::to_string(&subtitles)? + } else { + write_webvtt(node.public.title.clone().unwrap_or_default(), subtitles) + .context("writing webvtt")? }; + tokio::task::spawn(async move { + let _ = b.write_all(output.as_bytes()).await; + }); Ok(()) } diff --git a/transcoder/src/subtitles.rs b/transcoder/src/subtitles.rs index 84db5a9..9118ebc 100644 --- a/transcoder/src/subtitles.rs +++ b/transcoder/src/subtitles.rs @@ -3,11 +3,30 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2024 metamuffin <metamuffin.org> */ -use anyhow::anyhow; +use anyhow::{anyhow, bail, Context}; use jellycommon::jhls::SubtitleCue; use std::fmt::Write; -// TODO discontinued for now. since this should be snippetized aswell. +pub fn parse_subtitles( + codec: &str, + codec_private: Option<Vec<u8>>, + blocks: Vec<(u64, u64, Vec<u8>)>, +) -> anyhow::Result<Vec<SubtitleCue>> { + match codec { + "D_WEBVTT/SUBTITLES" => parse_webvtt_blocks(blocks), + "S_HDMV/PGS" => bail!("no HDMV/PGS yet"), + "S_HDMV/TEXTST" => bail!("no HDMV/PGS yet"), + "S_ARISUB" => bail!("no arisub yet"), + "S_TEXT/UTF8" => parse_subrip_blocks(blocks), + "S_VOBSUB" => bail!("no vobsub yet"), + "S_TEXT/ASS" => parse_ass_blocks( + codec_private.ok_or(anyhow!("ass without CodecPrivate"))?, + blocks, + ), + x => bail!("unknown sub codec {x:?}"), + } + .context(anyhow!("parsing {codec} subtitles")) +} pub fn write_webvtt(title: String, subtitles: Vec<SubtitleCue>) -> anyhow::Result<String> { let mut out = String::new(); @@ -39,12 +58,12 @@ pub fn parse_webvtt_blocks(blocks: Vec<(u64, u64, Vec<u8>)>) -> anyhow::Result<V } Ok(out) } -pub fn parse_pgs_blocks(blocks: Vec<(u64, u64, Vec<u8>)>) -> anyhow::Result<Vec<SubtitleCue>> { +pub fn parse_subrip_blocks(blocks: Vec<(u64, u64, Vec<u8>)>) -> anyhow::Result<Vec<SubtitleCue>> { let mut out = Vec::new(); for (pts, dur, block) in blocks { - let _content = String::from_utf8_lossy(&block).trim().to_string(); + let content = String::from_utf8_lossy(&block).trim().to_string(); out.push(SubtitleCue { - content: "PGS stub".to_string(), + content, start: pts as f64 / 1000., end: (pts + dur) as f64 / 1000., }); |