Skip to content

Commit

Permalink
use sidecar for ffmpeg
Browse files Browse the repository at this point in the history
  • Loading branch information
Brendonovich committed Sep 11, 2024
1 parent 610adf2 commit ce72ee8
Show file tree
Hide file tree
Showing 14 changed files with 126 additions and 104 deletions.
1 change: 0 additions & 1 deletion .github/prebuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { fileURLToPath } from "node:url";
import { exec as execCb } from "node:child_process";
import { env } from "node:process";
import { promisify } from "node:util";
import extract from "extract-zip";

const exec = promisify(execCb);
const signId = env.APPLE_SIGNING_IDENTITY || "-";
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ jobs:
- name: Build MacOS Apps
working-directory: apps/desktop
run: |
export TARGET_TRIPLE=${{ matrix.settings.target }}
node ${{ github.workspace }}/.github/prebuild.js ${{ matrix.settings.prebuild }}
pnpm tauri build --target ${{ matrix.settings.target }}
env:
Expand Down
4 changes: 3 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ members = [

[workspace.dependencies]
ffmpeg-next = { git = "https://github.com/CapSoftware/rust-ffmpeg", rev = "11f8d264bbc0" }
tauri = { version = "2.0.0-rc" }
71 changes: 71 additions & 0 deletions apps/desktop/scripts/prepareSidecars.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as fs from "node:fs/promises";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { exec as execCb } from "node:child_process";
import { promisify } from "node:util";

const exec = promisify(execCb);

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const binariesDir = path.join(__dirname, "../../../target/binaries");

const FFMPEG_BINARIES = {
"aarch64-apple-darwin": {
url: "https://cap-ffmpeg.s3.amazonaws.com/ffmpegarm.zip",
path: "./ffmpeg",
},
"x86_64-apple-darwin": {
url: "https://cap-ffmpeg.s3.amazonaws.com/ffmpeg-7.0.1.zip",
path: "./ffmpeg",
},
};

async function getRustupTarget() {
const { stdout } = await exec("rustup show");
const line = stdout
.split("\n")
.find((line) => line.includes("Default host:"));

return line.split(":")[1].trim();
}

async function exists(path) {
return await fs
.access(path)
.then(() => true)
.catch(() => false);
}

async function main() {
const targetTriple = process.env.TARGET_TRIPLE ?? (await getRustupTarget());
const binaries = FFMPEG_BINARIES[targetTriple];

const ffmpegDownloadPath = path.join(binariesDir, "ffmpeg-download");
if (!(await exists(ffmpegDownloadPath))) {
await fs.mkdir(binariesDir, { recursive: true });
console.log("downloading ffmpeg archive");
const archive = await fetch(binaries.url)
.then((r) => r.blob())
.then((b) => b.arrayBuffer())
.then((a) => Buffer.from(a));

await fs.writeFile(ffmpegDownloadPath, archive);
}

const ffmpegUnzippedPath = path.join(binariesDir, "ffmpeg-unzipped");
if (!(await exists(ffmpegUnzippedPath))) {
console.log("extracting ffmpeg archive");
await exec(`unzip ${ffmpegDownloadPath} -d ${ffmpegUnzippedPath}`);
}

const ffmpegSidecarName = `ffmpeg-${targetTriple}`;
console.log(`copying ffmpeg binary '${ffmpegSidecarName}`);
await fs.copyFile(
path.join(ffmpegUnzippedPath, binaries.path),
path.join(binariesDir, ffmpegSidecarName)
);
}

main();
4 changes: 2 additions & 2 deletions apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "desktop"
version = "0.3.0-alpha.1"
version = "0.3.0-alpha.2"
description = "A Tauri App"
authors = ["you"]
edition = "2021"
Expand All @@ -16,7 +16,7 @@ tauri-build = { version = "2.0.0-rc", features = [] }
swift-rs = { version = "1.0.6", features = ["build"] }

[dependencies]
tauri = { version = "2.0.0-rc", features = [
tauri = { workspace = true, features = [
"macos-private-api",
"protocol-asset",
"tray-icon",
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
fn main() {
tauri_build::build()
tauri_build::build();
}
51 changes: 23 additions & 28 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,7 @@ async fn remove_fake_window(

const PREV_RECORDINGS_WINDOW: &str = "prev-recordings";

// must not be async bc of panel
#[tauri::command]
#[specta::specta]
fn show_previous_recordings_window(app: AppHandle) {
Expand Down Expand Up @@ -908,7 +909,7 @@ fn show_previous_recordings_window(app: AppHandle) {
window.make_transparent().ok();
let panel = window.to_panel().unwrap();

panel.set_level(NSMainMenuWindowLevel + 1);
panel.set_level(NSMainMenuWindowLevel);

panel.set_collection_behaviour(
NSWindowCollectionBehavior::NSWindowCollectionBehaviorTransient
Expand Down Expand Up @@ -962,7 +963,7 @@ fn show_previous_recordings_window(app: AppHandle) {
});
}

#[tauri::command]
#[tauri::command(async)]
#[specta::specta]
fn open_editor(app: AppHandle, id: String) {
let window = WebviewWindow::builder(
Expand All @@ -983,7 +984,7 @@ fn open_editor(app: AppHandle, id: String) {
window.set_traffic_lights_inset(20.0, 48.0).unwrap();
}

#[tauri::command]
#[tauri::command(async)]
#[specta::specta]
fn close_previous_recordings_window(app: AppHandle) {
if let Ok(panel) = app.get_webview_panel(PREV_RECORDINGS_WINDOW) {
Expand All @@ -1006,7 +1007,7 @@ fn on_recording_options_change(app: &AppHandle, options: &RecordingOptions) {
RecordingOptionsChanged.emit(app).ok();
}

#[tauri::command]
#[tauri::command(async)]
#[specta::specta]
fn focus_captures_panel(app: AppHandle) {
let panel = app.get_webview_panel(PREV_RECORDINGS_WINDOW).unwrap();
Expand Down Expand Up @@ -1089,7 +1090,7 @@ async fn save_project_config(app: AppHandle, video_id: String, config: ProjectCo
.unwrap();
}

#[tauri::command]
#[tauri::command(async)]
#[specta::specta]
fn open_in_finder(path: PathBuf) {
Command::new("open")
Expand All @@ -1111,7 +1112,7 @@ async fn list_audio_devices() -> Result<Vec<String>, ()> {
.map_err(|_| ())
}

#[tauri::command]
#[tauri::command(async)]
#[specta::specta]
fn open_main_window(app: AppHandle) {
if let Some(window) = app.get_webview_window("main") {
Expand Down Expand Up @@ -1296,12 +1297,6 @@ pub fn run() {

let app_handle = app.handle().clone();

if let Err(_error) = FFmpeg::install_if_necessary() {
println!("Failed to install FFmpeg, which is required for Cap to function. Shutting down now");
// TODO: UI message instead
panic!("Failed to install FFmpeg, which is required for Cap to function. Shutting down now")
};

if permissions::do_permissions_check().necessary_granted() {
open_main_window(app_handle.clone());
} else {
Expand All @@ -1313,7 +1308,7 @@ pub fn run() {
start_recording_options: RecordingOptions {
capture_target: CaptureTarget::Screen,
camera_label: None,
audio_input_name: None
audio_input_name: None,
},
current_recording: None,
})));
Expand All @@ -1334,21 +1329,21 @@ pub fn run() {
Ok(())
})
.on_window_event(|window, event| {
let label = window.label();
if label.starts_with("editor-") {
if let WindowEvent::CloseRequested {..} = event {
let id = label.strip_prefix("editor-").unwrap().to_string();

let app = window.app_handle().clone();

tokio::spawn(async move {
if let Some(editor) = remove_editor_instance(&app, id.clone()).await {
editor.dispose().await;
}
});
}
}
})
let label = window.label();
if label.starts_with("editor-") {
if let WindowEvent::CloseRequested { .. } = event {
let id = label.strip_prefix("editor-").unwrap().to_string();

let app = window.app_handle().clone();

tokio::spawn(async move {
if let Some(editor) = remove_editor_instance(&app, id.clone()).await {
editor.dispose().await;
}
});
}
}
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Expand Down
7 changes: 4 additions & 3 deletions apps/desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"productName": "Cap",
"identifier": "so.cap.desktop",
"build": {
"beforeDevCommand": "pnpm localdev",
"beforeDevCommand": "node scripts/prepareSidecars.js && pnpm localdev",
"devUrl": "http://localhost:3001",
"beforeBuildCommand": "pnpm build",
"beforeBuildCommand": "node scripts/prepareSidecars.js && pnpm build",
"frontendDist": "../.output/public"
},
"app": {
Expand Down Expand Up @@ -37,6 +37,7 @@
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
],
"externalBin": ["../../../target/binaries/ffmpeg"]
}
}
2 changes: 2 additions & 0 deletions crates/ffmpeg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ edition = "2021"
[dependencies]
ffmpeg-sidecar = "1.1.0"
cap-utils = { path = "../utils" }
tauri = { workspace = true }
tauri-plugin-shell = "2.0.0-rc"
59 changes: 14 additions & 45 deletions crates/ffmpeg/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
mod utils;
pub use utils::*;

use cap_utils::create_named_pipe;
use std::{
ffi::OsString,
io::{Read, Write},
ops::Deref,
path::PathBuf,
path::{Path, PathBuf},
process::{Child, ChildStdin, Command, Stdio},
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};

use ffmpeg_sidecar::{
command::ffmpeg_is_installed,
download::{check_latest_version, download_ffmpeg_package, ffmpeg_download_url, unpack_ffmpeg},
paths::sidecar_dir,
version::ffmpeg_version,
};

use cap_utils::create_named_pipe;
use tauri::utils::platform;

pub struct FFmpegProcess {
pub ffmpeg_stdin: ChildStdin,
Expand Down Expand Up @@ -202,6 +192,8 @@ impl ApplyFFmpegArgs for FFmpegRawVideoInput {
command.args(["-r", &self.fps.to_string()]);
}

dbg!(PathBuf::from(&self.input).exists());

// if self.offset != 0.0 {
// command.args(["-itsoffset", &self.offset.to_string()]);
// }
Expand Down Expand Up @@ -247,18 +239,10 @@ pub struct FFmpeg {
source_index: u8,
}

impl Default for FFmpeg {
fn default() -> Self {
Self::new()
}
}

impl FFmpeg {
pub fn new() -> Self {
let ffmpeg_binary_path_str = ffmpeg_path_as_str().unwrap().to_owned();

Self {
command: Command::new(ffmpeg_binary_path_str),
command: Command::new(dbg!(relative_command_path("ffmpeg").unwrap())).into(),
source_index: 0,
}
}
Expand Down Expand Up @@ -309,29 +293,14 @@ impl FFmpeg {
pub fn start(self) -> FFmpegProcess {
FFmpegProcess::spawn(self.command)
}
}

pub fn install_if_necessary() -> Result<(), String> {
if ffmpeg_is_installed() {
return Ok(());
}

match check_latest_version() {
Ok(version) => println!("Latest available version: {}", version),
Err(e) => println!("Skipping version check due to error: {e}"),
}

let download_url = ffmpeg_download_url().map_err(|e| e.to_string())?;
let destination = sidecar_dir().map_err(|e| e.to_string())?;

let archive_path =
download_ffmpeg_package(download_url, &destination).map_err(|e| e.to_string())?;

unpack_ffmpeg(&archive_path, &destination).map_err(|e| e.to_string())?;

let version = ffmpeg_version().map_err(|e| e.to_string())?;

println!("Done! Installed FFmpeg version {} 🏁", version);

Ok(())
fn relative_command_path(command: impl AsRef<Path>) -> Result<PathBuf, tauri_plugin_shell::Error> {
match platform::current_exe()?.parent() {
#[cfg(windows)]
Some(exe_dir) => Ok(exe_dir.join(command.as_ref()).with_extension("exe")),
#[cfg(not(windows))]
Some(exe_dir) => Ok(exe_dir.join(command.as_ref())),
None => Err(tauri_plugin_shell::Error::CurrentExeHasNoParent),
}
}
20 changes: 0 additions & 20 deletions crates/ffmpeg/src/utils.rs

This file was deleted.

Loading

0 comments on commit ce72ee8

Please sign in to comment.