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.and_then(|ptr| {
68 if (*ptr).ref_count == 0 {
69 None
70 } else {
71 Some(ptr)
72 }
73 })
74 };
75 let gobject_address_value = gobject.map(|obj| obj as usize);
76 let gobject_with_ty = gobject.and_then(|obj| unsafe {
77 let ty: gst::glib::Type =
78 from_glib(obj.as_ref()?.g_type_instance.g_class.as_ref()?.g_type);
79 Some((obj, ty))
80 });
81 let gobject_type_value = gobject_with_ty.as_ref().map(|(_, ty)| ty.name());
82 let gstobject = gobject_with_ty.and_then(|(obj, ty)| {
83 if ty.is_a(gst::Object::static_type()) {
84 Some(obj as *mut gst::ffi::GstObject)
85 } else {
86 None
87 }
88 });
89
90 let gstobject_name = gstobject.as_ref().and_then(|gstobject| unsafe {
91 Some(GStr::from_ptr((*(*gstobject)).name.as_ref()?).as_str())
95 });
96
97 let user_span = gstobject.as_ref().and_then(|gstobject| unsafe {
98 let quark = *span_quark();
99 let obj: gst::Object = ref_gst_object(*gstobject);
100 obj.qdata::<tracing::Span>(quark)
101 .map(|s| s.as_ref().clone())
102 });
103
104 let gstobject_name_value = gstobject_name;
105 let gstelement = gobject_with_ty.as_ref().and_then(|(obj, ty)| {
106 if ty.is_a(gst::Element::static_type()) {
107 Some(*obj as *mut gst::ffi::GstElement)
108 } else {
109 None
110 }
111 });
112 let gstelement_states = gstelement.map(|e| unsafe {
113 let curr: gst::State = from_glib((*e).current_state);
114 let pend: gst::State = from_glib((*e).pending_state);
115 (curr.name().as_str(), pend.name().as_str())
116 });
117 let gstelement_state_value = gstelement_states.map(|(c, _)| c);
118 let gstelement_pending_state_value = gstelement_states.map(|(_, p)| p);
119 let gstpad = gobject_with_ty.as_ref().and_then(|(obj, ty)| {
120 if ty.is_a(gst::Pad::static_type()) {
121 Some(*obj as *mut gst::ffi::GstPad)
122 } else {
123 None
124 }
125 });
126 let gstpad_flags = gstpad.map(|p| unsafe {
127 let flags = gst::PadFlags::from_bits_truncate((*p).object.flags);
129 tracing_core::field::display(flags)
130 });
131 let gstpad_parent = gstpad.and_then(|p| unsafe {
132 let parent = (*p).object.parent;
134 if parent.is_null() || (*parent).object.ref_count == 0 {
135 None
136 } else {
137 Some(parent)
138 }
139 });
140 let gstpad_parent_name = gstpad_parent.and_then(|obj| unsafe {
141 Some(GStr::from_ptr((*obj).name.as_ref()?).as_str())
143 });
144 let gstpad_parent_name_value = gstpad_parent_name;
145
146 let gstpad_parent_states = gstpad_parent.and_then(|obj| unsafe {
147 let ty: gst::glib::Type =
148 from_glib((*obj).object.g_type_instance.g_class.as_ref()?.g_type);
149 if ty.is_a(gst::Element::static_type()) {
150 let e = obj as *mut gst::ffi::GstElement;
151 let curr: gst::State = from_glib((*e).current_state);
152 let pend: gst::State = from_glib((*e).pending_state);
153 Some((curr.name().as_str(), pend.name().as_str()))
154 } else {
155 None
156 }
157 });
158 let gstpad_parent_state_value = gstpad_parent_states.map(|(c, _)| c);
159 let gstpad_parent_pending_state_value = gstpad_parent_states.map(|(_, p)| p);
160 let mut fields_iter = fields.into_iter();
161 let values = field_values![fields_iter =>
162 "message" = message_value;
164 "gobject.address" = gobject_address_value;
165 "gobject.type" = gobject_type_value;
166 "gstobject.name" = gstobject_name_value;
167 "gstelement.state" = gstelement_state_value;
168 "gstelement.pending_state" = gstelement_pending_state_value;
169 "gstpad.flags" = gstpad_flags;
170 "gstpad.parent.name" = gstpad_parent_name_value;
171 "gstpad.parent.state" = gstpad_parent_state_value;
172 "gstpad.parent.pending_state" = gstpad_parent_pending_state_value;
173 ];
174 let valueset = fields.value_set(&values);
175
176 let event = match user_span {
177 Some(user_span) => Event::new_child_of(user_span, meta, &valueset),
178 None => Event::new(meta, &valueset),
179 };
180
181 dispatcher.event(&event);
182 });
183}
184
185static LOG_FUNCTION: Mutex<Option<gst::log::DebugLogFunction>> = Mutex::new(None);
186
187pub(crate) fn debug_add_log_function() {
188 let handle = gst::log::add_log_function(log_handler);
189 *LOG_FUNCTION.lock().unwrap() = Some(handle);
190}
191
192pub(crate) fn debug_remove_log_function() {
193 if let Some(handle) = LOG_FUNCTION.lock().unwrap().take() {
194 gst::log::remove_log_function(handle);
195 }
196}
197
198unsafe fn ref_gst_object(ptr: *mut gst::ffi::GstObject) -> gst::Object {
203 unsafe {
204 gst::glib::gobject_ffi::g_object_ref(ptr as *mut gst::glib::gobject_ffi::GObject);
205 from_glib_full(ptr)
206 }
207}
208
209#[inline]
210pub(crate) fn span_quark() -> &'static gst::glib::Quark {
211 #[allow(dead_code)]
218 struct QDataTracingSpan(tracing::Span);
219
220 static ELEMENT_SPAN_QUARK: LazyLock<gst::glib::Quark> = LazyLock::new(|| {
221 let type_id = std::any::TypeId::of::<QDataTracingSpan>();
222 let mut hasher = std::collections::hash_map::DefaultHasher::new();
223 std::hash::Hash::hash(&type_id, &mut hasher);
224 let type_id_hash = std::hash::Hasher::finish(&hasher);
225 let key = format!("tracing-gstreamer:{type_id_hash}\0");
226 let gstr = GStr::from_utf8_with_nul(key.as_bytes()).unwrap();
227 gst::glib::Quark::from_str(gstr)
228 });
229
230 &ELEMENT_SPAN_QUARK
231}
232
233pub unsafe fn attach_span<O: IsA<gst::Object>>(object: &O, span: tracing::Span) {
262 if unsafe { gst::ffi::gst_is_initialized() == gst::glib::ffi::GTRUE } {
263 static INIT_PROPAGATION_TRACER: std::sync::Once = std::sync::Once::new();
264 INIT_PROPAGATION_TRACER.call_once(|| {
265 let tracer = gst::glib::Object::new::<crate::tracer::SpanPropagationTracer>();
267 std::mem::forget(tracer);
268 });
269 }
270
271 unsafe {
272 object.set_qdata(*span_quark(), span);
282 }
283}