diff --git a/assets/i18n/de.yaml b/assets/i18n/de.yaml index e3e6f0322..ac47d03f9 100644 --- a/assets/i18n/de.yaml +++ b/assets/i18n/de.yaml @@ -379,6 +379,9 @@ "export.audio_codec": "Audio-Codec" "export.color": "Farbraum" "export.color.sdr": "SDR — Rec.709 8-bit" +"export.resolution": "Auflösung (0 = Sequenz)" +"export.framerate": "Bildrate (0 = Sequenz)" +"export.bitrate": "Bitrate (0 = Codec-Standard)" "export.color.hdr": "HDR — BT.2020 10-bit" "export.range": "Exportbereich" "export.range.all": "Ganze Timeline" diff --git a/assets/i18n/en-US.yaml b/assets/i18n/en-US.yaml index 9dedb2681..7405c8fa4 100644 --- a/assets/i18n/en-US.yaml +++ b/assets/i18n/en-US.yaml @@ -377,6 +377,9 @@ "export.audio_codec": "Audio Codec" "export.color": "Color Space" "export.color.sdr": "SDR — Rec.709 8-bit" +"export.resolution": "Resolution (0 = sequence)" +"export.framerate": "Frame Rate (0 = sequence)" +"export.bitrate": "Bit Rate (0 = codec default)" "export.color.hdr": "HDR — BT.2020 10-bit" "export.range": "Export Range" "export.range.all": "Whole Timeline" diff --git a/assets/i18n/es.yaml b/assets/i18n/es.yaml index a8ec40944..80e230f06 100644 --- a/assets/i18n/es.yaml +++ b/assets/i18n/es.yaml @@ -379,6 +379,9 @@ "export.audio_codec": "Códec de audio" "export.color": "Espacio de color" "export.color.sdr": "SDR — Rec.709 8-bit" +"export.resolution": "Resolución (0 = secuencia)" +"export.framerate": "Frecuencia de imagen (0 = secuencia)" +"export.bitrate": "Tasa de bits (0 = valor por defecto)" "export.color.hdr": "HDR — BT.2020 10-bit" "export.range": "Rango de exportación" "export.range.all": "Toda la línea de tiempo" diff --git a/assets/i18n/fr.yaml b/assets/i18n/fr.yaml index bba8ab21d..feabac77c 100644 --- a/assets/i18n/fr.yaml +++ b/assets/i18n/fr.yaml @@ -379,6 +379,9 @@ "export.audio_codec": "Codec audio" "export.color": "Espace colorimétrique" "export.color.sdr": "SDR — Rec.709 8 bits" +"export.resolution": "Résolution (0 = séquence)" +"export.framerate": "Cadence (0 = séquence)" +"export.bitrate": "Débit (0 = défaut codec)" "export.color.hdr": "HDR — BT.2020 10 bits" "export.range": "Plage d’export" "export.range.all": "Toute la timeline" diff --git a/assets/i18n/ja.yaml b/assets/i18n/ja.yaml index cf4c6288c..04c47ce78 100644 --- a/assets/i18n/ja.yaml +++ b/assets/i18n/ja.yaml @@ -379,6 +379,9 @@ "export.audio_codec": "オーディオコーデック" "export.color": "色域" "export.color.sdr": "SDR — Rec.709 8bit" +"export.resolution": "解像度(0 = シーケンスに従う)" +"export.framerate": "フレームレート(0 = シーケンスに従う)" +"export.bitrate": "ビットレート(0 = コーデック既定)" "export.color.hdr": "HDR — BT.2020 10bit" "export.range": "書き出し範囲" "export.range.all": "タイムライン全体" diff --git a/assets/i18n/pt.yaml b/assets/i18n/pt.yaml index 5ae26fc81..d7c210a2b 100644 --- a/assets/i18n/pt.yaml +++ b/assets/i18n/pt.yaml @@ -379,6 +379,9 @@ "export.audio_codec": "Codec de áudio" "export.color": "Espaço de cor" "export.color.sdr": "SDR — Rec.709 8-bit" +"export.resolution": "Resolução (0 = sequência)" +"export.framerate": "Taxa de quadros (0 = sequência)" +"export.bitrate": "Taxa de bits (0 = padrão do codec)" "export.color.hdr": "HDR — BT.2020 10-bit" "export.range": "Faixa de exportação" "export.range.all": "Linha do tempo inteira" diff --git a/assets/i18n/ru.yaml b/assets/i18n/ru.yaml index d3398cb2d..b4439fd6b 100644 --- a/assets/i18n/ru.yaml +++ b/assets/i18n/ru.yaml @@ -379,6 +379,9 @@ "export.audio_codec": "Аудиокодек" "export.color": "Цветовое пространство" "export.color.sdr": "SDR — Rec.709 8 бит" +"export.resolution": "Разрешение (0 = как в последовательности)" +"export.framerate": "Частота кадров (0 = как в последовательности)" +"export.bitrate": "Битрейт (0 = по умолчанию кодекa)" "export.color.hdr": "HDR — BT.2020 10 бит" "export.range": "Диапазон экспорта" "export.range.all": "Вся таймлиния" diff --git a/assets/i18n/zh-CN.yaml b/assets/i18n/zh-CN.yaml index 3bde663c6..9c169b681 100644 --- a/assets/i18n/zh-CN.yaml +++ b/assets/i18n/zh-CN.yaml @@ -377,6 +377,9 @@ "export.audio_codec": "音频编码" "export.color": "色域/色深" "export.color.sdr": "SDR — Rec.709 8 位" +"export.resolution": "分辨率(0 = 跟随序列)" +"export.framerate": "帧率(0 = 跟随序列)" +"export.bitrate": "码率(0 = 编码器默认)" "export.color.hdr": "HDR — BT.2020 10 位" "export.range": "导出范围" "export.range.all": "整条时间轴" diff --git a/crates/oak-app/src/dialogs.rs b/crates/oak-app/src/dialogs.rs index 213497e83..87b7818ed 100644 --- a/crates/oak-app/src/dialogs.rs +++ b/crates/oak-app/src/dialogs.rs @@ -997,6 +997,10 @@ pub struct ExportDialogContent { audio_codec: Entity, color: Entity, range: Entity, + resolution_w: Entity, + resolution_h: Entity, + frame_rate: Entity, + bitrate: Entity, path: Entity, /// (format id, display name, extension) in dropdown order. formats: Vec<(i32, String, String)>, @@ -1101,6 +1105,43 @@ impl ExportDialogContent { }); range.update(cx, |combo, cx| combo.set_selected(Some(0), cx)); + // Resolution / frame rate / bitrate SPIN boxes: 0 = keep the + // sequence's / the codec's default (the engine treats 0 that way), + // any typed value is honored (the backend accepts arbitrary + // width/height/rate/bitrate). + let resolution_w = cx.new(|cx| { + SpinBox::new( + 9, + SliderModel::new(ValueKind::Integer, 0.0, 16384.0, 4.0, 0.0), + window, + cx, + ) + }); + let resolution_h = cx.new(|cx| { + SpinBox::new( + 12, + SliderModel::new(ValueKind::Integer, 0.0, 16384.0, 4.0, 0.0), + window, + cx, + ) + }); + let frame_rate = cx.new(|cx| { + SpinBox::new( + 10, + SliderModel::new(ValueKind::Float, 0.0, 240.0, 0.1, 0.0), + window, + cx, + ) + }); + let bitrate = cx.new(|cx| { + SpinBox::new( + 11, + SliderModel::new(ValueKind::Integer, 0.0, 500_000_000.0, 1_000_000.0, 0.0), + window, + cx, + ) + }); + let path = cx.new(|cx| { let editor = cx.new(|cx| EditableTextState::new(StringStorage::default(), cx)); PathField { @@ -1115,6 +1156,10 @@ impl ExportDialogContent { audio_codec, color, range, + resolution_w, + resolution_h, + frame_rate, + bitrate, path, formats, active_format, @@ -1197,9 +1242,10 @@ impl ExportDialogContent { cx.notify(); } - /// The full export settings (codec/color/bit-depth/range) the engine - /// consumes. The codec combos expose only the container's compatible - /// codes, so the picked ids are always valid for `format`. + /// The full export settings (codec/color/bit-depth/range/size/fps/ + /// bitrate) the engine consumes. The codec combos expose only the + /// container's compatible codes, so the picked ids are always valid + /// for `format`. pub fn settings(&self, cx: &App) -> crate::oakui::engine::ExportSettings { let video_codec = compatible_video_codecs(self.active_format) .get(self.video_codec.read(cx).selected().unwrap_or(0)) @@ -1211,13 +1257,18 @@ impl ExportDialogContent { .unwrap_or(12); let hdr = self.color.read(cx).selected() == Some(1); let in_out = self.range.read(cx).selected() == Some(1); + let w = self.resolution_w.read(cx).value().to_f64().round() as i32; + let h = self.resolution_h.read(cx).value().to_f64().round() as i32; + let size = if w > 0 && h > 0 { (w, h) } else { (0, 0) }; + let frame_rate = self.frame_rate.read(cx).value().to_f64(); + let video_bitrate = self.bitrate.read(cx).value().to_f64().round() as i64; crate::oakui::engine::ExportSettings { format: self.active_format, video_codec, audio_codec, - size: (0, 0), - frame_rate: 0.0, - video_bitrate: 0, + size, + frame_rate, + video_bitrate, audio_bitrate: 0, bit_depth: if hdr { 10 } else { 8 }, color_primaries: if hdr { 9 } else { 1 }, @@ -1284,6 +1335,29 @@ impl Render for ExportDialogContent { i18n::tr("export.range").into(), self.range.clone(), )) + .child(form_row( + &colors, + i18n::tr("export.resolution").into(), + div() + .flex() + .gap_2() + .child(self.resolution_w.clone()) + .child(div() + .text_color(colors.disabled) + .text_xs() + .child("×")) + .child(self.resolution_h.clone()), + )) + .child(form_row( + &colors, + i18n::tr("export.framerate").into(), + self.frame_rate.clone(), + )) + .child(form_row( + &colors, + i18n::tr("export.bitrate").into(), + self.bitrate.clone(), + )) .child(form_row( &colors, i18n::tr("export.path").into(),