mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-11-21 19:11:02 +00:00
relationmeta: Add relationmeta2onvifmeta element
Add relationmeta2onvifmeta which convert relation metas to ONVIF metas and add them to buffer. Used relation metas are removed from buffer. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1464>
This commit is contained in:
parent
de153222da
commit
fc3cefc38c
12 changed files with 687 additions and 0 deletions
37
Cargo.lock
generated
37
Cargo.lock
generated
|
@ -2756,6 +2756,21 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gst-plugin-relationmeta"
|
||||
version = "0.14.0-alpha.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"glib",
|
||||
"gst-plugin-version-helper",
|
||||
"gstreamer",
|
||||
"gstreamer-analytics",
|
||||
"gstreamer-base",
|
||||
"gstreamer-rtp",
|
||||
"gstreamer-video",
|
||||
"xmltree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gst-plugin-reqwest"
|
||||
version = "0.14.0-alpha.1"
|
||||
|
@ -3189,6 +3204,28 @@ dependencies = [
|
|||
"system-deps 7.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-analytics"
|
||||
version = "0.24.0"
|
||||
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#d1ece4bb12bba4cbe9cc933206caaa2b79c7011d"
|
||||
dependencies = [
|
||||
"glib",
|
||||
"gstreamer",
|
||||
"gstreamer-analytics-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-analytics-sys"
|
||||
version = "0.24.0"
|
||||
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#d1ece4bb12bba4cbe9cc933206caaa2b79c7011d"
|
||||
dependencies = [
|
||||
"glib-sys",
|
||||
"gstreamer-sys",
|
||||
"libc",
|
||||
"system-deps 7.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-app"
|
||||
version = "0.24.0"
|
||||
|
|
|
@ -31,6 +31,7 @@ members = [
|
|||
"net/onvif",
|
||||
"net/raptorq",
|
||||
"net/reqwest",
|
||||
"net/relationmeta",
|
||||
"net/rtp",
|
||||
"net/rtsp",
|
||||
"net/webrtchttp",
|
||||
|
@ -86,6 +87,7 @@ default-members = [
|
|||
"net/onvif",
|
||||
"net/raptorq",
|
||||
"net/reqwest",
|
||||
"net/relationmeta",
|
||||
"net/rtp",
|
||||
"net/rtsp",
|
||||
"net/webrtchttp",
|
||||
|
@ -142,6 +144,7 @@ gdk-x11 = { package = "gdk4-x11", git = "https://github.com/gtk-rs/gtk4-rs", bra
|
|||
gdk-win32 = { package = "gdk4-win32", git = "https://github.com/gtk-rs/gtk4-rs", branch = "main", features = ["v4_4"]}
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
|
||||
gst-allocators = { package = "gstreamer-allocators", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
|
||||
gst-analytics = { package = "gstreamer-analytics", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
|
||||
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
|
||||
|
|
|
@ -24,6 +24,7 @@ RS_PREFIXED = [
|
|||
'rtp',
|
||||
'rtsp',
|
||||
'inter',
|
||||
'relationmeta',
|
||||
]
|
||||
|
||||
OVERRIDE = {
|
||||
|
|
|
@ -32,6 +32,7 @@ RENAMES = {
|
|||
'rsrtsp': 'rtsp',
|
||||
'rswebp': 'webp',
|
||||
'rsonvif': 'onvif',
|
||||
'rsrelationmeta': 'relationmeta',
|
||||
'rstracers': 'tracers',
|
||||
'rsclosedcaption': 'closedcaption',
|
||||
'rswebrtc': 'webrtc',
|
||||
|
|
|
@ -7967,6 +7967,78 @@
|
|||
"tracers": {},
|
||||
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
},
|
||||
"rsrelationmeta": {
|
||||
"description": "GStreamer Rust Relation Meta Plugin",
|
||||
"elements": {
|
||||
"relationmeta2onvifmeta": {
|
||||
"author": "Benjamin Gaignard <benjamin.gaignard@collabora.com>",
|
||||
"description": "Convert relation metadata to ONVIF metadata",
|
||||
"hierarchy": [
|
||||
"GstRelationMeta2OnvifMeta",
|
||||
"GstElement",
|
||||
"GstObject",
|
||||
"GInitiallyUnowned",
|
||||
"GObject"
|
||||
],
|
||||
"klass": "Metadata",
|
||||
"pad-templates": {
|
||||
"sink": {
|
||||
"caps": "ANY",
|
||||
"direction": "sink",
|
||||
"presence": "always"
|
||||
},
|
||||
"src": {
|
||||
"caps": "ANY",
|
||||
"direction": "src",
|
||||
"presence": "always"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"time-source": {
|
||||
"blurb": "Time source for UTC timestamps",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "clock (0)",
|
||||
"mutable": "ready",
|
||||
"readable": true,
|
||||
"type": "GstRsOnvifNtpTimeSource",
|
||||
"writable": true
|
||||
}
|
||||
},
|
||||
"rank": "none"
|
||||
}
|
||||
},
|
||||
"filename": "gstrsrelationmeta",
|
||||
"license": "MPL-2.0",
|
||||
"other-types": {
|
||||
"GstRsOnvifNtpTimeSource": {
|
||||
"kind": "enum",
|
||||
"values": [
|
||||
{
|
||||
"desc": "UNIX time based on realtime clock.",
|
||||
"name": "clock",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"desc": "Running time is in UTC",
|
||||
"name": "running-time",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"desc": "Pipeline clock is UTC",
|
||||
"name": "clock-time",
|
||||
"value": "2"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"package": "gst-plugin-relationmeta",
|
||||
"source": "gst-plugin-relationmeta",
|
||||
"tracers": {},
|
||||
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
},
|
||||
"rsrtp": {
|
||||
"description": "GStreamer Rust RTP Plugin",
|
||||
"elements": {
|
||||
|
|
|
@ -91,6 +91,9 @@ endif
|
|||
if get_option('threadshare').allowed() or get_option('rtsp').allowed()
|
||||
deps += [['gstreamer-net-1.0', 'gstreamer', 'gst_net_dep', 'gst_net']]
|
||||
endif
|
||||
if get_option('relationmeta').allowed()
|
||||
deps += [['gstreamer-analytics-1.0', 'gst-plugins-bad', 'gstanalytics_dep', 'gstanalytics']]
|
||||
endif
|
||||
|
||||
glib_dep = dependency('glib-2.0', version: glib_req)
|
||||
deps_cache += {'glib-2.0': glib_dep}
|
||||
|
@ -155,6 +158,7 @@ plugins = {
|
|||
},
|
||||
'raptorq': {'library': 'libgstraptorq'},
|
||||
'reqwest': {'library': 'libgstreqwest'},
|
||||
'relationmeta': {'library': 'libgstrsrelationmeta'},
|
||||
'rtsp': {'library': 'libgstrsrtsp'},
|
||||
'rtp': {'library': 'libgstrsrtp'},
|
||||
'webrtchttp': {'library': 'libgstwebrtchttp'},
|
||||
|
|
|
@ -33,6 +33,7 @@ option('ndi', type: 'feature', value: 'auto', description: 'Build ndi plugin')
|
|||
option('onvif', type: 'feature', value: 'auto', description: 'Build onvif plugin')
|
||||
option('raptorq', type: 'feature', value: 'auto', description: 'Build raptorq plugin')
|
||||
option('reqwest', type: 'feature', value: 'auto', description: 'Build reqwest plugin')
|
||||
option('relationmeta', type: 'feature', value: 'auto', description: 'Build relationmeta plugin')
|
||||
option('rtsp', type: 'feature', value: 'auto', description: 'Build rtsp plugin')
|
||||
option('rtp', type: 'feature', value: 'auto', description: 'Build rtp plugin')
|
||||
option('webrtc', type: 'feature', value: 'auto', yield: true, description: 'Build webrtc plugin')
|
||||
|
|
46
net/relationmeta/Cargo.toml
Normal file
46
net/relationmeta/Cargo.toml
Normal file
|
@ -0,0 +1,46 @@
|
|||
[package]
|
||||
name = "gst-plugin-relationmeta"
|
||||
version.workspace = true
|
||||
authors = ["Benjamin Gaignard <benjamin.gaignard@collabora.com>"]
|
||||
repository.workspace = true
|
||||
license = "MPL-2.0"
|
||||
description = "GStreamer Rust Relation Meta Plugin"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
gst = { workspace = true, features = ["v1_24"] }
|
||||
gst-rtp = { workspace = true, features = ["v1_24"] }
|
||||
gst-base = { workspace = true, features = ["v1_24"] }
|
||||
gst-video = { workspace = true, features = ["v1_24"] }
|
||||
gst-analytics = { workspace = true }
|
||||
chrono = { version = "0.4.31", default-features = false }
|
||||
xmltree = "0.10"
|
||||
glib = { workspace = true, features = ["v2_62"] }
|
||||
|
||||
[lib]
|
||||
name = "gstrsrelationmeta"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper.workspace = true
|
||||
|
||||
[features]
|
||||
static = []
|
||||
capi = []
|
||||
doc = ["gst/v1_18"]
|
||||
|
||||
[package.metadata.capi]
|
||||
min_version = "0.9.21"
|
||||
|
||||
[package.metadata.capi.header]
|
||||
enabled = false
|
||||
|
||||
[package.metadata.capi.library]
|
||||
install_subdir = "gstreamer-1.0"
|
||||
versioning = false
|
||||
import_library = false
|
||||
|
||||
[package.metadata.capi.pkg_config]
|
||||
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gobject-2.0, glib-2.0, gmodule-2.0, gstreamer-analytics-1.0"
|
3
net/relationmeta/build.rs
Normal file
3
net/relationmeta/build.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
gst_plugin_version_helper::info()
|
||||
}
|
42
net/relationmeta/src/lib.rs
Normal file
42
net/relationmeta/src/lib.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright (C) 2024 Benjamin Gaignard <benjamin.gaignard@collabora.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
#![allow(clippy::non_send_fields_in_send_ty, unused_doc_comments)]
|
||||
|
||||
/**
|
||||
* plugin-rsrelationmeta:
|
||||
*
|
||||
* Since: plugins-rs-0.13.0
|
||||
*/
|
||||
use gst::glib;
|
||||
|
||||
pub(crate) const ONVIF_METADATA_SCHEMA: &str = "http://www.onvif.org/ver10/schema";
|
||||
pub(crate) const ONVIF_METADATA_PREFIX: &str = "tt";
|
||||
|
||||
mod relationmeta2onvifmeta;
|
||||
|
||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
relationmeta2onvifmeta::register(plugin)?;
|
||||
|
||||
if !gst::meta::CustomMeta::is_registered("OnvifXMLFrameMeta") {
|
||||
gst::meta::CustomMeta::register("OnvifXMLFrameMeta", &[]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
gst::plugin_define!(
|
||||
rsrelationmeta,
|
||||
env!("CARGO_PKG_DESCRIPTION"),
|
||||
plugin_init,
|
||||
concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
|
||||
"MPL-2.0",
|
||||
env!("CARGO_PKG_NAME"),
|
||||
env!("CARGO_PKG_NAME"),
|
||||
env!("CARGO_PKG_REPOSITORY"),
|
||||
env!("BUILD_REL_DATE")
|
||||
);
|
449
net/relationmeta/src/relationmeta2onvifmeta/imp.rs
Normal file
449
net/relationmeta/src/relationmeta2onvifmeta/imp.rs
Normal file
|
@ -0,0 +1,449 @@
|
|||
// Copyright (C) 2024 Benjamin Gaignard <benjamin.gaignard@collabora.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
use gst_analytics::*;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::Enum)]
|
||||
#[repr(u32)]
|
||||
#[enum_type(name = "GstRsOnvifNtpTimeSource")]
|
||||
pub enum TimeSource {
|
||||
#[enum_value(name = "UNIX time based on realtime clock.", nick = "clock")]
|
||||
Clock,
|
||||
#[enum_value(name = "Running time is in UTC", nick = "running-time")]
|
||||
RunningTime,
|
||||
#[enum_value(name = "Pipeline clock is UTC", nick = "clock-time")]
|
||||
ClockTime,
|
||||
}
|
||||
|
||||
const DEFAULT_TIME_SOURCE: TimeSource = TimeSource::Clock;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Settings {
|
||||
timesource: TimeSource,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Settings {
|
||||
timesource: DEFAULT_TIME_SOURCE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct State {
|
||||
video_info: Option<gst_video::VideoInfo>,
|
||||
segment: gst::FormattedSegment<gst::ClockTime>,
|
||||
}
|
||||
|
||||
pub struct RelationMeta2OnvifMeta {
|
||||
// Input media stream with relation metadata
|
||||
sinkpad: gst::Pad,
|
||||
// Output media stream with ONVIF metadata
|
||||
srcpad: gst::Pad,
|
||||
state: Mutex<State>,
|
||||
settings: Mutex<Settings>,
|
||||
}
|
||||
|
||||
static CAT: LazyLock<gst::DebugCategory> = LazyLock::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"relationmeta2onvifmeta",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Relation metadata to ONVIF metadata"),
|
||||
)
|
||||
});
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for RelationMeta2OnvifMeta {
|
||||
const NAME: &'static str = "GstRelationMeta2OnvifMeta";
|
||||
type Type = super::RelationMeta2OnvifMeta;
|
||||
type ParentType = gst::Element;
|
||||
|
||||
fn with_class(klass: &Self::Class) -> Self {
|
||||
let templ = klass.pad_template("sink").unwrap();
|
||||
let sinkpad = gst::Pad::builder_from_template(&templ)
|
||||
.chain_function(|pad, parent, buffer| {
|
||||
RelationMeta2OnvifMeta::catch_panic_pad_function(
|
||||
parent,
|
||||
|| Err(gst::FlowError::Error),
|
||||
|convert| convert.sink_chain(pad, buffer),
|
||||
)
|
||||
})
|
||||
.event_function(|pad, parent, event| {
|
||||
RelationMeta2OnvifMeta::catch_panic_pad_function(
|
||||
parent,
|
||||
|| false,
|
||||
|convert| convert.sink_event(pad, event),
|
||||
)
|
||||
})
|
||||
.flags(gst::PadFlags::PROXY_CAPS)
|
||||
.flags(gst::PadFlags::PROXY_ALLOCATION)
|
||||
.build();
|
||||
let templ = klass.pad_template("src").unwrap();
|
||||
let srcpad = gst::Pad::builder_from_template(&templ)
|
||||
.flags(gst::PadFlags::PROXY_CAPS)
|
||||
.flags(gst::PadFlags::PROXY_ALLOCATION)
|
||||
.build();
|
||||
|
||||
Self {
|
||||
srcpad,
|
||||
sinkpad,
|
||||
state: Mutex::new(State::default()),
|
||||
settings: Mutex::new(Settings::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for RelationMeta2OnvifMeta {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: LazyLock<Vec<glib::ParamSpec>> = LazyLock::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecEnum::builder_with_default("time-source", DEFAULT_TIME_SOURCE)
|
||||
.nick("time source")
|
||||
.blurb("Time source for UTC timestamps")
|
||||
.mutable_ready()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"time-source" => {
|
||||
let mut settings = self.settings.lock().unwrap();
|
||||
settings.timesource = value.get::<TimeSource>().expect("type checked upstream");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"time-source" => {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
settings.timesource.to_value()
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let obj = self.obj();
|
||||
obj.add_pad(&self.sinkpad).unwrap();
|
||||
obj.add_pad(&self.srcpad).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl GstObjectImpl for RelationMeta2OnvifMeta {}
|
||||
|
||||
impl ElementImpl for RelationMeta2OnvifMeta {
|
||||
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||
static ELEMENT_METADATA: LazyLock<gst::subclass::ElementMetadata> = LazyLock::new(|| {
|
||||
gst::subclass::ElementMetadata::new(
|
||||
"Relation metadata to ONVIF metadata",
|
||||
"Metadata",
|
||||
"Convert relation metadata to ONVIF metadata",
|
||||
"Benjamin Gaignard <benjamin.gaignard@collabora.com>",
|
||||
)
|
||||
});
|
||||
|
||||
Some(&*ELEMENT_METADATA)
|
||||
}
|
||||
|
||||
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||
static PAD_TEMPLATES: LazyLock<Vec<gst::PadTemplate>> = LazyLock::new(|| {
|
||||
let sink_caps = gst::Caps::new_any();
|
||||
let sink_pad_template = gst::PadTemplate::new(
|
||||
"sink",
|
||||
gst::PadDirection::Sink,
|
||||
gst::PadPresence::Always,
|
||||
&sink_caps,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let src_caps = gst::Caps::new_any();
|
||||
let src_pad_template = gst::PadTemplate::new(
|
||||
"src",
|
||||
gst::PadDirection::Src,
|
||||
gst::PadPresence::Always,
|
||||
&src_caps,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
vec![src_pad_template, sink_pad_template]
|
||||
});
|
||||
|
||||
PAD_TEMPLATES.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl RelationMeta2OnvifMeta {
|
||||
fn sink_event(&self, pad: &gst::Pad, event: gst::Event) -> bool {
|
||||
use gst::EventView;
|
||||
|
||||
match event.view() {
|
||||
EventView::Caps(c) => {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
let info = match gst_video::VideoInfo::from_caps(c.caps()) {
|
||||
Ok(info) => info,
|
||||
Err(_) => {
|
||||
gst::error!(CAT, obj = pad, "Failed to parse caps {:?}", c.caps());
|
||||
return false;
|
||||
}
|
||||
};
|
||||
state.video_info = Some(info);
|
||||
self.srcpad.push_event(event)
|
||||
}
|
||||
EventView::Segment(s) => {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
let segment = s.segment().clone();
|
||||
let segment = match segment.downcast::<gst::ClockTime>() {
|
||||
Ok(segment) => segment,
|
||||
Err(_) => {
|
||||
gst::element_imp_error!(
|
||||
self,
|
||||
gst::CoreError::Event,
|
||||
["Only time segments are supported"]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
state.segment = segment.clone();
|
||||
gst::Pad::event_default(pad, Some(&*self.obj()), event)
|
||||
}
|
||||
_ => gst::Pad::event_default(pad, Some(&*self.obj()), event),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_utc_time(&self, running_time: gst::ClockTime) -> gst::ClockTime {
|
||||
match self.settings.lock().unwrap().timesource {
|
||||
TimeSource::Clock => {
|
||||
if let (Some(base_time), Some(clock)) = (self.obj().base_time(), self.obj().clock())
|
||||
{
|
||||
let utc_now = std::time::SystemTime::now()
|
||||
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let utc_now = gst::ClockTime::from_nseconds(utc_now);
|
||||
let running_time_now = clock.time().unwrap() - base_time;
|
||||
|
||||
let rt_diff = utc_now - running_time_now;
|
||||
running_time_now + rt_diff
|
||||
} else {
|
||||
gst::error!(
|
||||
CAT,
|
||||
imp = self,
|
||||
"Can only use the clock if the pipeline has a clock"
|
||||
);
|
||||
gst::ClockTime::ZERO
|
||||
}
|
||||
}
|
||||
TimeSource::RunningTime => running_time,
|
||||
TimeSource::ClockTime => {
|
||||
if let Some(base_time) = self.obj().base_time() {
|
||||
running_time + base_time
|
||||
} else {
|
||||
gst::warning!(
|
||||
CAT,
|
||||
imp = self,
|
||||
"Clock time was selected but there is no base time"
|
||||
);
|
||||
gst::ClockTime::ZERO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sink_chain(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
mut input_buffer: gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
let metas = input_buffer.meta::<AnalyticsRelationMeta>();
|
||||
|
||||
let mut xml = xmltree::Element::new("MetadataStream");
|
||||
xml.namespaces
|
||||
.get_or_insert_with(|| xmltree::Namespace(Default::default()))
|
||||
.put("tt", crate::ONVIF_METADATA_SCHEMA);
|
||||
xml.namespace = Some(String::from(crate::ONVIF_METADATA_SCHEMA));
|
||||
xml.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
|
||||
let mut video_analytics = xmltree::Element::new("VideoAnalytics");
|
||||
video_analytics.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
let state = self.state.lock().unwrap();
|
||||
|
||||
let current_time =
|
||||
self.get_utc_time(state.segment.to_running_time(input_buffer.pts()).unwrap());
|
||||
let dt =
|
||||
gst::DateTime::from_unix_epoch_utc_usecs(current_time.useconds().try_into().unwrap())
|
||||
.unwrap()
|
||||
.to_g_date_time()
|
||||
.unwrap()
|
||||
.format_iso8601()
|
||||
.unwrap();
|
||||
|
||||
let mut frame = xmltree::Element::new("Frame");
|
||||
frame.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
frame
|
||||
.attributes
|
||||
.insert("UtcTime".to_string(), dt.to_string());
|
||||
|
||||
let mut transformation = xmltree::Element::new("Transformation");
|
||||
transformation.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
|
||||
let mut translate = xmltree::Element::new("Translate");
|
||||
translate.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
translate
|
||||
.attributes
|
||||
.insert("x".to_string(), (-1.0).to_string());
|
||||
translate
|
||||
.attributes
|
||||
.insert("y".to_string(), (-1.0).to_string());
|
||||
|
||||
let mut scale = xmltree::Element::new("Scale");
|
||||
scale.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
|
||||
let video_info = state.video_info.as_ref().unwrap();
|
||||
let x = format!("{:.5}", 2_f64 / (video_info.width() as f64));
|
||||
let y = format!("{:.5}", 2_f64 / (video_info.height() as f64));
|
||||
scale.attributes.insert("x".to_string(), x);
|
||||
scale.attributes.insert("y".to_string(), y);
|
||||
|
||||
transformation
|
||||
.children
|
||||
.push(xmltree::XMLNode::Element(translate));
|
||||
transformation
|
||||
.children
|
||||
.push(xmltree::XMLNode::Element(scale));
|
||||
|
||||
frame
|
||||
.children
|
||||
.push(xmltree::XMLNode::Element(transformation));
|
||||
|
||||
if let Some(metas) = metas {
|
||||
for meta in metas.iter::<AnalyticsODMtd>() {
|
||||
let loc = meta.location().unwrap();
|
||||
|
||||
let mut object = xmltree::Element::new("Object");
|
||||
object.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
object
|
||||
.attributes
|
||||
.insert("ObjectId".to_string(), meta.id().to_string());
|
||||
|
||||
let mut appearance = xmltree::Element::new("Appearance");
|
||||
appearance.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
|
||||
let mut shape = xmltree::Element::new("Shape");
|
||||
shape.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
|
||||
let mut bounding_box = xmltree::Element::new("BoundingBox");
|
||||
bounding_box.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
bounding_box
|
||||
.attributes
|
||||
.insert("left".to_string(), loc.x.to_string());
|
||||
bounding_box
|
||||
.attributes
|
||||
.insert("top".to_string(), loc.y.to_string());
|
||||
bounding_box
|
||||
.attributes
|
||||
.insert("right".to_string(), (loc.x + loc.w).to_string());
|
||||
bounding_box
|
||||
.attributes
|
||||
.insert("bottom".to_string(), (loc.y + loc.h).to_string());
|
||||
shape.children.push(xmltree::XMLNode::Element(bounding_box));
|
||||
|
||||
let mut class = xmltree::Element::new("Class");
|
||||
class.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
|
||||
let mut t = xmltree::Element::new("Type");
|
||||
t.prefix = Some(String::from(crate::ONVIF_METADATA_PREFIX));
|
||||
t.attributes
|
||||
.insert("Likelihood".to_string(), loc.loc_conf_lvl.to_string());
|
||||
let obj_name = if let Some(obj_type) = meta.obj_type() {
|
||||
obj_type.as_str()
|
||||
} else {
|
||||
"Unknown"
|
||||
};
|
||||
t.children
|
||||
.push(xmltree::XMLNode::Text(obj_name.to_string()));
|
||||
|
||||
gst::trace!(
|
||||
CAT,
|
||||
imp = self,
|
||||
"Transformed object: {}x{}@({}h{}) prob: {} name: {:?}",
|
||||
loc.w,
|
||||
loc.w,
|
||||
loc.x,
|
||||
loc.y,
|
||||
loc.loc_conf_lvl,
|
||||
meta.obj_type()
|
||||
);
|
||||
|
||||
class.children.push(xmltree::XMLNode::Element(t));
|
||||
|
||||
appearance.children.push(xmltree::XMLNode::Element(shape));
|
||||
appearance.children.push(xmltree::XMLNode::Element(class));
|
||||
|
||||
object.children.push(xmltree::XMLNode::Element(appearance));
|
||||
|
||||
frame.children.push(xmltree::XMLNode::Element(object));
|
||||
}
|
||||
} else {
|
||||
gst::trace!(CAT, imp = self, "No meta, nothing to transform");
|
||||
}
|
||||
|
||||
video_analytics
|
||||
.children
|
||||
.push(xmltree::XMLNode::Element(frame));
|
||||
xml.children
|
||||
.push(xmltree::XMLNode::Element(video_analytics));
|
||||
|
||||
let mut text = Vec::new();
|
||||
if let Err(err) = xml.write_with_config(
|
||||
&mut text,
|
||||
xmltree::EmitterConfig {
|
||||
perform_indent: true,
|
||||
..xmltree::EmitterConfig::default()
|
||||
},
|
||||
) {
|
||||
gst::error!(CAT, obj = pad, "Can't serialize XML element: {}", err);
|
||||
}
|
||||
|
||||
gst::trace!(
|
||||
CAT,
|
||||
imp = self,
|
||||
"Generated ONVIF xml: {}",
|
||||
std::str::from_utf8(&text).unwrap()
|
||||
);
|
||||
|
||||
let meta_buf = gst::Buffer::from_mut_slice(text);
|
||||
let mut buflist = gst::BufferList::new();
|
||||
buflist.get_mut().unwrap().add(meta_buf);
|
||||
|
||||
let buf = input_buffer.make_mut();
|
||||
let mut onvif_meta = gst::meta::CustomMeta::add(buf, "OnvifXMLFrameMeta").unwrap();
|
||||
let s = onvif_meta.mut_structure();
|
||||
s.set("frames", buflist);
|
||||
|
||||
gst::trace!(CAT, obj = pad, "Send buffer {:?}", buf);
|
||||
self.srcpad.push(input_buffer)
|
||||
}
|
||||
}
|
28
net/relationmeta/src/relationmeta2onvifmeta/mod.rs
Normal file
28
net/relationmeta/src/relationmeta2onvifmeta/mod.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright (C) 2024 Benjamin Gaignard <benjamin.gaignard@collabora.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct RelationMeta2OnvifMeta(ObjectSubclass<imp::RelationMeta2OnvifMeta>) @extends gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
#[cfg(feature = "doc")]
|
||||
imp::TimeSource::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty());
|
||||
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"relationmeta2onvifmeta",
|
||||
gst::Rank::NONE,
|
||||
RelationMeta2OnvifMeta::static_type(),
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue