1#![cfg_attr(not(test), warn(clippy::print_stderr))]
12
13mod audio_host;
14mod audio_input;
15pub use audio_input::AudioStreamConfig;
16mod audio_timeline;
17mod bitstream;
18mod capabilities;
19mod convert;
20mod decode;
21mod recovery;
22pub use recovery::{
23 ConcealedAudioGap, CorruptPacket, DECODE_WORK_LIMIT, DecodeDiagnostic, DecodeDiagnosticCounts,
24 DecodeDiagnostics, DecodePolicy, DecodeProgressWatch, DroppedAudioOverlap, VideoRecoveryGap,
25 watch_decode_progress,
26};
27mod encode;
28mod mux;
29mod partial;
30pub use partial::{PARTIAL_OUTPUT_IN_USE, remove_stale_partial_output, write_replacing};
31#[cfg(any(
32 test,
33 all(
34 any(target_os = "linux", target_os = "macos", target_os = "windows"),
35 feature = "hwdecode"
36 )
37))]
38mod parameter_sets;
39mod passthrough;
40pub use passthrough::{
41 AudioBlockFeed, CopyAudio, PacketCopy, PacketCopyAudio, PacketCopyPlan, PacketCopyPlanner,
42 PacketCopyReport, PacketCopyRequest, check_yuv_transcode, copy_packets, plan_packet_copy,
43 same_chroma_siting,
44};
45#[cfg(all(target_os = "linux", feature = "hwdecode"))]
46mod hwdecode;
47#[cfg(feature = "hwencode")]
48mod hwencode;
49mod rate_control;
50pub use rate_control::{AppliedRateControl, RateControlMechanism};
51#[cfg(test)]
52mod tests;
53mod tools;
54pub use tools::{
55 FrameScore, THUMBNAIL_LUMA_RANGE, choose_thumbnail, downscale_frame, resize_rgba, score_frame,
56};
57mod video_encode;
58mod video_frame;
59pub use alpha2_ffmpeg_bridge::Yuv420pFrameBuffer;
60#[cfg(feature = "cpal-host")]
61pub use audio_host::{CpalHostError, CpalOutputHost, RealtimeAudioSource};
62pub use audio_host::{
63 DecodedAudioSources, HostAudioConfig, HostSampleFormat, OfflineAudioError,
64 negotiate_output_config, render_offline,
65};
66#[cfg(feature = "timeline")]
67pub use audio_host::{ProjectAudioRequest, render_project_audio};
68pub use capabilities::{
69 AvEncodeConfig, NativeAudioCodec, NativeAudioEncoderCapability, NativeCodecCapabilities,
70 NativeEncoderClass, NativeEncoderHardwareConfig, NativeEncoderRejection,
71 NativeHardwareDecodeCapabilities, NativeHardwareDecodeCapability, NativeRateControlProbe,
72 NativeVideoCodec, NativeVideoEncoderCapability, NativeVideoEncoderFallback,
73 NativeVideoEncoderProbe, NativeVideoEncoderSelection, codec_capabilities,
74 hardware_decode_backend, hardware_decode_capabilities, hybrid_encoders_use_hardware,
75 probe_video_encoders,
76};
77pub use convert::{
78 CodecThreadAllocation, CodecThreadRequest, codec_thread_allocation, codec_thread_budget,
79 codec_thread_override, configured_codec_thread_override,
80};
81pub use decode::{
82 DecodeStageTimings, NativeAudioDecoder, NativeDecoder, ProbeResult, SOURCE_SAMPLE_RATES, probe,
83 software_decode_unsupported, visit_packet_times,
84};
85pub use encode::{
86 EncodeCallWatch, EncodeStageTimings, NativeAvEncodeSink, NativeWavEncodeSink,
87 watch_encode_calls,
88};
89#[cfg(all(target_os = "linux", feature = "hwdecode"))]
90pub use hwdecode::{VulkanDecodeProbe, probe_vulkan_decode};
91#[cfg(all(target_os = "windows", feature = "hwencode"))]
92pub use hwencode::{
93 D3D12EncodeProbe, D3D12RateControlOpen, D3D12RateControlRunner, D3D12TestEncodeRunner,
94 D3D12VA_TEST_ENCODE_SIZE, d3d12va_encoder, open_d3d12va_rate_control, probe_d3d12va_encode,
95 set_d3d12va_rate_control_runner, set_d3d12va_test_encode_runner,
96};
97#[cfg(feature = "hwencode")]
98pub use hwencode::{
99 EncoderInputHandoff, EncoderInputLayout, EncoderPoolStats, EncoderSurface,
100 EncoderSurfaceDescriptor, HardwareEncodeBackend, HardwareEncodeCapability,
101 HardwareEncodeDevice, HardwareEncodeInput, HardwareEncodeOpenError, HardwareEncodeRequest,
102 HardwareEncodeSession, InputFrame,
103};
104#[cfg(all(target_os = "macos", feature = "hwencode"))]
105pub use hwencode::{VideoToolboxEncodeProbe, probe_videotoolbox_encode};
106#[cfg(all(target_os = "linux", feature = "hwencode"))]
107pub use hwencode::{VulkanEncodeProbe, probe_vulkan_encode};
108pub use video_encode::{NativeVideoEncodeSink, VideoDisplay, VideoEncodeConfig};
109pub use video_frame::{
110 HardwareDecodeFallback, HardwareFrameLease, HardwareOpenError, NativeExportDecoder,
111 NativePlaneLayout, NativeVideoFrame, NativeVideoFrameMetadata,
112};
113
114#[derive(Clone, Debug, Eq, PartialEq)]
116pub struct FfmpegRuntimeIdentity {
117 pub version: String,
119 pub configuration: String,
121 pub pinned_sdk_release: &'static str,
123}
124
125impl FfmpegRuntimeIdentity {
126 #[must_use]
141 pub fn pinned_sdk_mismatch(&self) -> Option<String> {
142 let mut reasons = Vec::new();
143 if self.version != PINNED_SDK_SOURCE_TAG {
144 reasons.push(format!(
145 "version {} is not {PINNED_SDK_SOURCE_TAG}",
146 self.version
147 ));
148 }
149 let flags = || self.configuration.split_whitespace();
150 for flag in flags().filter(|flag| {
151 ["--enable-gpl", "--enable-nonfree", "--enable-version3"].contains(flag)
152 || (flag.starts_with("--enable-lib")
153 && !PINNED_SDK_EXTERNAL_LIBRARIES.contains(flag))
154 }) {
155 reasons.push(format!("configured with {flag}"));
156 }
157 for library in PINNED_SDK_EXTERNAL_LIBRARIES {
158 if !flags().any(|flag| flag == *library) {
159 reasons.push(format!("not configured with {library}"));
160 }
161 }
162 if !flags().any(|flag| flag == "--disable-autodetect") {
163 reasons.push("not configured with --disable-autodetect".to_owned());
164 }
165 (!reasons.is_empty()).then(|| reasons.join(", "))
166 }
167}
168
169pub fn require_pinned_ffmpeg_for_tests() {
181 static MISMATCH: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
182 let mismatch = MISMATCH.get_or_init(|| {
183 let identity = ffmpeg_runtime_identity();
184 let reason = identity.pinned_sdk_mismatch()?;
185 if std::env::var_os("ALPHA2_ALLOW_UNPINNED_FFMPEG").is_some_and(|value| value == "1") {
186 alpha2_os::diagnostic!(
187 "alpha2: testing against FFmpeg {} although it is not the pinned SDK ({reason}): \
188 ALPHA2_ALLOW_UNPINNED_FFMPEG=1",
189 identity.version
190 );
191 return None;
192 }
193 Some(format!(
194 "these tests loaded FFmpeg {}, not the pinned SDK {}: {reason}. The build \
195 linked whatever FFmpeg pkg-config found. Rebuild with \
196 FFMPEG_PKG_CONFIG_PATH=\"$(scripts/fetch-ffmpeg-sdk.sh)/lib/pkgconfig\" \
197 (on Windows, FFMPEG_INCLUDE_DIR and FFMPEG_LIBS_DIR). \
198 ALPHA2_ALLOW_UNPINNED_FFMPEG=1 runs them against this FFmpeg anyway.",
199 identity.version, identity.pinned_sdk_release,
200 ))
201 });
202 if let Some(message) = mismatch {
203 panic!("{message}");
204 }
205}
206
207const PINNED_SDK_SOURCE_TAG: &str = "n8.1.3";
210
211const PINNED_SDK_EXTERNAL_LIBRARIES: &[&str] = &[
215 "--enable-libdav1d",
216 "--enable-libopus",
217 "--enable-libsvtav1",
218 "--enable-libvpx",
219];
220
221#[must_use]
223pub fn ffmpeg_runtime_identity() -> FfmpegRuntimeIdentity {
224 FfmpegRuntimeIdentity {
225 version: alpha2_ffmpeg_bridge::runtime_version(),
226 configuration: alpha2_ffmpeg_bridge::runtime_configuration(),
227 pinned_sdk_release: "ffmpeg-8.1.3-shared-build3",
228 }
229}