HW6: AV1 export format, enabled where a hardware encoder registers one (#30)
Format::Av1 in export settings and jobs, SampleEntry::av1 (av01 + av1C) in isobmff, and the Export panel's Format list, profile and encoder rows. No encoder ships here: like H.265, AV1 is listed greyed out until a platform encoder registers a probe, and a job without one fails with "AV1 encoder not available yet". Split out of #409; the VA-API AV1 encoder follows on main's vaapi::va design. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
98ba9acbce
commit
7f4bf2157b
|
|
@ -72,7 +72,7 @@ use rayon::prelude::*;
|
|||
use crate::audio_out::AudioOut;
|
||||
use crate::mxf_out::{MxfMux, MxfSetup};
|
||||
use crate::pipeline::Pipeline;
|
||||
use crate::settings::{AudioCodec, BitrateMode, Multiplexer};
|
||||
use crate::settings::{AudioCodec, BitrateMode};
|
||||
use crate::{
|
||||
AudioEncoder, ColorSignal, EncodedPacket, EncoderFrame, ExportError, ExportSettings, Format, H264Pass, Out, Progress, Report, Result, Stage, VideoEncoder,
|
||||
audio_factories, export_range, frame_span, note_stage, timed, video_factories,
|
||||
|
|
@ -186,7 +186,10 @@ pub struct Exporter {
|
|||
|
||||
/// Whether [`Exporter`] handles a format.
|
||||
pub fn stepped(format: Format) -> bool {
|
||||
matches!(format, Format::H264 | Format::Hevc | Format::ProRes | Format::DnxHr | Format::Apv | Format::Mjpeg | Format::MxfOp1a | Format::MxfOpAtom)
|
||||
matches!(
|
||||
format,
|
||||
Format::H264 | Format::Hevc | Format::Av1 | Format::ProRes | Format::DnxHr | Format::Apv | Format::Mjpeg | Format::MxfOp1a | Format::MxfOpAtom
|
||||
)
|
||||
}
|
||||
|
||||
/// Whether an export is HDR: an HDR working space, SDR not asked for, and a format whose encoder
|
||||
|
|
@ -249,7 +252,7 @@ impl Exporter {
|
|||
progress.set_status(format!("Exporting {} frames ({})", nframes, settings.format.label()));
|
||||
}
|
||||
let venc = make_venc(&settings, pipe.w, pipe.h, pipe.rate)?;
|
||||
let brand = if settings.format.is_h26x() && settings.multiplexer == Multiplexer::Mp4 { Brand::Mp4 } else { Brand::Mov };
|
||||
let brand = if settings.format.is_mp4_with(settings.multiplexer) { Brand::Mp4 } else { Brand::Mov };
|
||||
let audio = if settings.has_audio() { Some(AudioOut::new(project.clone(), seq, &settings, range)?) } else { None };
|
||||
let aenc: Option<Box<dyn AudioEncoder>> = match (&audio, settings.audio_codec()) {
|
||||
(Some(a), AudioCodec::Aac) => {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ pub enum Format {
|
|||
/// ([`hdr_available`]); elsewhere they are tone-mapped to SDR.
|
||||
#[serde(rename = "hevc", alias = "Hevc")]
|
||||
Hevc,
|
||||
/// MPEG-4, AV1 Main (8-bit) video + AAC audio. Like H.265 it has no built-in encoder: offered
|
||||
/// where a hardware encoder registers one.
|
||||
#[serde(rename = "av1", alias = "Av1")]
|
||||
Av1,
|
||||
/// QuickTime, Apple ProRes 422 (HQ unless the settings pick another flavour) + PCM.
|
||||
#[serde(rename = "prores", alias = "ProRes")]
|
||||
ProRes,
|
||||
|
|
@ -112,6 +116,7 @@ impl Format {
|
|||
Some(match s.to_ascii_lowercase().replace([' ', '-', '_', '.'], "").as_str() {
|
||||
"h264" | "mp4" | "avc" | "m4v" => Format::H264,
|
||||
"hevc" | "h265" | "hvc1" | "hev1" | "x265" => Format::Hevc,
|
||||
"av1" | "av01" => Format::Av1,
|
||||
"prores" | "mov" | "appleprores" => Format::ProRes,
|
||||
"dnxhr" | "dnxhd" | "dnx" | "avid" | "aviddnxhr" | "aviddnxhd" | "vc3" => Format::DnxHr,
|
||||
"apv" | "apv1" => Format::Apv,
|
||||
|
|
@ -132,6 +137,7 @@ impl Format {
|
|||
match self {
|
||||
Format::H264 => "h264",
|
||||
Format::Hevc => "hevc",
|
||||
Format::Av1 => "av1",
|
||||
Format::ProRes => "prores",
|
||||
Format::DnxHr => "dnxhr",
|
||||
Format::Apv => "apv",
|
||||
|
|
@ -148,7 +154,7 @@ impl Format {
|
|||
}
|
||||
pub fn extension(self) -> &'static str {
|
||||
match self {
|
||||
Format::H264 | Format::Hevc => "mp4",
|
||||
Format::H264 | Format::Hevc | Format::Av1 => "mp4",
|
||||
Format::ProRes | Format::DnxHr | Format::Apv | Format::Mjpeg => "mov",
|
||||
Format::PngSequence => "png",
|
||||
Format::TiffSequence => "tif",
|
||||
|
|
@ -163,6 +169,7 @@ impl Format {
|
|||
match self {
|
||||
Format::H264 => "H.264",
|
||||
Format::Hevc => "H.265 (HEVC)",
|
||||
Format::Av1 => "AV1",
|
||||
Format::ProRes => "Apple ProRes",
|
||||
Format::DnxHr => "Avid DNxHR",
|
||||
Format::Apv => "APV",
|
||||
|
|
@ -184,15 +191,24 @@ impl Format {
|
|||
/// Whether the crate carries an encoder for the format; the others need one registered at runtime
|
||||
/// ([`register_format_probe`]).
|
||||
pub fn has_builtin_encoder(self) -> bool {
|
||||
self != Format::Hevc
|
||||
!matches!(self, Format::Hevc | Format::Av1)
|
||||
}
|
||||
/// H.264 or H.265: MPEG-4 (or QuickTime) with AAC audio, set up with the same bitrate controls.
|
||||
pub fn is_h26x(self) -> bool {
|
||||
matches!(self, Format::H264 | Format::Hevc)
|
||||
}
|
||||
pub const ALL: [Format; 14] = [
|
||||
/// A bitrate-controlled MPEG-4 video format with AAC audio: H.264, H.265 or AV1 (MPEG-4 only).
|
||||
pub fn is_mp4_video(self) -> bool {
|
||||
matches!(self, Format::H264 | Format::Hevc | Format::Av1)
|
||||
}
|
||||
/// Whether the file is MPEG-4 rather than QuickTime for `mux`.
|
||||
pub fn is_mp4_with(self, mux: Multiplexer) -> bool {
|
||||
self == Format::Av1 || (self.is_h26x() && mux == Multiplexer::Mp4)
|
||||
}
|
||||
pub const ALL: [Format; 15] = [
|
||||
Format::H264,
|
||||
Format::Hevc,
|
||||
Format::Av1,
|
||||
Format::ProRes,
|
||||
Format::DnxHr,
|
||||
Format::Apv,
|
||||
|
|
@ -524,6 +540,9 @@ impl ExportSettings {
|
|||
if self.format == Format::Hevc && self.bitrate_mode == BitrateMode::Vbr2Pass {
|
||||
return Err(ExportError::Unsupported("H.265 export has no two-pass mode: choose CBR or VBR, 1 pass".into()));
|
||||
}
|
||||
if self.format == Format::Av1 && self.bitrate_mode == BitrateMode::Vbr2Pass {
|
||||
return Err(ExportError::Unsupported("AV1 export has no two-pass mode: choose CBR or VBR, 1 pass".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1641,7 +1660,7 @@ pub fn export(project: &Arc<Project>, seq: ItemId, settings: &ExportSettings, so
|
|||
}
|
||||
(total, count)
|
||||
}
|
||||
Format::H264 | Format::Hevc | Format::ProRes | Format::DnxHr | Format::Apv | Format::Mjpeg | Format::MxfOp1a | Format::MxfOpAtom => {
|
||||
Format::H264 | Format::Hevc | Format::Av1 | Format::ProRes | Format::DnxHr | Format::Apv | Format::Mjpeg | Format::MxfOp1a | Format::MxfOpAtom => {
|
||||
// Handled by the stepped exporter above; reaching here would be a dispatch bug.
|
||||
return Err(ExportError::Unsupported(format!("{:?} must run as a stepped export", settings.format)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -422,8 +422,8 @@ impl ExportSettings {
|
|||
pub fn audio_codec(&self) -> AudioCodec {
|
||||
match (self.format, self.audio.codec) {
|
||||
(Format::Wav | Format::Aiff | Format::MxfOp1a | Format::MxfOpAtom, _) => AudioCodec::Pcm,
|
||||
(f, _) if f.is_h26x() && self.multiplexer == Multiplexer::Mp4 => AudioCodec::Aac,
|
||||
(_, AudioCodec::Auto) if self.format.is_h26x() => AudioCodec::Aac,
|
||||
(f, _) if f.is_mp4_with(self.multiplexer) => AudioCodec::Aac,
|
||||
(_, AudioCodec::Auto) if self.format.is_mp4_video() => AudioCodec::Aac,
|
||||
(_, AudioCodec::Auto) => AudioCodec::Pcm,
|
||||
(_, c) => c,
|
||||
}
|
||||
|
|
@ -451,7 +451,7 @@ impl ExportSettings {
|
|||
let fps = r.rate.num as f64 / r.rate.den as f64;
|
||||
let px = r.width as f64 * r.height as f64;
|
||||
let video_bps = match self.video_format() {
|
||||
Format::H264 | Format::Hevc => r.target_kbps as f64 * 1000.0,
|
||||
Format::H264 | Format::Hevc | Format::Av1 => r.target_kbps as f64 * 1000.0,
|
||||
Format::ProRes => crate::prores_profile(&self.prores_profile).nominal_mbps_1080p30() * 1e6 * px / (1920.0 * 1080.0) * fps / 29.97,
|
||||
Format::DnxHr => {
|
||||
// nominal 1080p29.97 data rates of the DNxHR profiles (Mb/s)
|
||||
|
|
@ -534,6 +534,14 @@ impl ExportSettings {
|
|||
};
|
||||
v += &format!(", keyframe every {} frames", r.keyint);
|
||||
}
|
||||
Format::Av1 => {
|
||||
v += &format!(", AV1 Main (hardware encoder), {}", self.bitrate_mode.label());
|
||||
v += &match self.bitrate_mode {
|
||||
BitrateMode::Cbr => format!(", {}", mbps(r.target_kbps)),
|
||||
_ => format!(", Target {}, Max {}", mbps(r.target_kbps), mbps(r.max_kbps)),
|
||||
};
|
||||
v += &format!(", keyframe every {} frames", r.keyint);
|
||||
}
|
||||
Format::ProRes => {
|
||||
use filmcraft_prores::Profile;
|
||||
v += match crate::prores_profile(&self.prores_profile) {
|
||||
|
|
@ -565,7 +573,7 @@ impl ExportSettings {
|
|||
};
|
||||
let container = match self.format {
|
||||
Format::H264 | Format::Hevc if self.multiplexer == Multiplexer::Mov => "QuickTime",
|
||||
Format::H264 | Format::Hevc => "MP4",
|
||||
Format::H264 | Format::Hevc | Format::Av1 => "MP4",
|
||||
Format::ProRes | Format::DnxHr | Format::Apv | Format::Mjpeg => "QuickTime",
|
||||
Format::MxfOp1a | Format::MxfOpAtom => self.mxf_video_codec.label(),
|
||||
Format::PngSequence | Format::TiffSequence | Format::BmpSequence => "Image sequence",
|
||||
|
|
|
|||
|
|
@ -487,6 +487,37 @@ fn a_time_left_reads_like_a_clock() {
|
|||
assert!(!format_eta(Duration::MAX).is_empty(), "the largest duration cannot overflow");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn av1_is_an_mp4_format_that_needs_a_registered_encoder() {
|
||||
for name in ["av1", "AV1", "av01"] {
|
||||
assert_eq!(Format::from_name(name), Some(Format::Av1), "{name}");
|
||||
}
|
||||
assert_eq!(Format::from_name(Format::Av1.id()), Some(Format::Av1));
|
||||
assert_eq!((Format::Av1.extension(), Format::Av1.label()), ("mp4", "AV1"));
|
||||
assert!(Format::ALL.contains(&Format::Av1));
|
||||
let s: ExportSettings = serde_json::from_value(serde_json::json!({"format": "av1"})).unwrap();
|
||||
assert_eq!(s.format, Format::Av1);
|
||||
assert_eq!(serde_json::to_value(&s).unwrap()["format"], "av1");
|
||||
// MP4 with AAC only: a QuickTime multiplexer setting left over from H.264 does not apply
|
||||
let a = ExportSettings { format: Format::Av1, ..Default::default() };
|
||||
assert_eq!((a.audio_codec(), a.extension()), (AudioCodec::Aac, "mp4"));
|
||||
let mov = ExportSettings { multiplexer: Multiplexer::Mov, ..a.clone() };
|
||||
assert_eq!((mov.audio_codec(), mov.extension()), (AudioCodec::Aac, "mp4"));
|
||||
let b = a.estimate_bytes(1920, 1088, FrameRate::FPS_25, 48_000, Tick(10 * TICKS_PER_SECOND));
|
||||
assert!((b as f64 - 25.4e6).abs() < 0.2e6, "{b}");
|
||||
let summary = a.summary(1920, 1088, FrameRate::FPS_25, 48_000, Tick(10 * TICKS_PER_SECOND));
|
||||
assert_eq!(summary.format, "AV1 (MP4)");
|
||||
assert!(summary.video.contains("AV1 Main"), "{}", summary.video);
|
||||
let e = ExportSettings { bitrate_mode: BitrateMode::Vbr2Pass, ..a.clone() }.validate().unwrap_err();
|
||||
assert!(e.to_string().contains("two-pass"), "{e}");
|
||||
assert!(!Format::Av1.has_builtin_encoder());
|
||||
assert!(!available(Format::Av1), "nothing registered an encoder for it");
|
||||
let (p, seq, m) = matte([0.0, 0.0, 1.0, 1.0], 160, 96, None);
|
||||
let sc = Scratch::new("av1-missing");
|
||||
let err = export(&p, seq, &ExportSettings { path: sc.path("x.mp4"), include_audio: false, ..a.clone() }, &m, &Progress::default()).unwrap_err().to_string();
|
||||
assert!(err.contains("AV1") && err.contains("encoder not available"), "{err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn h265_is_a_format_that_needs_a_registered_encoder() {
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
|
@ -496,7 +527,7 @@ fn h265_is_a_format_that_needs_a_registered_encoder() {
|
|||
}
|
||||
assert_eq!(Format::from_name(Format::Hevc.id()), Some(Format::Hevc));
|
||||
assert_eq!((Format::Hevc.extension(), Format::Hevc.label()), ("mp4", "H.265 (HEVC)"));
|
||||
assert!(Format::ALL.contains(&Format::Hevc) && Format::ALL.len() == 14);
|
||||
assert!(Format::ALL.contains(&Format::Hevc) && Format::ALL.len() == 15);
|
||||
let s: ExportSettings = serde_json::from_value(serde_json::json!({"format": "hevc"})).unwrap();
|
||||
assert_eq!(s.format, Format::Hevc);
|
||||
assert_eq!(serde_json::to_value(&s).unwrap()["format"], "hevc");
|
||||
|
|
|
|||
|
|
@ -1275,6 +1275,10 @@ impl SampleEntry {
|
|||
pub fn hevc(cfg: HevcConfig, width: u16, height: u16) -> Self {
|
||||
Self::video(FourCc(*b"hvc1"), CodecConfig::Hevc(cfg), width, height)
|
||||
}
|
||||
/// AV1 entry (`av01`).
|
||||
pub fn av1(cfg: Av1Config, width: u16, height: u16) -> Self {
|
||||
Self::video(FourCc(*b"av01"), CodecConfig::Av1(cfg), width, height)
|
||||
}
|
||||
/// ProRes entry; `fourcc` one of `apch`/`apcn`/`apcs`/`apco`/`ap4h`/`ap4x`.
|
||||
pub fn prores(fourcc: FourCc, width: u16, height: u16) -> Self {
|
||||
let mut e = Self::video(fourcc, CodecConfig::ProRes { fourcc }, width, height);
|
||||
|
|
|
|||
|
|
@ -3062,6 +3062,7 @@
|
|||
Go to Sequence End Ir al final de la secuencia
|
||||
Go to Sequence Start Ir al inicio de la secuencia
|
||||
H.264 H.264
|
||||
AV1 AV1
|
||||
H.264 1920×1080 for video sharing sites, VBR 1 pass, 16 Mbps H.264 1920×1080 para sitios de video, VBR 1 pasada, 16 Mbps
|
||||
H.264 1920×1080, VBR 1 pass, 20 Mbps target, AAC 320 kbps H.264 1920×1080, VBR 1 pasada, 20 Mbps objetivo, AAC 320 kbps
|
||||
H.264 1920×1080, VBR 2 pass, 18 Mbps H.264 1920×1080, VBR 2 pasadas, 18 Mbps
|
||||
|
|
@ -3415,6 +3416,7 @@
|
|||
Main (8-bit; Main 10 for HDR sequences) Main (8 bits; Main 10 para secuencias HDR)
|
||||
Encoder Codificador
|
||||
Hardware (H.265 has no software encoder) Hardware (H.265 no tiene codificador por software)
|
||||
Hardware (AV1 has no software encoder) Hardware (AV1 no tiene codificador por software)
|
||||
Text Properties Propiedades de texto
|
||||
Text Box Height Altura del cuadro de texto
|
||||
Paragraph Text Texto de párrafo
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 3 and column 95.
|
|
|
@ -3057,6 +3057,7 @@
|
|||
Go to Sequence End シーケンスの終了位置へ移動
|
||||
Go to Sequence Start シーケンスの開始位置へ移動
|
||||
H.264 H.264
|
||||
AV1 AV1
|
||||
H.264 1920×1080 for video sharing sites, VBR 1 pass, 16 Mbps 動画共有サイト向けの H.264 1920×1080、VBR 1 パス、16 Mbps
|
||||
H.264 1920×1080, VBR 1 pass, 20 Mbps target, AAC 320 kbps H.264 1920×1080、VBR 1 パス、ターゲット 20 Mbps、AAC 320 kbps
|
||||
H.264 1920×1080, VBR 2 pass, 18 Mbps H.264 1920×1080、VBR 2 パス、18 Mbps
|
||||
|
|
@ -3410,6 +3411,7 @@
|
|||
Main (8-bit; Main 10 for HDR sequences) メイン (8 ビット、HDR シーケンスではメイン 10)
|
||||
Encoder エンコーダー
|
||||
Hardware (H.265 has no software encoder) ハードウェア (H.265 にはソフトウェアエンコーダーがありません)
|
||||
Hardware (AV1 has no software encoder) ハードウェア (AV1 にはソフトウェアエンコーダーがありません)
|
||||
Text Properties テキストのプロパティ
|
||||
Text Box Height テキストボックスの高さ
|
||||
Paragraph Text 段落テキスト
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 11 and column 59.
|
|
|
@ -3050,6 +3050,7 @@
|
|||
Go to Sequence End 转到序列末尾
|
||||
Go to Sequence Start 转到序列开头
|
||||
H.264 H.264
|
||||
AV1 AV1
|
||||
H.264 1920×1080 for video sharing sites, VBR 1 pass, 16 Mbps H.264 1920×1080(视频分享网站),VBR 1 遍,16 Mbps
|
||||
H.264 1920×1080, VBR 1 pass, 20 Mbps target, AAC 320 kbps H.264 1920×1080,VBR 1 遍,目标 20 Mbps,AAC 320 kbps
|
||||
H.264 1920×1080, VBR 2 pass, 18 Mbps H.264 1920×1080,VBR 2 遍,18 Mbps
|
||||
|
|
@ -3403,6 +3404,7 @@
|
|||
Main (8-bit; Main 10 for HDR sequences) 主要(8 位;HDR 序列使用主要 10)
|
||||
Encoder 编码器
|
||||
Hardware (H.265 has no software encoder) 硬件(H.265 无软件编码器)
|
||||
Hardware (AV1 has no software encoder) 硬件(AV1 无软件编码器)
|
||||
Text Properties 文本属性
|
||||
Text Box Height 文本框高度
|
||||
Paragraph Text 段落文本
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 3 and column 95.
|
|
|
@ -473,7 +473,7 @@ fn settings_column(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
|||
}
|
||||
if (s.has_audio()
|
||||
|| !s.has_video()
|
||||
|| s.format.is_h26x()
|
||||
|| s.format.is_mp4_video()
|
||||
|| matches!(s.format, Format::ProRes | Format::DnxHr | Format::Apv | Format::Mjpeg)
|
||||
|| s.format.is_mxf())
|
||||
&& !s.is_image_sequence()
|
||||
|
|
@ -521,7 +521,7 @@ fn settings_column(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
|||
if section(ui, &mut reg, &mut ex.open_sections, "effects", tl!("Effects"), &t) {
|
||||
pick_overlay = effects_section(ui, &mut reg, s, &t);
|
||||
}
|
||||
if matches!(s.format, Format::H264 | Format::Hevc | Format::ProRes | Format::DnxHr | Format::Apv | Format::Mjpeg)
|
||||
if matches!(s.format, Format::H264 | Format::Hevc | Format::Av1 | Format::ProRes | Format::DnxHr | Format::Apv | Format::Mjpeg)
|
||||
&& section(ui, &mut reg, &mut ex.open_sections, "metadata", tl!("Metadata"), &t)
|
||||
{
|
||||
let m = &mut s.metadata;
|
||||
|
|
@ -612,17 +612,23 @@ fn video_section(ui: &mut egui::Ui, reg: &mut Reg, s: &mut ExportSettings, t: &T
|
|||
});
|
||||
}
|
||||
match s.video_format() {
|
||||
f @ (Format::H264 | Format::Hevc) => {
|
||||
let hevc = f == Format::Hevc;
|
||||
f @ (Format::H264 | Format::Hevc | Format::Av1) => {
|
||||
// H.265 and AV1: hardware encoders only (one profile, level chosen by the encoder)
|
||||
let hevc = f != Format::H264;
|
||||
if hevc {
|
||||
// the hardware encoder writes Main (8-bit 4:2:0), or Main 10 for HDR sequences where it can; level chosen by the encoder
|
||||
let label = match (filmcraft_engine::export::hdr_available(Format::Hevc), seq_hdr && !s.sdr) {
|
||||
(true, true) => tl!("Main 10 (10-bit, HDR)"),
|
||||
(true, false) => tl!("Main (8-bit; Main 10 for HDR sequences)"),
|
||||
(false, _) => tl!("Main (8-bit)"),
|
||||
};
|
||||
row(ui, t, tl!("Profile"), |ui| ui.label(label));
|
||||
row(ui, t, tl!("Encoder"), |ui| ui.label(tl!("Hardware (H.265 has no software encoder)")));
|
||||
if f == Format::Av1 {
|
||||
row(ui, t, tl!("Profile"), |ui| ui.label(tl!("Main (8-bit)")));
|
||||
row(ui, t, tl!("Encoder"), |ui| ui.label(tl!("Hardware (AV1 has no software encoder)")));
|
||||
} else {
|
||||
// the hardware encoder writes Main (8-bit 4:2:0), or Main 10 for HDR sequences where it can; level chosen by the encoder
|
||||
let label = match (filmcraft_engine::export::hdr_available(Format::Hevc), seq_hdr && !s.sdr) {
|
||||
(true, true) => tl!("Main 10 (10-bit, HDR)"),
|
||||
(true, false) => tl!("Main (8-bit; Main 10 for HDR sequences)"),
|
||||
(false, _) => tl!("Main (8-bit)"),
|
||||
};
|
||||
row(ui, t, tl!("Profile"), |ui| ui.label(label));
|
||||
row(ui, t, tl!("Encoder"), |ui| ui.label(tl!("Hardware (H.265 has no software encoder)")));
|
||||
}
|
||||
} else {
|
||||
row(ui, t, tl!("Profile"), |ui| {
|
||||
let o = [H264Profile::Baseline, H264Profile::Main, H264Profile::High];
|
||||
|
|
@ -686,9 +692,9 @@ fn video_section(ui: &mut egui::Ui, reg: &mut Reg, s: &mut ExportSettings, t: &T
|
|||
}
|
||||
});
|
||||
// The system's hardware encoder where there is one (VideoToolbox on macOS, NVENC on NVIDIA
|
||||
// GPUs on Windows); everything it does not take (two-pass, HDR, MXF) and every machine
|
||||
// without one keeps the built-in encoder.
|
||||
// H.265 has only the hardware encoder: choosing the format is the opt-in.
|
||||
// GPUs on Windows, VA-API on Linux); everything it does not take (two-pass, HDR, MXF) and
|
||||
// every machine without one keeps the built-in encoder.
|
||||
// H.265 and AV1 have only hardware encoders: choosing the format is the opt-in.
|
||||
if !hevc {
|
||||
row(ui, t, "Hardware Encoding", |ui| {
|
||||
let mut on = s.hardware_encoding == HardwareEncoding::Auto;
|
||||
|
|
@ -758,13 +764,12 @@ fn audio_section(ui: &mut egui::Ui, reg: &mut Reg, s: &mut ExportSettings, t: &T
|
|||
}
|
||||
}
|
||||
row(ui, t, tl!("Audio Format"), |ui| {
|
||||
let fixed = s.format.is_h26x() && s.multiplexer == Multiplexer::Mp4 || audio_only || s.format.is_mxf();
|
||||
let fixed = s.format.is_mp4_with(s.multiplexer) || audio_only || s.format.is_mxf();
|
||||
let cur = match s.audio_codec() {
|
||||
AudioCodec::Aac => "AAC",
|
||||
_ => tl!("Uncompressed (PCM)"),
|
||||
};
|
||||
let labels =
|
||||
vec![("AAC".to_string(), !audio_only), (tl!("Uncompressed (PCM)").to_string(), !(s.format.is_h26x() && s.multiplexer == Multiplexer::Mp4))];
|
||||
let labels = vec![("AAC".to_string(), !audio_only), (tl!("Uncompressed (PCM)").to_string(), !s.format.is_mp4_with(s.multiplexer))];
|
||||
if fixed {
|
||||
ui.label(cur);
|
||||
} else if let Some(i) = combo(ui, reg, "export.audio.codec", cur, &labels, 180.0) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue