1use crate::callsite::GstCallsiteKind;
2use gst::{
3 glib::{GStr, translate::*},
4 prelude::*,
5};
6use std::sync::{LazyLock, Mutex};
7use tracing_core::{Callsite, Event, Level};
8
9fn log_handler(
10 category: gst::DebugCategory,
11 level: gst::DebugLevel,
12 file: &gst::glib::GStr,
13 module: &gst::glib::GStr,
14 line: u32,
15 object: Option<&gst::LoggedObject>,
16 message: &gst::DebugMessage,
17) {
18 skip_assert_initialized!();
19 let level = match level {
20 gst::DebugLevel::Error => Level::ERROR,
21 gst::DebugLevel::Warning | gst::DebugLevel::Fixme => Level::WARN,
22 gst::DebugLevel::Info => Level::INFO,
23 gst::DebugLevel::Debug | gst::DebugLevel::Log => Level::DEBUG,
24 gst::DebugLevel::Trace | gst::DebugLevel::Memdump => Level::TRACE,
25 _ => return,
26 };
27 let category_name = category.name();
28 let callsite = crate::callsite::DynamicCallsites::get().callsite_for(
29 level,
30 "",
31 category_name,
32 Some(file.as_str()),
33 Some(module.as_str()),
34 Some(line),
35 GstCallsiteKind::Event,
36 &[
37 "message",
38 "gobject.address",
39 "gobject.type",
40 "gstobject.name",
41 "gstelement.state",
42 "gstelement.pending_state",
43 "gstpad.state",
44 "gstpad.parent.name",
45 "gstpad.parent.state",
46 "gstpad.parent.pending_state",
47 ],
48 );
49 let interest = callsite.interest();
50 if interest.is_never() {
51 return;
52 }
53 let meta = callsite.metadata();
54 tracing_core::dispatcher::get_default(move |dispatcher| {
55 if !dispatcher.enabled(meta) {
56 return;
57 }
58 let fields = meta.fields();
59 let message_str = message.get();
60 let message_value = message_str.as_deref().map(|g| g.as_str());
61
62 let gobject = object.map(|o| o.as_ptr());
63 let gobject = unsafe {
64 gobject.filter(|&ptr| (*ptr).ref_count != 0)
68 };
69 let gobject_address_value = gobject.map(|obj| obj as usize);
70 let gobject_with_ty = gobject.and_then(|obj| unsafe {
71 let ty: gst::glib::Type =
72 from_glib(obj.as_ref()?.g_type_instance.g_class.as_ref()?.g_type);
73 Some((obj, ty))
74 });
75 let gobject_type_value = gobject_with_ty.as_ref().map(|(_, ty)| ty.name());
76 let gstobject = gobject_with_ty.and_then(|(obj, ty)| {
77 if ty.is_a(gst::Object::static_type()) {
78 Some(obj as *mut gst::ffi::GstObject)
79 } else {
80 None
81 }
82 });
83
84 let gstobject_name = gstobject.as_ref().and_then(|gstobject| unsafe {
85 Some(GStr::from_ptr((*(*gstobject)).name.as_ref()?).as_str())
89 });
90
91 let user_span = gstobject.as_ref().and_then(|gstobject| unsafe {
92 let quark = *span_quark();
93 let obj: gst::Object = ref_gst_object(*gstobject);
94 obj.qdata::<tracing::Span>(quark)
95 .map(|s| s.as_ref().clone())
96 });
97
98 let gstobject_name_value = gstobject_name;
99 let gstelement = gobject_with_ty.as_ref().and_then(|(obj, ty)| {
100 if ty.is_a(gst::Element::static_type()) {
101 Some(*obj as *mut gst::ffi::GstElement)
102 } else {
103 None
104 }
105 });
106 let gstelement_states = gstelement.map(|e| unsafe {
107 let curr: gst::State = from_glib((*e).current_state);
108 let pend: gst::State = from_glib((*e).pending_state);
109 (curr.name().as_str(), pend.name().as_str())
110 });
111 let gstelement_state_value = gstelement_states.map(|(c, _)| c);
112 let gstelement_pending_state_value = gstelement_states.map(|(_, p)| p);
113 let gstpad = gobject_with_ty.as_ref().and_then(|(obj, ty)| {
114 if ty.is_a(gst::Pad::static_type()) {
115 Some(*obj as *mut gst::ffi::GstPad)
116 } else {
117 None
118 }
119 });
120 let gstpad_flags = gstpad.map(|p| unsafe {
121 let flags = gst::PadFlags::from_bits_truncate((*p).object.flags);
123 tracing_core::field::display(flags)
124 });
125 let gstpad_parent = gstpad.and_then(|p| unsafe {
126 let parent = (*p).object.parent;
128 if parent.is_null() || (*parent).object.ref_count == 0 {
129 None
130 } else {
131 Some(parent)
132 }
133 });
134 let gstpad_parent_name = gstpad_parent.and_then(|obj| unsafe {
135 Some(GStr::from_ptr((*obj).name.as_ref()?).as_str())
137 });
138 let gstpad_parent_name_value = gstpad_parent_name;
139
140 let gstpad_parent_states = gstpad_parent.and_then(|obj| unsafe {
141 let ty: gst::glib::Type =
142 from_glib((*obj).object.g_type_instance.g_class.as_ref()?.g_type);
143 if ty.is_a(gst::Element::static_type()) {
144 let e = obj as *mut gst::ffi::GstElement;
145 let curr: gst::State = from_glib((*e).current_state);
146 let pend: gst::State = from_glib((*e).pending_state);
147 Some((curr.name().as_str(), pend.name().as_str()))
148 } else {
149 None
150 }
151 });
152 let gstpad_parent_state_value = gstpad_parent_states.map(|(c, _)| c);
153 let gstpad_parent_pending_state_value = gstpad_parent_states.map(|(_, p)| p);
154 let mut fields_iter = fields.into_iter();
155 let values = field_values![fields_iter =>
156 "message" = message_value;
158 "gobject.address" = gobject_address_value;
159 "gobject.type" = gobject_type_value;
160 "gstobject.name" = gstobject_name_value;
161 "gstelement.state" = gstelement_state_value;
162 "gstelement.pending_state" = gstelement_pending_state_value;
163 "gstpad.flags" = gstpad_flags;
164 "gstpad.parent.name" = gstpad_parent_name_value;
165 "gstpad.parent.state" = gstpad_parent_state_value;
166 "gstpad.parent.pending_state" = gstpad_parent_pending_state_value;
167 ];
168 let valueset = fields.value_set(&values);
169
170 let event = match user_span {
171 Some(user_span) => Event::new_child_of(user_span, meta, &valueset),
172 None => Event::new(meta, &valueset),
173 };
174
175 dispatcher.event(&event);
176 });
177}
178
179static LOG_FUNCTION: Mutex<Option<gst::log::DebugLogFunction>> = Mutex::new(None);
180
181pub(crate) fn debug_add_log_function() {
182 let handle = gst::log::add_log_function(log_handler);
183 *LOG_FUNCTION.lock().unwrap() = Some(handle);
184}
185
186pub(crate) fn debug_remove_log_function() {
187 if let Some(handle) = LOG_FUNCTION.lock().unwrap().take() {
188 gst::log::remove_log_function(handle);
189 }
190}
191
192unsafe fn ref_gst_object(ptr: *mut gst::ffi::GstObject) -> gst::Object {
197 unsafe {
198 gst::glib::gobject_ffi::g_object_ref(ptr as *mut gst::glib::gobject_ffi::GObject);
199 from_glib_full(ptr)
200 }
201}
202
203#[inline]
204pub(crate) fn span_quark() -> &'static gst::glib::Quark {
205 #[allow(dead_code)]
212 struct QDataTracingSpan(tracing::Span);
213
214 static ELEMENT_SPAN_QUARK: LazyLock<gst::glib::Quark> = LazyLock::new(|| {
215 let type_id = std::any::TypeId::of::<QDataTracingSpan>();
216 let mut hasher = std::collections::hash_map::DefaultHasher::new();
217 std::hash::Hash::hash(&type_id, &mut hasher);
218 let type_id_hash = std::hash::Hasher::finish(&hasher);
219 let key = format!("tracing-gstreamer:{type_id_hash}\0");
220 let gstr = GStr::from_utf8_with_nul(key.as_bytes()).unwrap();
221 gst::glib::Quark::from_str(gstr)
222 });
223
224 &ELEMENT_SPAN_QUARK
225}
226
227pub unsafe fn attach_span<O: IsA<gst::Object>>(object: &O, span: tracing::Span) {
256 if unsafe { gst::ffi::gst_is_initialized() == gst::glib::ffi::GTRUE } {
257 static INIT_PROPAGATION_TRACER: std::sync::Once = std::sync::Once::new();
258 INIT_PROPAGATION_TRACER.call_once(|| {
259 let tracer = gst::glib::Object::new::<crate::tracer::SpanPropagationTracer>();
261 std::mem::forget(tracer);
262 });
263 }
264
265 unsafe {
266 object.set_qdata(*span_quark(), span);
276 }
277}