mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-14 14:22:14 +00:00
Export, Media: codecs and containers from craft-libs; remove hevcenc, av1enc, vp9enc (#3522)
* Export, Media: H.264, ProRes, HEVC, VP9, AV1, AAC, MP4 and Matroska come from craft-libs crates/export and crates/media use craft-video, craft-audio and craft-media (one craft-libs rev) instead of filmcraft-h264enc, -prores, -aac, -isobmff, -matroska and -opus. The av1enc, hevcenc and vp9enc crates are removed: craft-video carries the same encoders, with the later changes merged in storytold/craft-libs#829. Their round-trip tests move to crates/export/tests and run against craft-video. opusenc stays (craft-libs has no Opus encoder yet), as do filmcraft-mpegts and the filmcraft-vp9, -hevc and -av1 decoders the tests use as oracles. * Pin craft-libs at the main commit that has the encoder colour description (e7c3dda7)
This commit is contained in:
1 parent
f334bcf3ef
commit
79c42749a8
75 files changed
+228
-16997
No files matched your search
+26
-26
@@ -1,13 +1,13 @@
|
||||
//! Movie export: FilmCraft H.264, HEVC (`effectcraft-hevcenc`) and AV1 (`effectcraft-av1enc`)
|
||||
//! Movie export: H.264 (`craft_video::h264`), HEVC (`craft_video::hevc`) and AV1 (`craft_video::av1`)
|
||||
//! → MP4 (+ AAC), ProRes and uncompressed `raw ` → MOV (+ PCM), muxed by
|
||||
//! FilmCraft's ISO BMFF / QuickTime writer.
|
||||
//! craft-media's ISO BMFF / QuickTime writer.
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
use craft_media::isobmff::{Brand, ColorInfo, FourCc, Mp4Writer, PcmConfig, SampleEntry, TrackConfig, WriteSample, WriterOptions};
|
||||
use effectcraft_project::render_queue::{AudioFormat, Channels, OutputFormat, ProResProfile};
|
||||
use effectcraft_project::{Comp, ProjectSettings};
|
||||
use effectcraft_time::{FrameRate, Tick};
|
||||
use filmcraft_isobmff::{Brand, ColorInfo, FourCc, Mp4Writer, PcmConfig, SampleEntry, TrackConfig, WriteSample, WriterOptions};
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::{Cx, ExportError, Report, Result, State, batch_size, wants_audio};
|
||||
@@ -117,7 +117,7 @@ pub(crate) trait VideoEncoder: Send {
|
||||
// ---------------------------------------------------------------- H.264
|
||||
|
||||
struct H264 {
|
||||
enc: filmcraft_h264enc::Encoder,
|
||||
enc: craft_video::h264::Encoder,
|
||||
w: u32,
|
||||
h: u32,
|
||||
rate: FrameRate,
|
||||
@@ -129,26 +129,26 @@ struct H264 {
|
||||
|
||||
impl H264 {
|
||||
fn new(w: u32, h: u32, rate: FrameRate, kbps: u32, codes: Codes) -> Result<H264> {
|
||||
let mut cfg = filmcraft_h264enc::EncoderConfig::new(w, h, rate.num as u32, rate.den as u32);
|
||||
cfg.format = filmcraft_h264enc::PacketFormat::LengthPrefixed;
|
||||
let mut cfg = craft_video::h264::EncoderConfig::new(w, h, rate.num as u32, rate.den as u32);
|
||||
cfg.format = craft_video::h264::PacketFormat::LengthPrefixed;
|
||||
cfg.aud = false;
|
||||
cfg.keyint = (rate.as_f64() * 2.0).round().max(1.0) as u32;
|
||||
let kbps = kbps.max(100);
|
||||
cfg.rate = filmcraft_h264enc::RateControl::Vbr { target_kbps: kbps, max_kbps: kbps * 3 / 2 };
|
||||
cfg.color = filmcraft_h264enc::ColorConfig { primaries: codes.primaries, transfer: codes.transfer, matrix: codes.matrix, full_range: false };
|
||||
let enc = filmcraft_h264enc::Encoder::new(cfg).map_err(enc)?;
|
||||
cfg.rate = craft_video::h264::RateControl::Vbr { target_kbps: kbps, max_kbps: kbps * 3 / 2 };
|
||||
cfg.color = craft_video::h264::ColorConfig { primaries: codes.primaries, transfer: codes.transfer, matrix: codes.matrix, full_range: false };
|
||||
let enc = craft_video::h264::Encoder::new(cfg).map_err(enc)?;
|
||||
Ok(H264 { enc, w, h, rate, codes, y: vec![], u: vec![], v: vec![] })
|
||||
}
|
||||
fn packets(&self, ps: Vec<filmcraft_h264enc::Packet>) -> Vec<Packet> {
|
||||
fn packets(&self, ps: Vec<craft_video::h264::Packet>) -> Vec<Packet> {
|
||||
ps.into_iter().map(|p| Packet { data: p.data, key: p.keyframe, cto: (p.pts - p.dts) as i32 }).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoEncoder for H264 {
|
||||
fn sample_entry(&self) -> SampleEntry {
|
||||
let cfg = filmcraft_isobmff::AvcConfig::parse(&self.enc.avcc()).unwrap_or_else(|_| {
|
||||
let cfg = craft_media::isobmff::AvcConfig::parse(&self.enc.avcc()).unwrap_or_else(|_| {
|
||||
let (sps, pps) = self.enc.sps_pps();
|
||||
filmcraft_isobmff::AvcConfig::new(vec![sps], vec![pps], 4)
|
||||
craft_media::isobmff::AvcConfig::new(vec![sps], vec![pps], 4)
|
||||
});
|
||||
let mut entry = SampleEntry::avc(cfg, self.w as u16, self.h as u16);
|
||||
tag_nclx_limited(&mut entry, self.codes);
|
||||
@@ -170,7 +170,7 @@ impl VideoEncoder for H264 {
|
||||
fn encode_yuv420(&mut self, p: &Yuv420, index: u64) -> Result<Vec<Packet>> {
|
||||
let (w, cw) = (self.w as usize, (self.w as usize).div_ceil(2));
|
||||
let (y, u, v) = p.slices(w, self.h as usize);
|
||||
let frame = filmcraft_h264enc::YuvFrame { y, u, v, y_stride: w, uv_stride: cw };
|
||||
let frame = craft_video::h264::YuvFrame { y, u, v, y_stride: w, uv_stride: cw };
|
||||
let ps = self.enc.try_encode(&frame, index as i64 * self.rate.den).map_err(enc)?;
|
||||
Ok(self.packets(ps))
|
||||
}
|
||||
@@ -188,8 +188,8 @@ pub(crate) use effectcraft_raster::yuv::rgba_to_yuv420;
|
||||
// ---------------------------------------------------------------- ProRes
|
||||
|
||||
struct ProRes {
|
||||
enc: filmcraft_prores::Encoder,
|
||||
profile: filmcraft_prores::Profile,
|
||||
enc: craft_video::prores::Encoder,
|
||||
profile: craft_video::prores::Profile,
|
||||
w: u32,
|
||||
h: u32,
|
||||
alpha: bool,
|
||||
@@ -202,8 +202,8 @@ fn prores_colr(c: Codes) -> ColorInfo {
|
||||
ColorInfo::Nclc { primaries: c.primaries.into(), transfer: c.transfer.into(), matrix: c.matrix.into() }
|
||||
}
|
||||
|
||||
fn prores_profile(p: ProResProfile) -> filmcraft_prores::Profile {
|
||||
use filmcraft_prores::Profile;
|
||||
fn prores_profile(p: ProResProfile) -> craft_video::prores::Profile {
|
||||
use craft_video::prores::Profile;
|
||||
match p {
|
||||
ProResProfile::Proxy => Profile::Proxy,
|
||||
ProResProfile::Lt => Profile::Lt,
|
||||
@@ -220,18 +220,18 @@ impl ProRes {
|
||||
let profile = if channels == Channels::Rgba && !profile.is_4444() { ProResProfile::P4444 } else { profile };
|
||||
let profile = prores_profile(profile);
|
||||
let alpha = channels == Channels::Rgba;
|
||||
let mut cfg = filmcraft_prores::EncoderConfig::new(profile, w, h);
|
||||
let mut cfg = craft_video::prores::EncoderConfig::new(profile, w, h);
|
||||
cfg.encode_alpha = alpha;
|
||||
// The frame header carries the same H.273 codes as the `colr` atom, so readers that trust
|
||||
// either one agree.
|
||||
if let ColorInfo::Nclc { primaries, transfer, matrix } = colr {
|
||||
let code = |c| u8::try_from(c).unwrap_or(2);
|
||||
cfg.color = filmcraft_prores::ColorInfo { primaries: code(primaries), transfer: code(transfer), matrix: code(matrix) };
|
||||
cfg.color = craft_video::prores::ColorInfo { primaries: code(primaries), transfer: code(transfer), matrix: code(matrix) };
|
||||
}
|
||||
ProRes { enc: filmcraft_prores::Encoder::with_config(cfg), profile, w, h, alpha, colr }
|
||||
ProRes { enc: craft_video::prores::Encoder::with_config(cfg), profile, w, h, alpha, colr }
|
||||
}
|
||||
fn is_444(&self) -> bool {
|
||||
matches!(self.profile, filmcraft_prores::Profile::P4444 | filmcraft_prores::Profile::P4444Xq)
|
||||
matches!(self.profile, craft_video::prores::Profile::P4444 | craft_video::prores::Profile::P4444Xq)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,8 +244,8 @@ impl VideoEncoder for ProRes {
|
||||
entry
|
||||
}
|
||||
fn encode(&mut self, rgba: &[u8], _index: u64) -> Result<Vec<Packet>> {
|
||||
let chroma = if self.is_444() { filmcraft_prores::ChromaFormat::Yuv444 } else { filmcraft_prores::ChromaFormat::Yuv422 };
|
||||
let mut fr = filmcraft_prores::Frame::new(self.w, self.h, chroma, 10, self.alpha);
|
||||
let chroma = if self.is_444() { craft_video::prores::ChromaFormat::Yuv444 } else { craft_video::prores::ChromaFormat::Yuv422 };
|
||||
let mut fr = craft_video::prores::Frame::new(self.w, self.h, chroma, 10, self.alpha).map_err(enc)?;
|
||||
rgba_to_yuv_10(rgba, self.w as usize, self.h as usize, self.is_444(), &mut fr.y, &mut fr.cb, &mut fr.cr);
|
||||
if let Some(a) = fr.alpha.as_mut() {
|
||||
for (d, s) in a.iter_mut().zip(rgba.as_chunks::<4>().0.iter()) {
|
||||
@@ -291,7 +291,7 @@ fn rgba_to_yuv_10(rgba: &[u8], w: usize, h: usize, full_chroma: bool, y: &mut [u
|
||||
// ---------------------------------------------------------------- audio
|
||||
|
||||
enum Audio {
|
||||
Aac(Box<filmcraft_aac::Encoder>),
|
||||
Aac(Box<craft_audio::aac::Encoder>),
|
||||
Pcm,
|
||||
}
|
||||
|
||||
@@ -401,7 +401,7 @@ pub(crate) async fn movie(job: &Cx<'_>, comp: &Comp, w: u32, h: u32, st: &mut St
|
||||
// from the media duration and drop the last frame.
|
||||
let frames_ts = st.total as i128 * rate.den as i128;
|
||||
let seg = (frames_ts * movie_ts + rate.num as i128 / 2) / rate.num as i128;
|
||||
vcfg.edits = vec![filmcraft_isobmff::Edit { segment_duration: seg as u64, media_time: start, media_rate: 0x10000 }];
|
||||
vcfg.edits = vec![craft_media::isobmff::Edit { segment_duration: seg as u64, media_time: start, media_rate: 0x10000 }];
|
||||
}
|
||||
let vt = mux.add_track(vcfg).map_err(mux_err)?;
|
||||
let sr = job.output.audio_sample_rate.clamp(8_000, 192_000);
|
||||
@@ -411,7 +411,7 @@ pub(crate) async fn movie(job: &Cx<'_>, comp: &Comp, w: u32, h: u32, st: &mut St
|
||||
let mut audio = None;
|
||||
if with_audio {
|
||||
let (a, entry, start) = if brand == Brand::Mp4 {
|
||||
let e = filmcraft_aac::Encoder::new(filmcraft_aac::EncoderConfig::cbr(sr, chans as usize, 160_000 * chans)).map_err(enc)?;
|
||||
let e = craft_audio::aac::Encoder::new(craft_audio::aac::EncoderConfig::cbr(sr, chans as usize, 160_000 * chans)).map_err(enc)?;
|
||||
let entry = SampleEntry::aac(e.audio_specific_config(), chans, sr);
|
||||
let priming = e.priming_samples() as i64;
|
||||
(Audio::Aac(Box::new(e)), entry, Some(priming))
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! HEVC (`effectcraft-hevcenc`) and AV1 (`effectcraft-av1enc`) video encoders for the MP4
|
||||
//! HEVC (`craft_video::hevc`) and AV1 (`craft_video::av1`) video encoders for the MP4
|
||||
//! movie writer (`hvc1` / `av01` sample entries) and AV1 in WebM, configured from the Output
|
||||
//! Module's [`VideoCodecOptions`].
|
||||
|
||||
use craft_media::isobmff::{Av1Config, CodecConfig, FourCc, HevcConfig, SampleEntry};
|
||||
use effectcraft_project::render_queue::{CodecProfile, OutputModule, RateControlMode, VideoCodecOptions};
|
||||
use effectcraft_time::FrameRate;
|
||||
use filmcraft_isobmff::{Av1Config, CodecConfig, FourCc, HevcConfig, SampleEntry};
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::encode::{Codes, Packet, VideoEncoder};
|
||||
@@ -55,7 +55,7 @@ struct Planes {
|
||||
// ---------------------------------------------------------------- HEVC
|
||||
|
||||
pub(crate) struct Hevc {
|
||||
enc: effectcraft_hevcenc::Encoder,
|
||||
enc: craft_video::hevc::Encoder,
|
||||
w: u32,
|
||||
h: u32,
|
||||
bits: u8,
|
||||
@@ -64,8 +64,8 @@ pub(crate) struct Hevc {
|
||||
}
|
||||
|
||||
/// The HEVC encoder settings for an output module.
|
||||
pub(crate) fn hevc_config(w: u32, h: u32, rate: FrameRate, om: &OutputModule) -> effectcraft_hevcenc::EncoderConfig {
|
||||
use effectcraft_hevcenc as he;
|
||||
pub(crate) fn hevc_config(w: u32, h: u32, rate: FrameRate, om: &OutputModule) -> craft_video::hevc::EncoderConfig {
|
||||
use craft_video::hevc as he;
|
||||
let o: &VideoCodecOptions = &om.codec;
|
||||
let mut cfg = he::EncoderConfig::new(w, h, rate.num as u32, rate.den as u32);
|
||||
cfg.profile = match o.profile {
|
||||
@@ -86,7 +86,7 @@ impl Hevc {
|
||||
let mut cfg = hevc_config(w, h, rate, om);
|
||||
(cfg.colour_primaries, cfg.transfer_characteristics, cfg.matrix_coeffs) = (codes.primaries, codes.transfer, codes.matrix);
|
||||
let bits = cfg.profile.bit_depth();
|
||||
let enc = effectcraft_hevcenc::Encoder::new(cfg).map_err(enc_err)?;
|
||||
let enc = craft_video::hevc::Encoder::new(cfg).map_err(enc_err)?;
|
||||
Ok(Hevc { enc, w, h, bits, codes, planes: Planes::default() })
|
||||
}
|
||||
}
|
||||
@@ -102,8 +102,8 @@ impl VideoEncoder for Hevc {
|
||||
let (w, h) = (self.w as usize, self.h as usize);
|
||||
let p = &mut self.planes;
|
||||
rgba_to_yuv420_bits(rgba, w, h, self.bits, &mut p.y, &mut p.u, &mut p.v);
|
||||
let frame = effectcraft_hevcenc::Frame { y: &p.y, u: &p.u, v: &p.v, y_stride: w, uv_stride: w.div_ceil(2) };
|
||||
let pk = self.enc.encode(&frame);
|
||||
let frame = craft_video::hevc::Frame { y: &p.y, u: &p.u, v: &p.v, y_stride: w, uv_stride: w.div_ceil(2) };
|
||||
let pk = self.enc.encode(&frame).map_err(enc_err)?;
|
||||
Ok(vec![Packet { data: pk.data, key: pk.keyframe, cto: 0 }])
|
||||
}
|
||||
fn flush(&mut self) -> Result<Vec<Packet>> {
|
||||
@@ -114,7 +114,7 @@ impl VideoEncoder for Hevc {
|
||||
// ---------------------------------------------------------------- AV1
|
||||
|
||||
pub(crate) struct Av1 {
|
||||
enc: effectcraft_av1enc::Encoder,
|
||||
enc: craft_video::av1::Encoder,
|
||||
w: u32,
|
||||
h: u32,
|
||||
bits: u8,
|
||||
@@ -123,8 +123,8 @@ pub(crate) struct Av1 {
|
||||
}
|
||||
|
||||
/// The AV1 encoder settings for an output module.
|
||||
pub(crate) fn av1_config(w: u32, h: u32, rate: FrameRate, om: &OutputModule) -> effectcraft_av1enc::EncoderConfig {
|
||||
use effectcraft_av1enc as ae;
|
||||
pub(crate) fn av1_config(w: u32, h: u32, rate: FrameRate, om: &OutputModule) -> craft_video::av1::EncoderConfig {
|
||||
use craft_video::av1 as ae;
|
||||
let o: &VideoCodecOptions = &om.codec;
|
||||
let mut cfg = ae::EncoderConfig::new(w, h, rate.num as u32, rate.den as u32);
|
||||
cfg.bit_depth = o.profile.bit_depth();
|
||||
@@ -142,7 +142,7 @@ impl Av1 {
|
||||
let mut cfg = av1_config(w, h, rate, om);
|
||||
(cfg.color_primaries, cfg.transfer_characteristics, cfg.matrix_coefficients) = (codes.primaries, codes.transfer, codes.matrix);
|
||||
let bits = cfg.bit_depth;
|
||||
let enc = effectcraft_av1enc::Encoder::new(cfg).map_err(enc_err)?;
|
||||
let enc = craft_video::av1::Encoder::new(cfg).map_err(enc_err)?;
|
||||
Ok(Av1 { enc, w, h, bits, codes, planes: Planes::default() })
|
||||
}
|
||||
/// The `av1C` record (also the WebM CodecPrivate).
|
||||
@@ -162,8 +162,8 @@ impl VideoEncoder for Av1 {
|
||||
let (w, h) = (self.w as usize, self.h as usize);
|
||||
let p = &mut self.planes;
|
||||
rgba_to_yuv420_bits(rgba, w, h, self.bits, &mut p.y, &mut p.u, &mut p.v);
|
||||
let frame = effectcraft_av1enc::Frame { y: &p.y, u: &p.u, v: &p.v, y_stride: w, uv_stride: w.div_ceil(2) };
|
||||
let pk = self.enc.encode(&frame);
|
||||
let frame = craft_video::av1::Frame { y: &p.y, u: &p.u, v: &p.v, y_stride: w, uv_stride: w.div_ceil(2) };
|
||||
let pk = self.enc.encode(&frame).map_err(enc_err)?;
|
||||
Ok(vec![Packet { data: pk.data, key: pk.keyframe, cto: 0 }])
|
||||
}
|
||||
fn flush(&mut self) -> Result<Vec<Packet>> {
|
||||
@@ -196,9 +196,9 @@ mod tests {
|
||||
om.keyframe_interval = 12;
|
||||
let rate = FrameRate::new(30, 1);
|
||||
let h = hevc_config(64, 32, rate, &om);
|
||||
assert_eq!(h.profile, effectcraft_hevcenc::Profile::Main10);
|
||||
assert_eq!(h.profile, craft_video::hevc::Profile::Main10);
|
||||
assert_eq!(h.level_idc, Some(123));
|
||||
assert_eq!(h.rate, effectcraft_hevcenc::RateControl::ConstantQp(4));
|
||||
assert_eq!(h.rate, craft_video::hevc::RateControl::ConstantQp(4));
|
||||
assert_eq!(h.keyint, 12);
|
||||
let a = av1_config(64, 32, rate, &om);
|
||||
assert_eq!((a.bit_depth, a.level_idx, a.keyint), (10, Some(9), 12));
|
||||
@@ -206,6 +206,6 @@ mod tests {
|
||||
om.keyframe_interval = 0;
|
||||
om.bitrate_kbps = 3000;
|
||||
let a = av1_config(64, 32, rate, &om);
|
||||
assert_eq!((a.rate, a.keyint), (effectcraft_av1enc::RateControl::Bitrate { kbps: 3000 }, 60));
|
||||
assert_eq!((a.rate, a.keyint), (craft_video::av1::RateControl::Bitrate { kbps: 3000 }, 60));
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,16 @@
|
||||
//!
|
||||
//! | Format | Container / files | Video | Alpha | Audio |
|
||||
//! |---|---|---|---|---|
|
||||
//! | H.264 | MP4 | FilmCraft H.264 High, 8-bit 4:2:0 BT.709 limited, VBR | no | AAC-LC (FilmCraft), mono/stereo |
|
||||
//! | H.264 | MP4 | `craft_video::h264` High, 8-bit 4:2:0 BT.709 limited, VBR | no | AAC-LC (`craft_audio::aac`), mono/stereo |
|
||||
//! | ProRes | QuickTime MOV | FilmCraft ProRes 422 Proxy/LT/422/HQ, 10-bit; 4444/4444 XQ 4:4:4 | 4444 (16-bit alpha coding) | PCM 16/24-bit or 32-bit float, mono/stereo |
|
||||
//! | Uncompressed | QuickTime MOV (`raw `) | 8-bit RGB (depth 24, rows padded to even bytes) or ARGB (depth 32) | RGB + Alpha (straight or premultiplied) | PCM 16/24-bit or 32-bit float, mono/stereo |
|
||||
//! | PNG / JPEG / TIFF sequence | one file per frame | 8-bit | PNG, TIFF | — |
|
||||
//! | OpenEXR sequence | one file per frame, scan lines | 32-bit float (default) or half float, linear light, premultiplied; compression None, RLE, ZIPS, ZIP (default), PIZ, PXR24, B44, B44A, DWAA, DWAB | yes | — |
|
||||
//! | Targa sequence | one file per frame | uncompressed 8-bit: 24-bit RGB, 32-bit RGB + Alpha (8 alpha bits), 8-bit grey for Alpha; top-left origin | yes (straight or premultiplied) | — |
|
||||
//! | Animated GIF | GIF89a | 256-colour palette per frame (NeuQuant) | 1-bit | — |
|
||||
//! | WebM | WebM (Matroska) | VP9 profile 0 (`effectcraft-vp9enc`: key + inter frames, a key frame every 2 s, loop filter), 8-bit 4:2:0; or AV1 (`effectcraft-av1enc`) | VP9 alpha (BlockAdditional) | Opus (`effectcraft-opusenc`: SILK / hybrid / CELT by bitrate and application), 48 kHz |
|
||||
//! | HEVC | MP4 (`hvc1`) | `effectcraft-hevcenc` Main / Main 10 4:2:0, I + P slices, bitrate or constant quality, level, key-frame interval | no | AAC-LC (FilmCraft) |
|
||||
//! | AV1 | MP4 (`av01`) | `effectcraft-av1enc` Main 8/10-bit 4:2:0, key + inter frames | no | AAC-LC (FilmCraft) |
|
||||
//! | WebM | WebM (Matroska) | VP9 profile 0 (`craft_video::vp9`: key + inter frames, a key frame every 2 s, loop filter), 8-bit 4:2:0; or AV1 (`craft_video::av1`) | VP9 alpha (BlockAdditional) | Opus (`effectcraft-opusenc`: SILK / hybrid / CELT by bitrate and application), 48 kHz |
|
||||
//! | HEVC | MP4 (`hvc1`) | `craft_video::hevc` Main / Main 10 4:2:0, I + P slices, bitrate or constant quality, level, key-frame interval | no | AAC-LC (`craft_audio::aac`) |
|
||||
//! | AV1 | MP4 (`av01`) | `craft_video::av1` Main 8/10-bit 4:2:0, key + inter frames | no | AAC-LC (`craft_audio::aac`) |
|
||||
//! | WAV / AIFF | RIFF WAVE / AIFF (AIFF-C for float) | — | — | PCM 16/24-bit or 32-bit float, mono/stereo (audio only) |
|
||||
//!
|
||||
//! Every frame goes through the internal `pipeline` module: the Render Settings overrides (Effects, Solo Switches,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//! A R G B / R G B order, and each row is padded to an even number of bytes (only depth 24 at odd
|
||||
//! widths needs it).
|
||||
|
||||
use filmcraft_isobmff::{CodecConfig, FourCc, SampleEntry, VideoParams};
|
||||
use craft_media::isobmff::{CodecConfig, FourCc, SampleEntry, VideoParams};
|
||||
|
||||
use crate::encode::{Packet, VideoEncoder};
|
||||
use crate::{ExportError, Result};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! (size patched at the end), Info, Tracks and one Cluster per render batch. Video frames are
|
||||
//! SimpleBlocks; with alpha (Channels: RGB + Alpha) each frame is a BlockGroup whose
|
||||
//! BlockAdditional (BlockAddID 1) carries a second VP9 frame coding the alpha channel as luma,
|
||||
//! with `AlphaMode` 1 on the track, as WebM defines. VP9 comes from `effectcraft-vp9enc` (key
|
||||
//! with `AlphaMode` 1 on the track, as WebM defines. VP9 comes from `craft_video::vp9` (key
|
||||
//! frames every two seconds, inter frames between them: SimpleBlocks carry the key flag, inter
|
||||
//! BlockGroups a ReferenceBlock; the alpha stream's key frames line up with the colour stream's),
|
||||
//! Opus from `effectcraft-opusenc` (CELT, 48 kHz stereo, 20 ms packets,
|
||||
@@ -142,7 +142,7 @@ pub(crate) async fn webm(job: &Cx<'_>, comp: &Comp, w: u32, h: u32, st: &mut Sta
|
||||
let colour_channels = if job.output.channels == Channels::Alpha { Channels::Alpha } else { Channels::Rgb };
|
||||
let quality = job.output.quality.clamp(1, 100);
|
||||
let fps = rate.as_f64().max(1.0);
|
||||
let cfg = effectcraft_vp9enc::EncoderConfig {
|
||||
let cfg = craft_video::vp9::EncoderConfig {
|
||||
width: w,
|
||||
height: h,
|
||||
quality,
|
||||
@@ -152,8 +152,8 @@ pub(crate) async fn webm(job: &Cx<'_>, comp: &Comp, w: u32, h: u32, st: &mut Sta
|
||||
frame_rate: fps,
|
||||
loop_filter: true,
|
||||
};
|
||||
let mut color_enc = effectcraft_vp9enc::Vp9Encoder::new(cfg.clone());
|
||||
let mut alpha_enc = alpha.then(|| effectcraft_vp9enc::Vp9Encoder::new(cfg.clone()));
|
||||
let mut color_enc = craft_video::vp9::Vp9Encoder::new(cfg.clone());
|
||||
let mut alpha_enc = alpha.then(|| craft_video::vp9::Vp9Encoder::new(cfg.clone()));
|
||||
let with_audio = wants_audio(job);
|
||||
let opus_channels = if job.output.audio_channels == 1 { 1usize } else { 2 };
|
||||
let mut opus = with_audio.then(|| opus_encoder(job));
|
||||
@@ -266,12 +266,12 @@ pub(crate) async fn webm(job: &Cx<'_>, comp: &Comp, w: u32, h: u32, st: &mut Sta
|
||||
for (j, px) in pixels.iter().enumerate() {
|
||||
let (color, a) = rayon::join(
|
||||
|| {
|
||||
let (y, u, vv) = effectcraft_vp9enc::rgba_to_yuv420(px, w, h, false);
|
||||
let (y, u, vv) = craft_video::vp9::rgba_to_yuv420(px, w, h, false);
|
||||
color_enc.encode_frame(&y, &u, &vv, false)
|
||||
},
|
||||
|| {
|
||||
alpha_enc.as_mut().map(|ea| {
|
||||
let (y, u, vv) = effectcraft_vp9enc::alpha_to_yuv420(px, w, h);
|
||||
let (y, u, vv) = craft_video::vp9::alpha_to_yuv420(px, w, h);
|
||||
ea.encode_frame(&y, &u, &vv, false).data
|
||||
})
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user