Skip to content

Commit 59dd5fe

Browse files
committed
Introduce PLUGIN_VARIANT
1 parent e3df0ea commit 59dd5fe

File tree

12 files changed

+353
-30
lines changed

12 files changed

+353
-30
lines changed

README.md

Lines changed: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,111 @@ cargo build --release --no-default-features --features eim
259259
cargo cache -a
260260
```
261261

262+
#### Building Multiple Plugin Variants
263+
264+
The plugin supports building multiple variants that can coexist in the same GStreamer installation. This is useful when you need to run different models or configurations in the same pipeline.
265+
266+
**Why PLUGIN_VARIANT?**
267+
268+
GStreamer identifies plugins by three key attributes:
269+
1. **Library filename**: The shared library file that contains the plugin
270+
2. **Plugin name**: The internal plugin identifier registered with GStreamer
271+
3. **Element names**: The names of individual elements (e.g., `edgeimpulsevideoinfer`)
272+
273+
To allow multiple plugin builds to coexist, each variant must have unique values for all three. The `PLUGIN_VARIANT` environment variable automatically handles this by:
274+
275+
- **Library naming**: After building, use the `rename-library.sh` script to rename the output library from `libgstedgeimpulse.{dylib,so,dll}` to `libgstedgeimpulse_{variant}.{dylib,so,dll}`
276+
- **Plugin naming**: The plugin name becomes `gst-plugins-edgeimpulse_{variant}` instead of just `gst-plugins-edgeimpulse`
277+
- **Element naming**: All elements are automatically suffixed with `_{variant}` (e.g., `edgeimpulsevideoinfer_variantX`, `edgeimpulseaudioinfer_variantX`, etc.)
278+
279+
**Usage:**
280+
281+
1. **Build with a variant:**
282+
```bash
283+
# Build variant "variantX"
284+
PLUGIN_VARIANT=variantX cargo build --release
285+
286+
# After build completes, rename the library
287+
PLUGIN_VARIANT=variantX ./rename-library.sh
288+
```
289+
290+
2. **Build multiple variants:**
291+
```bash
292+
# Build first variant
293+
PLUGIN_VARIANT=variantX \
294+
EI_MODEL=~/Downloads/model-a \
295+
EI_ENGINE=tflite \
296+
USE_FULL_TFLITE=1 \
297+
cargo build --release
298+
PLUGIN_VARIANT=variantX ./rename-library.sh
299+
300+
# Build second variant (with different model or configuration)
301+
PLUGIN_VARIANT=variantY \
302+
EI_MODEL=~/Downloads/model-b \
303+
EI_ENGINE=tflite \
304+
USE_FULL_TFLITE=1 \
305+
cargo build --release
306+
PLUGIN_VARIANT=variantY ./rename-library.sh
307+
```
308+
309+
3. **Use both variants in the same pipeline:**
310+
```bash
311+
# Make sure both libraries are in GST_PLUGIN_PATH
312+
export GST_PLUGIN_PATH="$(pwd)/target/release"
313+
314+
# Use elements from both variants
315+
gst-launch-1.0 \
316+
videotestsrc ! \
317+
edgeimpulsevideoinfer_variantX ! \
318+
edgeimpulseoverlay_variantX ! \
319+
queue ! \
320+
edgeimpulsevideoinfer_variantY ! \
321+
edgeimpulseoverlay_variantY ! \
322+
autovideosink
323+
```
324+
325+
**Technical Details:**
326+
327+
- The `PLUGIN_VARIANT` environment variable must be set during both the build and rename steps
328+
- The `rename-library.sh` script renames the output library from `libgstedgeimpulse.{dylib,so,dll}` to `libgstedgeimpulse_{variant}.{dylib,so,dll}`
329+
- Each variant produces a uniquely named library file, allowing GStreamer to load multiple variants simultaneously
330+
- Element names include the variant suffix, preventing naming conflicts when multiple variants are loaded
331+
332+
**Example Workflow:**
333+
334+
```bash
335+
# Build variant for model A
336+
PLUGIN_VARIANT=person-detection \
337+
EI_MODEL=~/Downloads/person-detection-v140 \
338+
EI_ENGINE=tflite \
339+
USE_FULL_TFLITE=1 \
340+
cargo build --release
341+
PLUGIN_VARIANT=person-detection ./rename-library.sh
342+
343+
# Build variant for model B
344+
PLUGIN_VARIANT=anomaly-detection \
345+
EI_MODEL=~/Downloads/anomaly-detection-v50 \
346+
EI_ENGINE=tflite \
347+
USE_FULL_TFLITE=1 \
348+
cargo build --release
349+
PLUGIN_VARIANT=anomaly-detection ./rename-library.sh
350+
351+
# Both libraries will be in target/release:
352+
# - libgstedgeimpulse_person-detection.dylib
353+
# - libgstedgeimpulse_anomaly-detection.dylib
354+
355+
# Use both in a pipeline
356+
export GST_PLUGIN_PATH="$(pwd)/target/release"
357+
gst-launch-1.0 \
358+
videotestsrc ! \
359+
edgeimpulsevideoinfer_person-detection ! \
360+
edgeimpulseoverlay_person-detection ! \
361+
queue ! \
362+
edgeimpulsevideoinfer_anomaly-detection ! \
363+
edgeimpulseoverlay_anomaly-detection ! \
364+
autovideosink
365+
```
366+
262367
#### Environment Variables
263368

264369
**Required for FFI Mode:**
@@ -272,11 +377,11 @@ cargo build --release --no-default-features --features eim
272377

273378
**Platform-Specific Variables:**
274379
- `TARGET`: Standard Rust target triple (e.g., `aarch64-unknown-linux-gnu`, `x86_64-apple-darwin`)
275-
- `TARGET_MAC_ARM64=1`: Build for Apple Silicon (M1/M2/M3) - legacy
276-
- `TARGET_MAC_X86_64=1`: Build for Intel Mac - legacy
277-
- `TARGET_LINUX_X86=1`: Build for Linux x86_64 - legacy
278-
- `TARGET_LINUX_AARCH64=1`: Build for Linux ARM64 - legacy
279-
- `TARGET_LINUX_ARMV7=1`: Build for Linux ARMv7 - legacy
380+
- `TARGET_MAC_ARM64=1`: Build for Apple Silicon (M1/M2/M3)
381+
- `TARGET_MAC_X86_64=1`: Build for Intel Mac
382+
- `TARGET_LINUX_X86=1`: Build for Linux x86_64
383+
- `TARGET_LINUX_AARCH64=1`: Build for Linux ARM64
384+
- `TARGET_LINUX_ARMV7=1`: Build for Linux ARMv7
280385

281386
**Example:**
282387
```bash

build.rs

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::env;
2+
13
fn main() {
24
// Set PKG_CONFIG_PATH to include all necessary directories for macOS
35
if cfg!(target_os = "macos") {
@@ -8,17 +10,130 @@ fn main() {
810
format!("{}/share/pkgconfig", homebrew_prefix),
911
];
1012

11-
let existing_path = std::env::var("PKG_CONFIG_PATH").unwrap_or_default();
13+
let existing_path = env::var("PKG_CONFIG_PATH").unwrap_or_default();
1214
let new_path = if existing_path.is_empty() {
1315
pkg_config_paths.join(":")
1416
} else {
1517
format!("{}:{}", pkg_config_paths.join(":"), existing_path)
1618
};
1719

1820
println!("cargo:warning=Setting PKG_CONFIG_PATH to: {}", new_path);
19-
std::env::set_var("PKG_CONFIG_PATH", &new_path);
21+
env::set_var("PKG_CONFIG_PATH", &new_path);
2022
println!("cargo:rerun-if-env-changed=PKG_CONFIG_PATH");
2123
}
2224

25+
// Set PLUGIN_VARIANT for plugin/element name suffixing
26+
let plugin_variant = env::var("PLUGIN_VARIANT").unwrap_or_else(|_| "".to_string());
27+
println!("cargo:rustc-env=PLUGIN_VARIANT={}", plugin_variant);
28+
29+
// Generate the full plugin name
30+
// GStreamer plugin names should use hyphens, not underscores
31+
let pkg_name = env::var("CARGO_PKG_NAME").expect("CARGO_PKG_NAME should be set by Cargo");
32+
let plugin_name = if plugin_variant.is_empty() {
33+
pkg_name
34+
} else {
35+
format!("{}-{}", pkg_name, plugin_variant)
36+
};
37+
println!("cargo:rustc-env=PLUGIN_NAME={}", plugin_name);
38+
39+
// Generate the plugin identifier for gst::plugin_define!
40+
// GStreamer derives the registration function name from the library filename, not the plugin name
41+
// For libgstedgeimpulse_banana.dylib, it expects gst_plugin_edgeimpulse_banana_register
42+
// So we strip "libgst" and ".dylib" from the library name to get the identifier
43+
let lib_name = if plugin_variant.is_empty() {
44+
"edgeimpulse".to_string()
45+
} else {
46+
// Convert variant to a valid identifier (replace any non-alphanumeric with underscore)
47+
let variant_ident = plugin_variant
48+
.chars()
49+
.map(|c| {
50+
if c.is_alphanumeric() || c == '_' {
51+
c
52+
} else {
53+
'_'
54+
}
55+
})
56+
.collect::<String>();
57+
format!("edgeimpulse_{}", variant_ident)
58+
};
59+
let plugin_identifier = lib_name;
60+
61+
// Generate type names for GObject types to make them unique per variant
62+
// This prevents type registration conflicts when multiple variants are loaded
63+
let type_suffix = if plugin_variant.is_empty() {
64+
"".to_string()
65+
} else {
66+
// Convert variant to a valid identifier for type names (capitalize first letter)
67+
let variant_capitalized = plugin_variant
68+
.chars()
69+
.enumerate()
70+
.map(|(i, c)| {
71+
if i == 0 {
72+
c.to_uppercase().collect::<String>()
73+
} else if c.is_alphanumeric() || c == '_' {
74+
c.to_string()
75+
} else {
76+
"_".to_string()
77+
}
78+
})
79+
.collect::<String>();
80+
variant_capitalized
81+
};
82+
83+
// Generate the plugin definition code with the correct identifier
84+
// This allows the registration function name to match what GStreamer expects
85+
let out_dir = env::var("OUT_DIR").unwrap();
86+
let plugin_def_path = std::path::Path::new(&out_dir).join("plugin_define.rs");
87+
let plugin_def_code = format!(
88+
r#"gst::plugin_define!(
89+
{},
90+
env!("CARGO_PKG_DESCRIPTION"),
91+
plugin_init,
92+
env!("CARGO_PKG_VERSION"),
93+
"MIT/X11",
94+
env!("PLUGIN_NAME"),
95+
env!("PLUGIN_NAME"),
96+
"https://github.com/edgeimpulse/gst-plugins-edgeimpulse",
97+
env!("BUILD_REL_DATE")
98+
);
99+
"#,
100+
plugin_identifier
101+
);
102+
std::fs::write(&plugin_def_path, plugin_def_code)
103+
.expect("Failed to write plugin definition file");
104+
105+
// Generate type name constants for each element type
106+
// This prevents type registration conflicts when multiple variants are loaded
107+
let type_names_path = std::path::Path::new(&out_dir).join("type_names.rs");
108+
let type_names_code = format!(
109+
r#"// Auto-generated type names for variant: {}
110+
#[allow(dead_code)]
111+
pub const VIDEO_INFER_TYPE_NAME: &str = "EdgeImpulseVideoInfer{}";
112+
#[allow(dead_code)]
113+
pub const AUDIO_INFER_TYPE_NAME: &str = "EdgeImpulseAudioInfer{}";
114+
#[allow(dead_code)]
115+
pub const OVERLAY_TYPE_NAME: &str = "EdgeImpulseOverlay{}";
116+
#[allow(dead_code)]
117+
pub const SINK_TYPE_NAME: &str = "GstEdgeImpulseSink{}";
118+
"#,
119+
plugin_variant,
120+
type_suffix,
121+
type_suffix,
122+
type_suffix,
123+
type_suffix
124+
);
125+
std::fs::write(&type_names_path, type_names_code)
126+
.expect("Failed to write type names file");
127+
128+
if !plugin_variant.is_empty() {
129+
println!("cargo:warning=PLUGIN_VARIANT is set to: {}", plugin_variant);
130+
println!("cargo:warning=Plugin name: {}", plugin_name);
131+
println!("cargo:warning=Plugin identifier: {}", plugin_identifier);
132+
println!(
133+
"cargo:warning=After build completes, run: PLUGIN_VARIANT={} ./rename-library.sh",
134+
plugin_variant
135+
);
136+
}
137+
23138
gst_plugin_version_helper::info()
24139
}

rename-library.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
# Rename the output library to include PLUGIN_VARIANT suffix
3+
# This allows multiple plugin variants to coexist in the same GStreamer installation
4+
#
5+
# Usage:
6+
# PLUGIN_VARIANT=variantX ./rename-library.sh
7+
# Or after building: cargo build --release && PLUGIN_VARIANT=variantX ./rename-library.sh
8+
9+
set -e
10+
11+
if [ -z "$PLUGIN_VARIANT" ]; then
12+
echo "Error: PLUGIN_VARIANT environment variable is not set"
13+
echo "Usage: PLUGIN_VARIANT=variantX ./rename-library.sh"
14+
exit 1
15+
fi
16+
17+
# Determine the library extension based on the platform
18+
if [[ "$OSTYPE" == "darwin"* ]]; then
19+
LIB_EXT="dylib"
20+
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
21+
LIB_EXT="so"
22+
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
23+
LIB_EXT="dll"
24+
else
25+
LIB_EXT="so" # Default fallback
26+
fi
27+
28+
# Try both debug and release directories
29+
for PROFILE in debug release; do
30+
BUILD_DIR="target/$PROFILE"
31+
ORIGINAL_LIB="libgstedgeimpulse.${LIB_EXT}"
32+
NEW_LIB="libgstedgeimpulse_${PLUGIN_VARIANT}.${LIB_EXT}"
33+
ORIGINAL_D="libgstedgeimpulse.d"
34+
NEW_D="libgstedgeimpulse_${PLUGIN_VARIANT}.d"
35+
36+
if [ -f "$BUILD_DIR/$ORIGINAL_LIB" ]; then
37+
echo "Renaming $ORIGINAL_LIB to $NEW_LIB in $BUILD_DIR"
38+
mv "$BUILD_DIR/$ORIGINAL_LIB" "$BUILD_DIR/$NEW_LIB"
39+
40+
if [ -f "$BUILD_DIR/$ORIGINAL_D" ]; then
41+
mv "$BUILD_DIR/$ORIGINAL_D" "$BUILD_DIR/$NEW_D"
42+
fi
43+
echo "Successfully renamed library to $NEW_LIB"
44+
exit 0
45+
fi
46+
done
47+
48+
echo "Error: Library libgstedgeimpulse.${LIB_EXT} not found in target/debug or target/release"
49+
echo "Please run 'cargo build' or 'cargo build --release' first"
50+
exit 1
51+

src/audio/imp.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,18 @@ use once_cell::sync::Lazy;
100100
use std::collections::VecDeque;
101101
use std::sync::Mutex;
102102

103+
// Include generated type names for variant-specific builds
104+
include!(concat!(env!("OUT_DIR"), "/type_names.rs"));
105+
103106
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
107+
let variant = env!("PLUGIN_VARIANT");
108+
let name = if variant.is_empty() {
109+
"edgeimpulseaudioinfer".to_string()
110+
} else {
111+
format!("edgeimpulseaudioinfer_{}", variant)
112+
};
104113
gst::DebugCategory::new(
105-
"edgeimpulseaudioinfer",
114+
&name,
106115
gst::DebugColorFlags::empty(),
107116
Some("Edge Impulse Audio Inference"),
108117
)
@@ -170,7 +179,7 @@ pub struct EdgeImpulseAudioInfer {
170179

171180
#[glib::object_subclass]
172181
impl ObjectSubclass for EdgeImpulseAudioInfer {
173-
const NAME: &'static str = "EdgeImpulseAudioInfer";
182+
const NAME: &'static str = AUDIO_INFER_TYPE_NAME;
174183
type Type = super::EdgeImpulseAudioInfer;
175184
type ParentType = gstreamer_base::BaseTransform;
176185
}

src/audio/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@ unsafe impl Sync for EdgeImpulseAudioInfer {}
1717

1818
// Register the type for our element
1919
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
20+
let variant = env!("PLUGIN_VARIANT");
21+
let name = if variant.is_empty() {
22+
"edgeimpulseaudioinfer".to_string()
23+
} else {
24+
format!("edgeimpulseaudioinfer_{}", variant)
25+
};
2026
gst::Element::register(
2127
Some(plugin),
22-
"edgeimpulseaudioinfer",
28+
&name,
2329
gst::Rank::NONE,
2430
EdgeImpulseAudioInfer::static_type(),
2531
)

src/lib.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,6 @@ fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
1515
Ok(())
1616
}
1717

18-
gst::plugin_define!(
19-
edgeimpulse,
20-
env!("CARGO_PKG_DESCRIPTION"),
21-
plugin_init,
22-
concat!(env!("CARGO_PKG_VERSION")),
23-
"MIT/X11",
24-
env!("CARGO_PKG_NAME"),
25-
env!("CARGO_PKG_NAME"),
26-
"https://github.com/edgeimpulse/gst-plugins-edgeimpulse",
27-
env!("BUILD_REL_DATE")
28-
);
18+
// Include the dynamically generated plugin definition from build.rs
19+
// This ensures the registration function name matches what GStreamer expects
20+
include!(concat!(env!("OUT_DIR"), "/plugin_define.rs"));

0 commit comments

Comments
 (0)