Merge pull request #682 from vangel76/fix-342-prores-4444-export

#342: ProRes 4444 and 4444 XQ export, with alpha
This commit is contained in:
Brandon Thomas 2026-10-10 16:14:22 -04:00 committed by GitHub
commit 73723151ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 172 additions and 22 deletions

View File

@ -1264,7 +1264,7 @@ fn build() -> Vec<CommandSpec> {
"Media…",
["File", "Export"],
None,
r#"{"path":str,"preset":str?,"settings":ExportSettings?,"format":"h264|hevc|prores|dnxhr|apv|mjpeg|mxf-op1a|mxf-opatom|png|tiff|bmp|gif|wav|aiff"?,"width":u32?,"height":u32?,"fps":f64?,"bitrateKbps":u32?,"bitrateMode":"cbr|vbr1Pass|vbr2Pass|crf"?,"crf":0..51=23,"hardwareEncoding":"off|auto"?,"scale":f32=1,"audio":bool=true,"quality":0..100,"burnCaptions":bool=false,"alpha":bool=false,"captionSidecar":"srt|vtt"?,"loudnessLufs":f64?,"proresProfile":"proxy|lt|standard|hq"?,"dnxProfile":"lb|sq|hq|hqx"?,"apvProfile":"422-10|422-12|444-10|444-12"?,"mxfVideoCodec":"dnxhr|proRes|h264"?,"sequence":id?,"range":"entire|inOut|workArea|custom"?,"startSeconds":f64?,"endSeconds":f64?,"wait":bool=false}"#,
r#"{"path":str,"preset":str?,"settings":ExportSettings?,"format":"h264|hevc|prores|dnxhr|apv|mjpeg|mxf-op1a|mxf-opatom|png|tiff|bmp|gif|wav|aiff"?,"width":u32?,"height":u32?,"fps":f64?,"bitrateKbps":u32?,"bitrateMode":"cbr|vbr1Pass|vbr2Pass|crf"?,"crf":0..51=23,"hardwareEncoding":"off|auto"?,"scale":f32=1,"audio":bool=true,"quality":0..100,"burnCaptions":bool=false,"alpha":bool=false,"captionSidecar":"srt|vtt"?,"loudnessLufs":f64?,"proresProfile":"proxy|lt|standard|hq|4444|4444xq"?,"dnxProfile":"lb|sq|hq|hqx"?,"apvProfile":"422-10|422-12|444-10|444-12"?,"mxfVideoCodec":"dnxhr|proRes|h264"?,"sequence":id?,"range":"entire|inOut|workArea|custom"?,"startSeconds":f64?,"endSeconds":f64?,"wait":bool=false}"#,
has_seq,
crate::export_tools::export_media
),

View File

@ -217,9 +217,16 @@ fn every_builtin_preset_exports_and_ffprobe_confirms() {
"proxy" => ("Proxy", 45.0),
"lt" => ("LT", 102.0),
"standard" => ("Standard", 147.0),
"4444" => ("4444", 330.0),
"4444xq" => ("XQ", 500.0),
_ => ("HQ", 220.0),
};
assert_eq!(v["profile"], profile, "{name}");
if matches!(profile, "4444" | "XQ") {
let pix = v["pix_fmt"].as_str().unwrap_or_default();
let alpha = settings["alpha"].as_bool().unwrap_or(false);
assert!(pix.contains("444") && pix.starts_with("yuva") == alpha, "{name}: {pix}");
}
assert!(vbr <= mbps * px * 1.5, "{name}: {vbr:.1} Mb/s vs nominal {:.1}", mbps * px);
assert_eq!(a["codec_name"], "pcm_s24le", "{name}");
}
@ -318,7 +325,7 @@ fn user_presets_persist_with_favourites_import_and_export() {
let favs = s.execute("export.presets.list", json!({"favorites": true})).unwrap()["presets"].as_array().unwrap().len();
assert_eq!(favs, 2);
let hits = s.execute("export.presets.list", json!({"query": "prores"})).unwrap()["presets"].as_array().unwrap().len();
assert_eq!(hits, 5, "four QuickTime ProRes presets and MXF OP1a ProRes 422 HQ");
assert_eq!(hits, 8, "seven QuickTime ProRes presets and MXF OP1a ProRes 422 HQ");
// persisted: a fresh session with the same data directory sees both
let mut s2 = demo();
s2.export_presets.set_dir(&dir.0);

View File

@ -239,7 +239,7 @@ pub struct ExportSettings {
/// `progress.total`, `finished` and the final status; this call only adds to `done`.
#[serde(default)]
pub part_of_batch: bool,
/// ProRes flavour: `proxy`, `lt`, `standard` or `hq` (empty = HQ).
/// ProRes flavour: `proxy`, `lt`, `standard`, `hq`, `4444` or `4444xq` (empty = HQ).
#[serde(default)]
pub prores_profile: String,
/// DNxHR profile: `lb`, `sq`, `hq` or `hqx` (empty = HQ).
@ -256,8 +256,9 @@ pub struct ExportSettings {
/// sequence's HDR space and signal it (VUI / `colr` / `mdcv` / `clli` / SEI).
#[serde(default)]
pub sdr: bool,
/// Keep the alpha channel in PNG and TIFF sequences (straight alpha, Premiere's "Include
/// Alpha Channel"). Off, every frame is flattened over black (#160). Other formats ignore it.
/// Keep the alpha channel in PNG and TIFF sequences and ProRes 4444 / 4444 XQ QuickTime movies
/// (straight alpha, Premiere's "Include Alpha Channel"). Off, every frame is flattened over
/// black (#160). Other formats ignore it.
#[serde(default)]
pub alpha: bool,
/// Output frame size (None = Match Source: the sequence size times `scale`).
@ -1006,6 +1007,8 @@ fn aac_factory(_format: Format, sample_rate: u32, channels: u32, s: &ExportSetti
struct ProResEncoder {
enc: filmcraft_prores::Encoder,
profile: filmcraft_prores::Profile,
/// Code the frames' alpha channel (4444 profiles with Include Alpha Channel).
alpha: bool,
w: u32,
h: u32,
rate: FrameRate,
@ -1022,13 +1025,19 @@ impl VideoEncoder for ProResEncoder {
self.rate.num as u32
}
fn encode(&mut self, f: &EncoderFrame) -> Result<Vec<EncodedPacket>> {
let mut fr = filmcraft_prores::Frame::new(f.width, f.height, filmcraft_prores::ChromaFormat::Yuv422, 10, false);
timed(Stage::Convert, || match f.hdr {
Some(rgb) => {
let (kr, kb) = self.signal.kr_kb();
rgbf_to_yuv422_10(rgb, f.width as usize, f.height as usize, kr, kb, &mut fr.y, &mut fr.cb, &mut fr.cr)
let chroma = self.profile.chroma();
let mut fr = filmcraft_prores::Frame::new(f.width, f.height, chroma, 10, self.alpha);
let (w, h) = (f.width as usize, f.height as usize);
timed(Stage::Convert, || match (chroma, f.hdr) {
(filmcraft_prores::ChromaFormat::Yuv444, hdr) => {
let (kr, kb) = if hdr.is_some() { self.signal.kr_kb() } else { (0.2126, 0.0722) };
to_yuv444_10(f.rgba, hdr, w, h, kr, kb, &mut fr.y, &mut fr.cb, &mut fr.cr, fr.alpha.as_deref_mut())
}
None => rgba_to_yuv422_10(f.rgba, f.width as usize, f.height as usize, &mut fr.y, &mut fr.cb, &mut fr.cr),
(_, Some(rgb)) => {
let (kr, kb) = self.signal.kr_kb();
rgbf_to_yuv422_10(rgb, w, h, kr, kb, &mut fr.y, &mut fr.cb, &mut fr.cr)
}
(_, None) => rgba_to_yuv422_10(f.rgba, w, h, &mut fr.y, &mut fr.cb, &mut fr.cr),
});
let data = self.enc.encode(&fr).map_err(|e| ExportError::Encode(e.to_string()))?;
Ok(vec![EncodedPacket { data, key: true, duration: self.rate.den as u32, composition_offset: 0 }])
@ -1038,6 +1047,47 @@ impl VideoEncoder for ProResEncoder {
}
}
/// Limited-range 10-bit 4:4:4 with matrix (Kr, Kb) from straight RGBA8, or from encoded R'G'B'
/// floats (3 per pixel) when `hdr` is given. `alpha` (full range, 0 = transparent) takes the
/// RGBA8 alpha; it stays opaque for float input, which carries none.
#[allow(clippy::too_many_arguments)]
pub fn to_yuv444_10(
rgba: &[u8],
hdr: Option<&[f32]>,
w: usize,
h: usize,
kr: f32,
kb: f32,
y: &mut [u16],
cb: &mut [u16],
cr: &mut [u16],
alpha: Option<&mut [u16]>,
) {
let (w, n) = (w.max(1), w.saturating_mul(h));
let kg = 1.0 - kr - kb;
let (sb, sr) = (2.0 * (1.0 - kb), 2.0 * (1.0 - kr));
let rgb_at = |i: usize| -> [f32; 3] {
match hdr {
Some(f) => f.get(i * 3..i * 3 + 3).map_or([0.0; 3], |p| [p[0], p[1], p[2]]),
None => rgba.get(i * 4..i * 4 + 3).map_or([0.0; 3], |p| [p[0] as f32 / 255.0, p[1] as f32 / 255.0, p[2] as f32 / 255.0]),
}
};
y.par_chunks_mut(w).zip(cb.par_chunks_mut(w).zip(cr.par_chunks_mut(w))).enumerate().for_each(|(row, (yr, (cbr, crr)))| {
for (x, (yv, (cbv, crv))) in yr.iter_mut().zip(cbr.iter_mut().zip(crr.iter_mut())).enumerate() {
let [r, g, b] = rgb_at(row * w + x);
let yy = kr * r + kg * g + kb * b;
*yv = (64.0 + 876.0 * yy).round().clamp(4.0, 1019.0) as u16;
*cbv = (512.0 + 896.0 * (b - yy) / sb).round().clamp(4.0, 1019.0) as u16;
*crv = (512.0 + 896.0 * (r - yy) / sr).round().clamp(4.0, 1019.0) as u16;
}
});
if let (Some(a), None) = (alpha, hdr) {
for (i, dst) in a.iter_mut().enumerate().take(n) {
*dst = rgba.get(i * 4 + 3).map_or(1023, |&v| ((v as u32 * 1023 + 127) / 255) as u16);
}
}
}
/// BT.709 limited-range 10-bit 4:2:2 from straight RGBA8 (chroma averaged horizontally).
pub fn rgba_to_yuv422_10(rgba: &[u8], w: usize, h: usize, y: &mut [u16], cb: &mut [u16], cr: &mut [u16]) {
let cw = w.div_ceil(2);
@ -1071,6 +1121,8 @@ pub fn prores_profile(name: &str) -> filmcraft_prores::Profile {
"proxy" => Profile::Proxy,
"lt" => Profile::Lt,
"standard" | "422" => Profile::Standard,
"4444" => Profile::P4444,
"4444xq" | "4444 xq" | "4444-xq" | "xq" => Profile::P4444Xq,
_ => Profile::Hq,
}
}
@ -1109,7 +1161,10 @@ fn prores_factory(format: Format, w: u32, h: u32, rate: FrameRate, s: &ExportSet
if s.signal.is_hdr() {
cfg.color = filmcraft_prores::ColorInfo { primaries: s.signal.primaries, transfer: s.signal.transfer, matrix: s.signal.matrix };
}
Ok(Box::new(ProResEncoder { enc: filmcraft_prores::Encoder::with_config(cfg), profile, w, h, rate, signal: s.signal }) as Box<dyn VideoEncoder>)
let alpha = s.keeps_alpha();
cfg.encode_alpha = alpha;
let enc = filmcraft_prores::Encoder::with_config(cfg);
Ok(Box::new(ProResEncoder { enc, profile, alpha, w, h, rate, signal: s.signal }) as Box<dyn VideoEncoder>)
})
}

View File

@ -38,7 +38,9 @@ fn picture(settings: &ExportSettings, w: u32, h: u32) -> PictureDesc {
Profile::Proxy => 1,
Profile::Lt => 2,
Profile::Standard => 3,
_ => 4,
Profile::Hq => 4,
Profile::P4444 => 5,
Profile::P4444Xq => 6,
},
}
}
@ -53,9 +55,6 @@ fn picture(settings: &ExportSettings, w: u32, h: u32) -> PictureDesc {
_ => PictureCoding::Vc3 { cid: crate::dnx_profile(&settings.dnx_profile).cid() },
};
let mut d = PictureDesc::new(coding, w, h);
if matches!(coding, PictureCoding::ProRes { .. }) {
d.depth = 10;
}
d.color = match settings.signal {
s if s == ColorSignal::PQ => ColorSpace::Rec2020Pq,
s if s == ColorSignal::HLG => ColorSpace::Rec2020Hlg,

View File

@ -12,7 +12,7 @@ use filmcraft_time::FrameRate;
use rayon::prelude::*;
use crate::settings::{ExportEffects, Placement, Scaling, TextOverlay};
use crate::{ExportError, ExportSettings, Format, FrameRenderer, GpuRendering, Result, build_frame_renderer, note_gpu_fallback, note_gpu_frame};
use crate::{ExportError, ExportSettings, FrameRenderer, GpuRendering, Result, build_frame_renderer, note_gpu_fallback, note_gpu_frame};
/// Where the rendered picture lands in the output frame.
#[derive(Clone, Copy, Debug, PartialEq)]
@ -201,7 +201,7 @@ impl Pipeline {
geom,
opts,
hdr_out,
alpha: settings.alpha && matches!(settings.format, Format::PngSequence | Format::TiffSequence),
alpha: settings.keeps_alpha(),
out_tf,
effects,
overlay,

View File

@ -122,6 +122,14 @@ pub fn builtin_presets() -> Vec<ExportPreset> {
p("Apple ProRes 422", bc, "QuickTime, ProRes 422, 24-bit 48 kHz PCM", mov(Format::ProRes, "standard")),
p("Apple ProRes 422 LT", bc, "QuickTime, ProRes 422 LT, 24-bit 48 kHz PCM", mov(Format::ProRes, "lt")),
p("Apple ProRes 422 Proxy", bc, "QuickTime, ProRes 422 Proxy, 24-bit 48 kHz PCM", mov(Format::ProRes, "proxy")),
p("Apple ProRes 4444", bc, "QuickTime, ProRes 4444 (4:4:4), 24-bit 48 kHz PCM", mov(Format::ProRes, "4444")),
p(
"Apple ProRes 4444 with Alpha",
bc,
"QuickTime, ProRes 4444 (4:4:4) with alpha channel, 24-bit 48 kHz PCM",
ExportSettings { alpha: true, ..mov(Format::ProRes, "4444") },
),
p("Apple ProRes 4444 XQ", bc, "QuickTime, ProRes 4444 XQ (4:4:4), 24-bit 48 kHz PCM", mov(Format::ProRes, "4444xq")),
p("Avid DNxHR HQ", bc, "QuickTime, DNxHR HQ (8-bit 4:2:2), 24-bit 48 kHz PCM", mov(Format::DnxHr, "hq")),
p("Avid DNxHR SQ", bc, "QuickTime, DNxHR SQ (8-bit 4:2:2), 24-bit 48 kHz PCM", mov(Format::DnxHr, "sq")),
p("Avid DNxHR LB", bc, "QuickTime, DNxHR LB (8-bit 4:2:2), 24-bit 48 kHz PCM", mov(Format::DnxHr, "lb")),

View File

@ -378,6 +378,21 @@ impl ExportSettings {
if self.format.is_mxf() { self.mxf_video_codec.encoder_format() } else { self.format }
}
/// Whether the output can carry an alpha channel ([`ExportSettings::alpha`]): PNG and TIFF
/// sequences and ProRes 4444 / 4444 XQ QuickTime movies.
pub fn supports_alpha(&self) -> bool {
match self.format {
Format::PngSequence | Format::TiffSequence => true,
Format::ProRes => crate::prores_profile(&self.prores_profile).chroma() == filmcraft_prores::ChromaFormat::Yuv444,
_ => false,
}
}
/// Whether the exported frames keep their alpha channel instead of being flattened over black.
pub fn keeps_alpha(&self) -> bool {
self.alpha && self.supports_alpha()
}
/// Whether the output is a numbered image sequence.
pub fn is_image_sequence(&self) -> bool {
matches!(self.format, Format::PngSequence | Format::TiffSequence | Format::BmpSequence)
@ -536,7 +551,9 @@ impl ExportSettings {
Profile::Proxy => ", ProRes 422 Proxy",
Profile::Lt => ", ProRes 422 LT",
Profile::Standard => ", ProRes 422",
_ => ", ProRes 422 HQ",
Profile::Hq => ", ProRes 422 HQ",
Profile::P4444 => ", ProRes 4444",
Profile::P4444Xq => ", ProRes 4444 XQ",
}
}
Format::DnxHr => v += &format!(", DNxHR {}", if self.dnx_profile.is_empty() { "HQ".into() } else { self.dnx_profile.to_ascii_uppercase() }),

View File

@ -101,6 +101,51 @@ fn prores_export_roundtrip() {
assert!(f[0] > 240 && f[1] < 15 && f[2] < 15, "{:?}", &f[..4]);
}
/// ProRes 4444 / 4444 XQ export (#342): 4:4:4 frames under the `ap4h` / `ap4x` codes, and with
/// Include Alpha Channel the picture's alpha survives instead of being flattened over black.
#[test]
fn prores_4444_export_roundtrip() {
let (p, seq, m) = project();
for (profile, label) in [("4444", "ProRes 4444"), ("4444xq", "ProRes 4444 XQ")] {
let path = tmp(&format!("pr_{profile}.mov"));
let s = ExportSettings { format: Format::ProRes, path: path.clone(), prores_profile: profile.into(), ..Default::default() };
export(&p, seq, &s, &m, &Progress::default()).unwrap();
let bytes: Arc<[u8]> = std::fs::read(&path).unwrap().into();
let src = filmcraft_codecs::open_bytes("pr.mov", bytes).unwrap();
let codec = src.info().video.as_ref().unwrap().codec.clone();
assert!(codec.contains(label), "{profile}: {codec}");
let f = src.video_frame(FrameRequest::full(Tick(TICKS_PER_SECOND / 3))).unwrap();
assert!(f.format_label().contains("4:4:4"), "{profile}: {}", f.format_label());
let px = f.to_rgba8();
assert!(px[0] > 240 && px[1] < 15 && px[2] < 15 && px[3] == 255, "{profile}: {:?}", &px[..4]);
}
// a half-transparent matte keeps its alpha with Include Alpha Channel, and only then
let mut p = (*p).clone();
let half = GeneratorSource::new(Generator::ColorMatte { color: [1.0, 0.0, 0.0, 0.5] }, 320, 180, FrameRate::FPS_24, Tick(2 * TICKS_PER_SECOND));
let item = p.sequence(seq).unwrap().video_tracks[0].items[0].item;
if let ItemKind::Media(mc) = &mut p.item_mut(item).unwrap().kind {
mc.media = MediaRef::Generator(half.generator.clone());
}
let mut m = m;
m.0.insert(item, Arc::new(half));
let p = Arc::new(p);
for alpha in [true, false] {
let path = tmp(&format!("pr_4444_alpha_{alpha}.mov"));
let s = ExportSettings { format: Format::ProRes, path: path.clone(), prores_profile: "4444".into(), alpha, ..Default::default() };
assert!(s.keeps_alpha() == alpha);
export(&p, seq, &s, &m, &Progress::default()).unwrap();
let bytes: Arc<[u8]> = std::fs::read(&path).unwrap().into();
let src = filmcraft_codecs::open_bytes("pr.mov", bytes).unwrap();
let px = src.video_frame(FrameRequest::full(Tick(TICKS_PER_SECOND / 3))).unwrap().to_rgba8();
if alpha {
assert!((px[3] as i32 - 128).abs() <= 3, "alpha kept: {:?}", &px[..4]);
} else {
assert_eq!(px[3], 255, "flattened: {:?}", &px[..4]);
}
}
}
#[test]
fn dnxhr_export_roundtrip() {
let (p, seq, m) = project();

View File

@ -2985,6 +2985,9 @@
Apple ProRes 422 HQ Apple ProRes 422 HQ
Apple ProRes 422 LT Apple ProRes 422 LT
Apple ProRes 422 Proxy Apple ProRes 422 Proxy
Apple ProRes 4444 Apple ProRes 4444
Apple ProRes 4444 with Alpha Apple ProRes 4444 con alfa
Apple ProRes 4444 XQ Apple ProRes 4444 XQ
Application Aplicación
Apply Guide Template Aplicar plantilla de guías
Auto Save Now Guardar automáticamente ahora
@ -3194,6 +3197,9 @@
QuickTime, ProRes 422 HQ, 24-bit 48 kHz PCM QuickTime, ProRes 422 HQ, PCM de 24 bits y 48 kHz
QuickTime, ProRes 422 LT, 24-bit 48 kHz PCM QuickTime, ProRes 422 LT, PCM de 24 bits y 48 kHz
QuickTime, ProRes 422 Proxy, 24-bit 48 kHz PCM QuickTime, ProRes 422 Proxy, PCM de 24 bits y 48 kHz
QuickTime, ProRes 4444 (4:4:4), 24-bit 48 kHz PCM QuickTime, ProRes 4444 (4:4:4), PCM de 24 bits y 48 kHz
QuickTime, ProRes 4444 (4:4:4) with alpha channel, 24-bit 48 kHz PCM QuickTime, ProRes 4444 (4:4:4) con canal alfa, PCM de 24 bits y 48 kHz
QuickTime, ProRes 4444 XQ (4:4:4), 24-bit 48 kHz PCM QuickTime, ProRes 4444 XQ (4:4:4), PCM de 24 bits y 48 kHz
QuickTime, ProRes 422, 24-bit 48 kHz PCM QuickTime, ProRes 422, PCM de 24 bits y 48 kHz
Quit FilmCraft Salir de FilmCraft
Rate Stretch Tool Herramienta Ajuste de velocidad

Can't render this file because it contains an unexpected character in line 3 and column 95.

View File

@ -2975,6 +2975,9 @@
Apple ProRes 422 HQ Apple ProRes 422 HQ
Apple ProRes 422 LT Apple ProRes 422 LT
Apple ProRes 422 Proxy Apple ProRes 422 Proxy
Apple ProRes 4444 Apple ProRes 4444
Apple ProRes 4444 with Alpha Apple ProRes 4444(アルファ付き)
Apple ProRes 4444 XQ Apple ProRes 4444 XQ
Application アプリケーション
Apply Guide Template ガイドテンプレートを適用
Auto Save Now 今すぐ自動保存
@ -3184,6 +3187,9 @@
QuickTime, ProRes 422 HQ, 24-bit 48 kHz PCM QuickTime、ProRes 422 HQ、24-bit 48 kHz PCM
QuickTime, ProRes 422 LT, 24-bit 48 kHz PCM QuickTime、ProRes 422 LT、24-bit 48 kHz PCM
QuickTime, ProRes 422 Proxy, 24-bit 48 kHz PCM QuickTime、ProRes 422 Proxy、24-bit 48 kHz PCM
QuickTime, ProRes 4444 (4:4:4), 24-bit 48 kHz PCM QuickTime、ProRes 4444 (4:4:4)、24-bit 48 kHz PCM
QuickTime, ProRes 4444 (4:4:4) with alpha channel, 24-bit 48 kHz PCM QuickTime、ProRes 4444 (4:4:4) アルファチャンネル付き、24-bit 48 kHz PCM
QuickTime, ProRes 4444 XQ (4:4:4), 24-bit 48 kHz PCM QuickTime、ProRes 4444 XQ (4:4:4)、24-bit 48 kHz PCM
QuickTime, ProRes 422, 24-bit 48 kHz PCM QuickTime、ProRes 422、24-bit 48 kHz PCM
Quit FilmCraft FilmCraft を終了
Rate Stretch Tool レートストレッチツール

Can't render this file because it contains an unexpected character in line 11 and column 59.

View File

@ -2968,6 +2968,9 @@
Apple ProRes 422 HQ Apple ProRes 422 HQ
Apple ProRes 422 LT Apple ProRes 422 LT
Apple ProRes 422 Proxy Apple ProRes 422 Proxy
Apple ProRes 4444 Apple ProRes 4444
Apple ProRes 4444 with Alpha Apple ProRes 4444(带 Alpha)
Apple ProRes 4444 XQ Apple ProRes 4444 XQ
Application 应用程序
Apply Guide Template 应用参考线模板
Auto Save Now 立即自动保存
@ -3177,6 +3180,9 @@
QuickTime, ProRes 422 HQ, 24-bit 48 kHz PCM QuickTime,ProRes 422 HQ,24 位 48 kHz PCM
QuickTime, ProRes 422 LT, 24-bit 48 kHz PCM QuickTime,ProRes 422 LT,24 位 48 kHz PCM
QuickTime, ProRes 422 Proxy, 24-bit 48 kHz PCM QuickTime,ProRes 422 Proxy,24 位 48 kHz PCM
QuickTime, ProRes 4444 (4:4:4), 24-bit 48 kHz PCM QuickTime,ProRes 4444 (4:4:4),24 位 48 kHz PCM
QuickTime, ProRes 4444 (4:4:4) with alpha channel, 24-bit 48 kHz PCM QuickTime,ProRes 4444 (4:4:4),带 Alpha 通道,24 位 48 kHz PCM
QuickTime, ProRes 4444 XQ (4:4:4), 24-bit 48 kHz PCM QuickTime,ProRes 4444 XQ (4:4:4),24 位 48 kHz PCM
QuickTime, ProRes 422, 24-bit 48 kHz PCM QuickTime,ProRes 422,24 位 48 kHz PCM
Quit FilmCraft 退出 FilmCraft
Rate Stretch Tool 速率伸缩工具

Can't render this file because it contains an unexpected character in line 3 and column 95.

View File

@ -606,7 +606,7 @@ fn video_section(ui: &mut egui::Ui, reg: &mut Reg, s: &mut ExportSettings, t: &T
}
});
}
if matches!(s.format, Format::PngSequence | Format::TiffSequence) {
if s.supports_alpha() {
row(ui, t, tl!("Alpha"), |ui| {
check(ui, reg, "export.video.alpha", &mut s.alpha, tl!("Include Alpha Channel"));
});
@ -708,8 +708,9 @@ fn video_section(ui: &mut egui::Ui, reg: &mut Reg, s: &mut ExportSettings, t: &T
}
Format::ProRes => {
row(ui, t, tl!("Profile"), |ui| {
let keys = ["proxy", "lt", "standard", "hq"];
let labels = ["Apple ProRes 422 Proxy", "Apple ProRes 422 LT", "Apple ProRes 422", "Apple ProRes 422 HQ"];
let keys = ["proxy", "lt", "standard", "hq", "4444", "4444xq"];
let labels =
["Apple ProRes 422 Proxy", "Apple ProRes 422 LT", "Apple ProRes 422", "Apple ProRes 422 HQ", "Apple ProRes 4444", "Apple ProRes 4444 XQ"];
let cur = keys.iter().position(|k| *k == s.prores_profile).unwrap_or(3);
if let Some(i) = combo(ui, reg, "export.video.proresProfile", labels[cur], &opts(&labels), 220.0) {
s.prores_profile = keys[i].into();