aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-01-28 17:55:11 +0100
committermetamuffin <metamuffin@disroot.org>2024-01-28 17:55:11 +0100
commit727be96686a2c6c5747b26be15933e11c9cab9c6 (patch)
tree817accf6c4965e814cc8736fdbd17b423fae0669
parent5b587f2914908daa804bb643ac216001290077ab (diff)
downloadjellything-727be96686a2c6c5747b26be15933e11c9cab9c6.tar
jellything-727be96686a2c6c5747b26be15933e11c9cab9c6.tar.bz2
jellything-727be96686a2c6c5747b26be15933e11c9cab9c6.tar.zst
clean up some code + subrip support?
-rw-r--r--common/src/impl.rs11
-rw-r--r--common/src/lib.rs6
-rw-r--r--remuxer/src/import/mod.rs18
-rw-r--r--remuxer/src/lib.rs27
-rw-r--r--stream/src/webvtt.rs71
-rw-r--r--transcoder/src/subtitles.rs29
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.,
});