Native Renderkern: Rust/GStreamer-Plugin nach ADR-0004 (996 Zeilen)

- Cargo.toml: gstreamer-rs 0.22, windows-rs 0.58 (D3D11/DXGI/HLSL),
  rmp-serde, serde, thiserror; cdylib+rlib; release mit LTO
- lib.rs: Modul-Struktur, GStreamer-Plugin-Registrierung (hmscompositor),
  FFI-Interface (hms_build_pipeline, hms_push_frame)
- compositor.rs: alle 9 Blend-Modi (§12.4), LayerTransform,
  D3D11-TextureHandle, LayerCompositor ohne CPU-Readback (§12.6)
- shader_loader.rs: HmsParams-cbuffer nach §14.4 (u_resolution,
  u_time_seconds, u_audio_*, 16 Plugin-Params), D3DCompile (ps_5_0),
  FrameSnapshot-Parameter-Bindung
- frame_receiver.rs: FrameSnapshot aus MessagePack (Feldnamen identisch
  zu Python engine.py), Thread-sicher
- pipeline_builder.rs: d3d11h264dec -> d3d11convert -> hmscompositor ->
  d3d11videosink (§13.1)
- README.md: Windows-Build-Anleitung, Python-Integration (ctypes)
- Nicht kompiliert (ADR-0008: kein cargo im Container); zwei
  Pruefpunkte fuer Windows-Durchlauf dokumentiert:
  SimpleElement-Subklasse, D3DCompile-Signatur
This commit is contained in:
HMS MediaEngine Agent
2026-09-11 08:37:06 +02:00
parent 73da76f44a
commit cbe1360aaa
7 changed files with 996 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
//! HMS MediaEngine native render bridge (ADR-0004).
//!
//! Rust/GStreamer-D3D11-Renderkern. Der Python-Render-Worker (§6.1C) orchestriert
//! Pipelines und übergibt pro Frame einen unveränderlichen `FrameSnapshot`
//! (MessagePack über IPC). Dieser Crate stellt:
//!
//! - ein GStreamer-Plugin mit einem eigenen Compositor-Element (`hmscompositor`),
//! - einen D3D11-Layer-Compositor (Blend-Modi §12.4),
//! - einen HLSL-Shader-Loader mit dem Standard-cbuffer-Layout (§14.4),
//! - einen FrameReceiver für binäres MessagePack,
//! - Pipeline-Builder für die D3D11-Elementkette (§13.1).
//!
//! Kein CPU-Readback im Normalpfad (§12.6, §33).
pub mod compositor;
pub mod frame_receiver;
pub mod pipeline_builder;
pub mod shader_loader;
use gstreamer::glib;
use gstreamer::prelude::*;
use gstreamer::subclass::prelude::*;
use gstreamer::{ElementFactory, Plugin};
/// Plugin-Name, unter dem das Element in GStreamer registriert wird.
pub const PLUGIN_NAME: &str = "hmsrender";
/// Element-Name des eigenen Compositors.
pub const ELEMENT_NAME: &str = "hmscompositor";
/// Registriert das HMS-Render-Plugin bei GStreamer.
///
/// Wird vom Python-Orchestrator beim Laden der `libhms_render_bridge`-Bibliothek
/// aufgerufen.
pub fn plugin_init(plugin: &Plugin) -> Result<(), glib::BoolError> {
ElementFactory::register(
plugin,
ELEMENT_NAME,
gstreamer::Rank::PRIMARY,
compositor::Compositor::static_type(),
)?;
Ok(())
}
/// GStreamer-Plugin-Deskriptor (statisch registriert beim Laden der cdylib).
gstreamer::plugin_define!(
hmsrender,
env!("CARGO_PKG_DESCRIPTION"),
plugin_init,
concat!(env!("CARGO_PKG_VERSION"), "-", env!("CARGO_PKG_NAME")),
"MIT",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION"),
"2026-09-11",
"hmsrender/plugin.rs"
);
/// Bridge-API für den Python-Orchestrator (FFI-freundlich, C-kompatibel).
///
/// Der Python-Prozess lädt die cdylib und ruft diese Funktionen auf, um
/// Pipelines zu bauen und Frames zu übergeben. Keine Pixelverarbeitung in
/// Python (§33).
pub mod ffi {
use crate::frame_receiver::FrameReceiver;
use crate::pipeline_builder::{build_render_pipeline, RenderPipelineConfig};
/// Baut eine Render-Pipeline aus einer JSON-kodierten Konfiguration.
///
/// # Safety
/// `config_json` muss ein gültiger, null-terminierter C-String sein.
#[no_mangle]
pub unsafe extern "C" fn hms_build_pipeline(config_json: *const std::os::raw::c_char) -> i32 {
let config = match std::ffi::CStr::from_ptr(config_json).to_str() {
Ok(s) => s,
Err(_) => return -1,
};
let cfg: RenderPipelineConfig = match serde_json::from_str(config) {
Ok(c) => c,
Err(_) => return -2,
};
match build_render_pipeline(&cfg) {
Ok(_) => 0,
Err(_) => -3,
}
}
/// Empfängt einen binären MessagePack-`FrameSnapshot` und reicht ihn an den
/// Compositor weiter.
///
/// # Safety
/// `data` muss `len` gültige Bytes zeigen.
#[no_mangle]
pub unsafe extern "C" fn hms_push_frame(data: *const u8, len: usize) -> i32 {
if data.is_null() {
return -1;
}
let bytes = std::slice::from_raw_parts(data, len);
match FrameReceiver::push_snapshot(bytes) {
Ok(()) => 0,
Err(_) => -2,
}
}
}