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::{
31 ExistingOutput, OutputTarget, PARTIAL_OUTPUT_IN_USE, remove_stale_partial_output, write_png,
32 write_whole,
33};
34#[cfg(any(
35 test,
36 all(
37 any(target_os = "linux", target_os = "macos", target_os = "windows"),
38 feature = "hwdecode"
39 )
40))]
41mod parameter_sets;
42mod passthrough;
43pub use passthrough::{
44 AudioBlockFeed, CopyAudio, PacketCopy, PacketCopyAudio, PacketCopyPlan, PacketCopyPlanner,
45 PacketCopyReport, PacketCopyRequest, check_yuv_transcode, copy_packets, plan_packet_copy,
46 same_chroma_siting,
47};
48#[cfg(all(target_os = "linux", feature = "hwdecode"))]
49mod hwdecode;
50#[cfg(feature = "hwencode")]
51mod hwencode;
52mod openh264;
53pub use openh264::{
54 OPENH264_ENCODER_NAME, OPENH264_INSTALL_COMMAND, OPENH264_NOTICE, OPENH264_VERSION,
55 OpenH264Library, OpenH264Provider, OpenH264Status, openh264_status,
56};
57mod rate_control;
58pub use rate_control::{AppliedRateControl, RateControlMechanism};
59#[cfg(test)]
60mod tests;
61mod tools;
62pub use tools::{
63 FrameScore, THUMBNAIL_LUMA_RANGE, choose_thumbnail, downscale_frame, resize_rgba, score_frame,
64};
65mod video_backend;
66mod video_encode;
67mod video_frame;
68pub use alpha2_ffmpeg_bridge::Yuv420pFrameBuffer;
69#[cfg(feature = "cpal-host")]
70pub use audio_host::{CpalHostError, CpalOutputHost, RealtimeAudioSource};
71pub use audio_host::{
72 DecodedAudioSources, HostAudioConfig, HostSampleFormat, OfflineAudioError,
73 negotiate_output_config, render_offline,
74};
75#[cfg(feature = "timeline")]
76pub use audio_host::{ProjectAudioRequest, render_project_audio};
77pub use capabilities::{
78 AvEncodeConfig, NativeAudioCodec, NativeAudioEncoderCapability, NativeCodecCapabilities,
79 NativeEncoderClass, NativeEncoderHardwareConfig, NativeEncoderLibrary, NativeEncoderRejection,
80 NativeHardwareDecodeCapabilities, NativeHardwareDecodeCapability, NativeRateControlProbe,
81 NativeVideoCodec, NativeVideoEncoderCapability, NativeVideoEncoderFallback,
82 NativeVideoEncoderProbe, NativeVideoEncoderSelection, audio_encoder_capabilities,
83 codec_capabilities, h264_encoder_remedy, hardware_decode_backend, hardware_decode_capabilities,
84 hybrid_encoders_use_hardware, no_video_encoder_message, probe_video_encoders,
85 video_encoder_capabilities, video_encoders_without_probing,
86};
87pub use convert::{
88 CodecThreadAllocation, CodecThreadRequest, HARDWARE_DECODER_THREADS, codec_thread_allocation,
89 codec_thread_budget, codec_thread_override, configured_codec_thread_override,
90};
91pub use decode::{
92 DecodeStageTimings, NativeAudioDecoder, NativeDecoder, ProbeResult, SOURCE_SAMPLE_RATES, probe,
93 software_decode_unsupported, visit_packet_times,
94};
95pub use encode::{
96 EncodeCallWatch, EncodeStageTimings, NativeAvEncodeSink, NativeWavEncodeSink,
97 watch_encode_calls,
98};
99#[cfg(all(target_os = "linux", feature = "hwdecode"))]
100pub use hwdecode::{VulkanDecodeProbe, probe_vulkan_decode};
101#[cfg(all(target_os = "windows", feature = "hwencode"))]
102pub use hwencode::{
103 D3D12EncodeProbe, D3D12RateControlOpen, D3D12RateControlRunner, D3D12TestEncodeRunner,
104 D3D12VA_TEST_ENCODE_SIZE, d3d12va_encoder, open_d3d12va_rate_control, probe_d3d12va_encode,
105 set_d3d12va_rate_control_runner, set_d3d12va_test_encode_runner,
106};
107#[cfg(feature = "hwencode")]
108pub use hwencode::{
109 EncoderInputHandoff, EncoderInputLayout, EncoderPoolStats, EncoderSurface,
110 EncoderSurfaceDescriptor, HardwareEncodeBackend, HardwareEncodeCapability,
111 HardwareEncodeDevice, HardwareEncodeInput, HardwareEncodeOpenError, HardwareEncodeRequest,
112 HardwareEncodeSession, InputFrame,
113};
114#[cfg(all(target_os = "macos", feature = "hwencode"))]
115pub use hwencode::{VideoToolboxEncodeProbe, probe_videotoolbox_encode};
116#[cfg(all(target_os = "linux", feature = "hwencode"))]
117pub use hwencode::{VulkanEncodeProbe, probe_vulkan_encode};
118pub use video_encode::{NativeVideoEncodeSink, VideoDisplay, VideoEncodeConfig};
119pub use video_frame::{
120 HardwareDecodeFallback, HardwareFrameLease, HardwareOpenError, NativeExportDecoder,
121 NativePlaneLayout, NativeVideoFrame, NativeVideoFrameMetadata,
122};
123
124#[derive(Clone, Debug, Eq, PartialEq)]
126pub struct FfmpegRuntimeIdentity {
127 pub version: String,
129 pub configuration: String,
131 pub pinned_sdk_release: &'static str,
133}
134
135impl FfmpegRuntimeIdentity {
136 #[must_use]
151 pub fn pinned_sdk_mismatch(&self) -> Option<String> {
152 let mut reasons = Vec::new();
153 if self.version != PINNED_SDK_SOURCE_TAG {
154 reasons.push(format!(
155 "version {} is not {PINNED_SDK_SOURCE_TAG}",
156 self.version
157 ));
158 }
159 let flags = || self.configuration.split_whitespace();
160 for flag in flags().filter(|flag| {
161 ["--enable-gpl", "--enable-nonfree", "--enable-version3"].contains(flag)
162 || (flag.starts_with("--enable-lib")
163 && !PINNED_SDK_EXTERNAL_LIBRARIES.contains(flag))
164 }) {
165 reasons.push(format!("configured with {flag}"));
166 }
167 for library in PINNED_SDK_EXTERNAL_LIBRARIES {
168 if !flags().any(|flag| flag == *library) {
169 reasons.push(format!("not configured with {library}"));
170 }
171 }
172 if !flags().any(|flag| flag == "--disable-autodetect") {
173 reasons.push("not configured with --disable-autodetect".to_owned());
174 }
175 (!reasons.is_empty()).then(|| reasons.join(", "))
176 }
177}
178
179pub fn require_pinned_ffmpeg_for_tests() {
191 static MISMATCH: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
192 let mismatch = MISMATCH.get_or_init(|| {
193 let identity = ffmpeg_runtime_identity();
194 let reason = identity.pinned_sdk_mismatch()?;
195 if std::env::var_os("ALPHA2_ALLOW_UNPINNED_FFMPEG").is_some_and(|value| value == "1") {
196 alpha2_os::diagnostic!(
197 "alpha2: testing against FFmpeg {} although it is not the pinned SDK ({reason}): \
198 ALPHA2_ALLOW_UNPINNED_FFMPEG=1",
199 identity.version
200 );
201 return None;
202 }
203 Some(format!(
204 "these tests loaded FFmpeg {}, not the pinned SDK {}: {reason}. The build \
205 linked whatever FFmpeg pkg-config found. Rebuild with \
206 FFMPEG_PKG_CONFIG_PATH=\"$(scripts/fetch-ffmpeg-sdk.sh)/lib/pkgconfig\" \
207 (on Windows, FFMPEG_INCLUDE_DIR and FFMPEG_LIBS_DIR). \
208 ALPHA2_ALLOW_UNPINNED_FFMPEG=1 runs them against this FFmpeg anyway.",
209 identity.version, identity.pinned_sdk_release,
210 ))
211 });
212 if let Some(message) = mismatch {
213 panic!("{message}");
214 }
215}
216
217const PINNED_SDK_SOURCE_TAG: &str = "n8.1.3";
220
221const PINNED_SDK_EXTERNAL_LIBRARIES: &[&str] = &[
225 "--enable-libdav1d",
226 "--enable-libopus",
227 "--enable-libsvtav1",
228 "--enable-libvpx",
229];
230
231#[must_use]
233pub fn ffmpeg_runtime_identity() -> FfmpegRuntimeIdentity {
234 FfmpegRuntimeIdentity {
235 version: alpha2_ffmpeg_bridge::runtime_version(),
236 configuration: alpha2_ffmpeg_bridge::runtime_configuration(),
237 pinned_sdk_release: "ffmpeg-8.1.3-shared-build4",
238 }
239}