use std::fs::File; use std::io::Write; use std::time::Instant; use nvenc::bitstream::BitStream; use nvenc::session::InitParams; use nvenc::{ session::Session, sys::guids::{NV_ENC_CODEC_H264_GUID, NV_ENC_PRESET_P3_GUID}, }; #[cfg(windows)] use windows::Win32::Graphics::Direct3D11::{ID3D11Device, ID3D11Texture2D}; #[cfg(windows)] pub fn setup() -> ID3D11Device { let dxgi_factory: windows::Win32::Graphics::Dxgi::IDXGIFactory = unsafe { windows::Win32::Graphics::Dxgi::CreateDXGIFactory() }.unwrap(); let dxgi_adapter = unsafe { dxgi_factory.EnumAdapters(0) }.unwrap(); let desc = unsafe { dxgi_adapter.GetDesc() }.unwrap(); println!("{:?}", windows::core::HSTRING::from_wide(&desc.Description)); let mut device = None; let mut device_context = None; unsafe { use windows::Win32::{ Foundation::HMODULE, Graphics::{ Direct3D::{D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL_11_0}, Direct3D11::{D3D11_CREATE_DEVICE_FLAG, D3D11_SDK_VERSION, D3D11CreateDevice}, }, }; D3D11CreateDevice( &dxgi_adapter, D3D_DRIVER_TYPE_UNKNOWN, HMODULE(std::ptr::null_mut()), D3D11_CREATE_DEVICE_FLAG(0), Some(&[D3D_FEATURE_LEVEL_11_0]), D3D11_SDK_VERSION, Some(&raw mut device), None, Some(&raw mut device_context), ) } .unwrap(); let device = device.unwrap(); let _device_context = device_context.unwrap(); device } #[cfg(windows)] fn image(device: &ID3D11Device) -> ID3D11Texture2D { let data: [u8; 1920 * 4] = std::array::from_fn(|idx| match idx % 4 { 0 => 255, 1 => 0, 2 => 0, 3 => 255, _ => unreachable!(), }); let mut texture = None; let desc = windows::Win32::Graphics::Direct3D11::D3D11_TEXTURE2D_DESC { Width: 1920, Height: 1080, MipLevels: 1, ArraySize: 1, Format: windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, SampleDesc: windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC { Count: 1, Quality: 0, }, Usage: windows::Win32::Graphics::Direct3D11::D3D11_USAGE_DEFAULT, BindFlags: windows::Win32::Graphics::Direct3D11::D3D11_BIND_SHADER_RESOURCE.0 as u32, CPUAccessFlags: windows::Win32::Graphics::Dxgi::Common::DXGI_CPU_ACCESS_NONE, MiscFlags: 0, }; let data = windows::Win32::Graphics::Direct3D11::D3D11_SUBRESOURCE_DATA { pSysMem: data.as_ptr() as _, SysMemPitch: 1920 * 4, SysMemSlicePitch: 0, }; unsafe { device.CreateTexture2D( &raw const desc, Some(&raw const data), Some(&raw mut texture), ) } .unwrap(); texture.unwrap() } fn main() { let device = setup(); #[cfg(target_os = "linux")] compile_error!("Linux is currently unsupported in this example"); #[cfg(windows)] let session: Session = Session::open_dx(&device).unwrap(); assert!( session .get_encode_codecs() .unwrap() .contains(&NV_ENC_CODEC_H264_GUID) ); assert!( session .get_encode_presets(NV_ENC_CODEC_H264_GUID) .unwrap() .contains(&NV_ENC_PRESET_P3_GUID) ); let (session, mut config) = session .get_encode_preset_config_ex( NV_ENC_CODEC_H264_GUID, NV_ENC_PRESET_P3_GUID, nvenc::sys::enums::NVencTuningInfo::LowLatency, ) .unwrap(); let texture = image(&device); config.preset_cfg.rc_params.rate_control_mode = nvenc::sys::enums::NVencParamsRcMode::VBR; config.preset_cfg.rc_params.average_bit_rate = 10_000_000; config.preset_cfg.gop_len = 0xffffffff; config.preset_cfg.frame_interval_p = 1; println!("P-Frames: {}", config.preset_cfg.frame_interval_p); println!("Bitrate: {}", config.preset_cfg.rc_params.average_bit_rate); let init_params = InitParams { encode_guid: NV_ENC_CODEC_H264_GUID, preset_guid: NV_ENC_PRESET_P3_GUID, aspect_ratio: [16, 9], encode_config: &mut config.preset_cfg, tuning_info: nvenc::sys::enums::NVencTuningInfo::LowLatency, buffer_format: nvenc::sys::enums::NVencBufferFormat::ARGB, frame_rate: [30, 1], resolution: [1920, 1080], enable_ptd: true, max_encoder_resolution: [0, 0], }; println!("FPS: 30"); println!("Len: 600 frames"); println!("Expected time: {}", 600 / 30); println!("Codec: H264"); println!("Tuning Info {:?}:", init_params.tuning_info); let encoder = session.init_encoder(init_params).unwrap(); let registered = encoder .register_resource_dx11(&texture, nvenc::sys::enums::NVencBufferFormat::ARGB, 0) .unwrap(); let (processed, to_use) = std::sync::mpsc::sync_channel::(2); let (re_use, to_process) = std::sync::mpsc::sync_channel::(2); processed .send(encoder.create_bitstream_buffer().unwrap()) .unwrap(); processed .send(encoder.create_bitstream_buffer().unwrap()) .unwrap(); let handle = std::thread::spawn({ move || { let mut out = File::create("output.h264").unwrap(); while let Ok(output) = to_process.recv() { let lock = output.try_lock(true).unwrap(); out.write_all(lock.as_slice()).unwrap(); drop(lock); if let Err(_) = processed.send(output) { break; } } } }); let instant = Instant::now(); let mut i = 0; while i < 600 { if let Ok(output) = to_use.recv() { encoder .encode_picture( ®istered, &output, i, instant.duration_since(Instant::now()).as_millis() as u64, nvenc::sys::enums::NVencBufferFormat::ARGB, nvenc::sys::enums::NVencPicStruct::Frame, nvenc::sys::enums::NVencPicType::P, None, ) .unwrap(); re_use.send(output).unwrap(); i += 1; } } drop(to_use); drop(re_use); handle.join().unwrap(); }