Skip to content

Commit ff3d30c

Browse files
committed
make things a bit nicer
1 parent de6f804 commit ff3d30c

File tree

6 files changed

+355
-150
lines changed

6 files changed

+355
-150
lines changed

iroh-livestream/Cargo.lock

+48
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

iroh-livestream/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ anyhow = "1.0.79"
1010
clap = { version = "4.5.0", features = ["derive"] }
1111
# ffmpeg-sidecar = "0.5.1"
1212
futures = "0.3.30"
13+
iroh-base = "0.12.0"
1314
iroh-net = "0.12.0"
1415
moq-pub = { git = "https://github.com/Frando/moq-rs.git", branch = "iroh" }
1516
moq-sub = { git = "https://github.com/Frando/moq-rs.git", branch = "iroh" }
1617
moq-transport = { git = "https://github.com/Frando/moq-rs.git", branch = "iroh" }
18+
postcard = "1.0.8"
1719
quinn = "0.10.2"
20+
serde = { version = "1.0.196", features = ["derive"] }
1821
thiserror = "1.0.57"
1922
tokio = { version = "1.36.0", features = ["full"] }
2023
tracing = "0.1.40"

iroh-livestream/src/ffmpeg.rs

+111-65
Original file line numberDiff line numberDiff line change
@@ -3,66 +3,79 @@ use std::process::Stdio;
33
use anyhow::{bail, Context, Result};
44
use tokio::{
55
io::{AsyncRead, AsyncWrite},
6-
process::Command,
6+
process::{Child, Command},
77
};
88
use tracing::{info, warn};
99

10-
// pub async fn ensure_ffmpeg_installed() -> anyhow::Result<()> {
11-
// use ffmpeg_sidecar::{command::ffmpeg_is_installed, paths::ffmpeg_path, version::ffmpeg_version};
12-
// if !ffmpeg_is_installed() {
13-
// tracing::info!("FFmpeg not found, downloading...");
14-
// tokio::task::spawn_blocking(|| {
15-
// ffmpeg_sidecar::download::auto_download().map_err(|e| anyhow!(format!("{e}")))
16-
// })
17-
// .await??;
18-
// }
19-
// let version = ffmpeg_version().map_err(|e| anyhow!(format!("{e}")))?;
20-
// println!("FFmpeg version: {}", version);
21-
// Ok(())
22-
// }
10+
pub fn capture_stdin() -> Result<impl AsyncRead + Send + Unpin + 'static> {
11+
let input = ["-i", "pipe:"];
12+
capture_ffmpeg(input.to_vec())
13+
}
2314

24-
pub fn publish() -> Result<impl AsyncRead + Send + Unpin + 'static> {
25-
// let bin = ffmpeg_path();
26-
let bin = "ffmpeg";
15+
pub fn capture_camera() -> Result<impl AsyncRead + Send + Unpin + 'static> {
2716
let input = match std::env::consts::OS {
17+
// TODO: this is same as desktop, find out if we can find the correct device
2818
"macos" => vec!["-f", "avfoundation", "-i", "default:default", "-r", "30"],
2919
"linux" => vec![
20+
"-f",
21+
"pulse",
22+
"-ac",
23+
"2",
24+
"-i",
25+
"default",
3026
"-f",
3127
"v4l2",
3228
"-i",
3329
"/dev/video0",
3430
"-r",
3531
"30",
32+
],
33+
"windows" => {
34+
// TODO: find out how windows dshow args work
35+
// likely have to get device name from `ffmpeg -list_devices`
36+
bail!("windows is not yet supported");
37+
}
38+
_ => bail!("Unsupported OS".to_string()),
39+
};
40+
capture_ffmpeg(input)
41+
}
42+
43+
pub fn capture_desktop() -> Result<impl AsyncRead + Send + Unpin + 'static> {
44+
let input = match std::env::consts::OS {
45+
// TODO: this is same as camera, find out if we can find the correct device
46+
"macos" => vec!["-f", "avfoundation", "-i", "default:default", "-r", "30"],
47+
"linux" => vec![
3648
"-f",
3749
"pulse",
3850
"-ac",
3951
"2",
4052
"-i",
4153
"default",
54+
"-framerate",
55+
"30",
56+
"-f",
57+
"x11grab",
58+
"-i",
59+
":0.0",
4260
],
4361
"windows" => {
44-
// TODO: find out how windows dshow args work
45-
// likely have to get device name from `ffmpeg -list_devices`
46-
bail!("windows is not yet supported");
62+
vec!["-f", "dshow", "-i", "video='screen-capture-recorder'"]
4763
}
4864
_ => bail!("Unsupported OS".to_string()),
4965
};
50-
// TODO: Find out if this actually helps, found it on the internets..
51-
let reduce_latency = [
52-
"-max_delay",
53-
"0",
54-
"-analyzeduration",
55-
"0",
56-
"-flags",
57-
"+low_delay",
58-
"-fflags",
59-
"+nobuffer",
60-
];
61-
let encode = [
66+
capture_ffmpeg(input)
67+
}
68+
69+
pub fn capture_ffmpeg(input: Vec<&'static str>) -> Result<impl AsyncRead + Send + Unpin + 'static> {
70+
let bin = match std::env::consts::OS {
71+
"windows" => "ffmpeg.exe",
72+
_ => "ffmpeg",
73+
};
74+
let encode_video = [
6275
"-vcodec",
6376
"libx264",
6477
"-preset",
65-
"ultrafast",
78+
"fast",
6679
"-tune",
6780
"zerolatency",
6881
];
@@ -77,16 +90,11 @@ pub fn publish() -> Result<impl AsyncRead + Send + Unpin + 'static> {
7790
"-",
7891
];
7992
let mut args = vec!["-hide_banner", "-v", "quiet"];
80-
args.extend_from_slice(&reduce_latency);
8193
args.extend_from_slice(&input);
82-
args.extend_from_slice(&encode);
94+
args.extend_from_slice(&encode_video);
8395
args.extend_from_slice(&output);
8496

85-
info!(
86-
"spawning ffmpeg: {} {}",
87-
bin,
88-
args.join(" ")
89-
);
97+
info!("spawn: {} {}", bin, args.join(" "));
9098
let mut cmd = Command::new(bin);
9199
cmd.args(args);
92100
cmd.stdout(Stdio::piped());
@@ -95,38 +103,76 @@ pub fn publish() -> Result<impl AsyncRead + Send + Unpin + 'static> {
95103
.stdout
96104
.take()
97105
.context("failed to capture FFmpeg stdout")?;
98-
// Ensure the child process is spawned in the runtime so it can
99-
// make progress on its own while we await for any output.
100-
tokio::spawn(async move {
101-
let status = child.wait().await;
102-
match status {
103-
Ok(status) => info!("FFmpeg exited with status {status}"),
104-
Err(err) => warn!("FFmpeg exited with error {err}"),
105-
}
106-
});
106+
tokio::spawn(wait_and_log(bin, child));
107107
Ok(stdout)
108108
}
109109

110-
pub fn subscribe() -> Result<impl AsyncWrite + Send + Unpin + 'static> {
111-
let bin = "ffplay";
112-
let args = ["-"];
110+
pub fn out_ffplay() -> Result<impl AsyncWrite + Send + Unpin + 'static> {
111+
let bin = match std::env::consts::OS {
112+
"windows" => "ffplay.exe",
113+
_ => "ffplay",
114+
};
115+
// TODO: Find out if this actually helps, found it on the internets..
116+
let args = [
117+
"-nostats",
118+
"-sync",
119+
"ext",
120+
"-max_delay",
121+
"0",
122+
"-analyzeduration",
123+
"0",
124+
"-flags",
125+
"+low_delay",
126+
"-fflags",
127+
"+nobuffer+fastseek+flush_packets",
128+
"-",
129+
];
113130

131+
info!("spawn: {} {}", bin, args.join(" "));
114132
let mut cmd = Command::new(bin);
115133
cmd.args(args);
116134
cmd.stdin(Stdio::piped());
117135
let mut child = cmd.spawn()?;
118-
let stdin = child
119-
.stdin
120-
.take()
121-
.context("failed to capture FFmpeg stdout")?;
122-
// Ensure the child process is spawned in the runtime so it can
123-
// make progress on its own while we await for any output.
124-
tokio::spawn(async move {
125-
let status = child.wait().await;
126-
match status {
127-
Ok(status) => info!("ffplay exited with status {status}"),
128-
Err(err) => warn!("ffplay exited with error {err}"),
129-
}
130-
});
136+
let stdin = child.stdin.take().context("failed to capture stdin")?;
137+
tokio::spawn(wait_and_log(bin, child));
131138
Ok(stdin)
132139
}
140+
141+
pub fn out_mpv() -> Result<impl AsyncWrite + Send + Unpin + 'static> {
142+
let bin = match std::env::consts::OS {
143+
"windows" => "mpv.exe",
144+
_ => "mpv",
145+
};
146+
let args = ["--profile=low-latency", "--no-cache", "--untimed", "-"];
147+
148+
info!("spawn: {} {}", bin, args.join(" "));
149+
let mut cmd = Command::new(bin);
150+
cmd.args(args);
151+
cmd.stdin(Stdio::piped());
152+
let mut child = cmd.spawn()?;
153+
let stdin = child.stdin.take().context("failed to capture stdin")?;
154+
tokio::spawn(wait_and_log(bin, child));
155+
Ok(stdin)
156+
}
157+
158+
async fn wait_and_log(name: &str, mut child: Child) {
159+
let status = child.wait().await;
160+
match status {
161+
Ok(status) => info!("{name} exited with status {status}"),
162+
Err(err) => warn!("{name} exited with error {err}"),
163+
}
164+
}
165+
166+
// pub async fn ensure_ffmpeg_installed() -> anyhow::Result<()> {
167+
// use ffmpeg_sidecar::{command::ffmpeg_is_installed, paths::ffmpeg_path, version::ffmpeg_version};
168+
// if !ffmpeg_is_installed() {
169+
// tracing::info!("FFmpeg not found, downloading...");
170+
// tokio::task::spawn_blocking(|| {
171+
// ffmpeg_sidecar::download::auto_download().map_err(|e| anyhow!(format!("{e}")))
172+
// })
173+
// .await??;
174+
// }
175+
// let version = ffmpeg_version().map_err(|e| anyhow!(format!("{e}")))?;
176+
// println!("FFmpeg version: {}", version);
177+
// Ok(())
178+
// }

0 commit comments

Comments
 (0)