Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom mp4 muxer #208

Merged
merged 6 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/prebuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@ rustflags = [
await exec(`tar xf ${__root}/native-deps.tar.xz -C ${nativeDeps}`);

if (os === "darwin") {
// const frameworkDir = path.join(nativeDeps, "Spacedrive.framework");
// const librariesDir = path.join(frameworkDir, "Libraries");

// const libraries = await fs.readdir(librariesDir);

// const unnecessaryLibraries = libraries.filter(
// (v) =>
// !(
// v.startsWith("libav") ||
// v.startsWith("libsw") ||
// v.startsWith("libpostproc")
// )
// );

// for (const lib of unnecessaryLibraries) {
// await fs.rm(path.join(librariesDir, lib));
// }

// await fs.rm(path.join(frameworkDir, "Resources", "Models"), {
// recursive: true,
// });

await symlinkSharedLibsMacOS(nativeDeps).catch((e) => {
console.error(`Failed to symlink shared libs.`);
throw e;
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

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

16 changes: 13 additions & 3 deletions apps/desktop/src-tauri/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub async fn export_video(
project: ProjectConfiguration,
progress: tauri::ipc::Channel<RenderProgress>,
force: bool,
use_custom_muxer: bool,
) -> Result<PathBuf, String> {
let VideoRecordingMetadata { duration, .. } =
get_video_metadata(app.clone(), video_id.clone(), Some(VideoType::Screen))
Expand All @@ -36,7 +37,7 @@ pub async fn export_video(
.send(RenderProgress::EstimatedTotalFrames { total_frames })
.ok();

cap_export::export_video_to_file(
let exporter = cap_export::Exporter::new(
project,
output_path.clone(),
move |frame_index| {
Expand All @@ -46,12 +47,21 @@ pub async fn export_video(
})
.ok();
},
&editor_instance.project_path,
editor_instance.project_path.clone(),
editor_instance.meta(),
editor_instance.render_constants.clone(),
&editor_instance.segments,
)
.await
.map_err(|e| {
sentry::capture_message(&e.to_string(), sentry::Level::Error);
e.to_string()
})?;

if use_custom_muxer {
exporter.export_with_custom_muxer().await
} else {
exporter.export_with_ffmpeg_cli().await
}
.map_err(|e| {
sentry::capture_message(&e.to_string(), sentry::Level::Error);
e.to_string()
Expand Down
19 changes: 16 additions & 3 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@ use cap_recording::RecordingOptions;
use cap_rendering::ProjectRecordings;
use clipboard_rs::common::RustImage;
use clipboard_rs::{Clipboard, ClipboardContext};
// use display::{list_capture_windows, Bounds, CaptureTarget, FPS};
use cap_export::is_valid_mp4;
use general_settings::GeneralSettingsStore;
use mp4::Mp4Reader;
// use display::{list_capture_windows, Bounds, CaptureTarget, FPS};
use notifications::NotificationType;
use png::{ColorType, Encoder};
use scap::capturer::Capturer;
Expand Down Expand Up @@ -500,7 +499,7 @@ async fn create_screenshot(
decoder.format(),
decoder.width(),
decoder.height(),
ffmpeg::format::Pixel::RGB24,
ffmpeg::format::Pixel::YUV420P,
size.map_or(decoder.width(), |s| s.0),
size.map_or(decoder.height(), |s| s.1),
ffmpeg::software::scaling::flag::Flags::BILINEAR,
Expand Down Expand Up @@ -751,6 +750,20 @@ async fn copy_file_to_path(app: AppHandle, src: String, dst: String) -> Result<(
Err(last_error.unwrap_or_else(|| "Maximum retry attempts exceeded".to_string()))
}

/// Validates if a file at the given path is a valid MP4 file
pub fn is_valid_mp4(path: &std::path::Path) -> bool {
if let Ok(file) = std::fs::File::open(path) {
let file_size = match file.metadata() {
Ok(metadata) => metadata.len(),
Err(_) => return false,
};
let reader = std::io::BufReader::new(file);
Mp4Reader::read_header(reader, file_size).is_ok()
} else {
false
}
}

#[tauri::command]
#[specta::specta]
async fn copy_screenshot_to_clipboard(
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src-tauri/src/recording.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ pub async fn stop_recording(app: AppHandle, state: MutableState<'_, App>) -> Res
config,
tauri::ipc::Channel::new(|_| Ok(())),
true,
true,
)
.await
.ok();
Expand Down
91 changes: 51 additions & 40 deletions apps/desktop/src/routes/editor/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,17 @@ export function Header() {

let percentage: number | undefined;
if (state.type === "saving") {
percentage = state.stage === "rendering" ? Math.min(
((state.renderProgress || 0) /
(state.totalFrames || 1)) *
100,
100
) : Math.min(state.progress || 0, 100);
percentage =
state.stage === "rendering"
? Math.min(
((state.renderProgress || 0) / (state.totalFrames || 1)) * 100,
100
)
: Math.min(state.progress || 0, 100);
}


if (percentage) currentWindow.setProgressBar({ progress: Math.round(percentage) });
if (percentage)
currentWindow.setProgressBar({ progress: Math.round(percentage) });
});

setTitlebar("border", false);
Expand All @@ -80,14 +81,14 @@ export function Header() {
return (
<>
<Titlebar />
<Dialog.Root open={progressState.type !== "idle"} onOpenChange={() => { }}>
<Dialog.Root open={progressState.type !== "idle"} onOpenChange={() => {}}>
<DialogContent
title={
progressState.type === "copying"
? "Link Copied"
: progressState.type === "uploading"
? "Creating Shareable Link"
: "Exporting Recording"
? "Creating Shareable Link"
: "Exporting Recording"
}
confirm={<></>}
class="bg-gray-600 text-gray-500 dark:text-gray-500"
Expand All @@ -112,23 +113,24 @@ export function Header() {
<div
class="bg-blue-300 h-2.5 rounded-full transition-all duration-200"
style={{
width: `${state.stage === "rendering"
width: `${
state.stage === "rendering"
? Math.min(
((state.renderProgress || 0) /
(state.totalFrames || 1)) *
100,
100
)
((state.renderProgress || 0) /
(state.totalFrames || 1)) *
100,
100
)
: Math.min(state.progress || 0, 100)
}%`,
}%`,
}}
/>
</div>

<p class="text-xs mt-3 relative z-10">
{state.stage === "rendering" &&
state.renderProgress &&
state.totalFrames
state.renderProgress &&
state.totalFrames
? `${state.message} (${state.renderProgress}/${state.totalFrames} frames)`
: state.message}
</p>
Expand All @@ -154,23 +156,24 @@ export function Header() {
<div
class="bg-blue-300 h-2.5 rounded-full transition-all duration-200"
style={{
width: `${state.stage === "rendering"
width: `${
state.stage === "rendering"
? Math.min(
((state.renderProgress || 0) /
(state.totalFrames || 1)) *
100,
100
)
((state.renderProgress || 0) /
(state.totalFrames || 1)) *
100,
100
)
: Math.min(state.progress || 0, 100)
}%`,
}%`,
}}
/>
</div>

<p class="text-xs mt-3 relative z-10">
{state.stage === "rendering" &&
state.renderProgress &&
state.totalFrames
state.renderProgress &&
state.totalFrames
? `${state.message} (${state.renderProgress}/${state.totalFrames} frames)`
: state.message}
</p>
Expand All @@ -196,22 +199,23 @@ export function Header() {
<div
class="bg-blue-300 h-2.5 rounded-full transition-all duration-200"
style={{
width: `${state.stage === "rendering"
width: `${
state.stage === "rendering"
? Math.min(state.renderProgress || 0, 100)
: Math.min(
(state.uploadProgress || 0) * 100,
100
)
}%`,
(state.uploadProgress || 0) * 100,
100
)
}%`,
}}
/>
</div>

<p class="text-xs text-white mt-3 relative z-10">
{state.stage === "rendering"
? `Rendering - ${Math.round(
state.renderProgress || 0
)}%`
state.renderProgress || 0
)}%`
: state.message}
</p>
</div>
Expand Down Expand Up @@ -280,7 +284,8 @@ function ExportButton() {
videoId,
project,
progress,
true
true,
false
);
await commands.copyFileToPath(videoPath, path);

Expand Down Expand Up @@ -399,14 +404,20 @@ function ShareButton() {
}
};

await commands.exportVideo(videoId, projectConfig, progress, true);
await commands.exportVideo(
videoId,
projectConfig,
progress,
true,
false
);

// Now proceed with upload
const result = recordingMeta()?.sharing
? await commands.uploadExportedVideo(videoId, "Reupload")
: await commands.uploadExportedVideo(videoId, {
Initial: { pre_created_video: null },
});
Initial: { pre_created_video: null },
});

console.log("Upload result:", result);

Expand Down
5 changes: 4 additions & 1 deletion apps/desktop/src/routes/recordings-overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ function createRecordingMutations(
mediaId,
presets.getDefaultConfig() ?? DEFAULT_PROJECT_CONFIG,
progress,
false,
false
);

Expand Down Expand Up @@ -873,7 +874,8 @@ function createRecordingMutations(
mediaId,
presets.getDefaultConfig() ?? DEFAULT_PROJECT_CONFIG,
progress,
true // Force re-render
true, // Force re-render
false
);

await commands.copyFileToPath(outputPath, savePath);
Expand Down Expand Up @@ -1002,6 +1004,7 @@ function createRecordingMutations(
mediaId,
presets.getDefaultConfig() ?? DEFAULT_PROJECT_CONFIG,
progress,
false,
false
);
console.log("Using existing rendered video");
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/utils/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ async focusCapturesPanel() : Promise<void> {
async getCurrentRecording() : Promise<JsonValue<RecordingInfo | null>> {
return await TAURI_INVOKE("get_current_recording");
},
async exportVideo(videoId: string, project: ProjectConfiguration, progress: TAURI_CHANNEL<RenderProgress>, force: boolean) : Promise<string> {
return await TAURI_INVOKE("export_video", { videoId, project, progress, force });
async exportVideo(videoId: string, project: ProjectConfiguration, progress: TAURI_CHANNEL<RenderProgress>, force: boolean, useCustomMuxer: boolean) : Promise<string> {
return await TAURI_INVOKE("export_video", { videoId, project, progress, force, useCustomMuxer });
},
async copyFileToPath(src: string, dst: string) : Promise<null> {
return await TAURI_INVOKE("copy_file_to_path", { src, dst });
Expand Down
1 change: 1 addition & 0 deletions crates/export/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ tempfile = "3.12.0"
image = "0.25.2"
mp4 = "0.14.0"
thiserror.workspace = true
futures = "0.3.31"
Loading
Loading