d474cba7c0e46a8f08ab3fb3816d2c7ab4a62d66
[platform/upstream/gstreamer.git] / plugins / tracers / gstlatency.c
1 /* GStreamer
2  * Copyright (C) 2013 Stefan Sauer <ensonic@users.sf.net>
3  *
4  * gstlatency.c: tracing module that logs processing latency stats
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include "gstlatency.h"
27
28 GST_DEBUG_CATEGORY_STATIC (gst_latency_debug);
29 #define GST_CAT_DEFAULT gst_latency_debug
30
31 #define _do_init \
32     GST_DEBUG_CATEGORY_INIT (gst_latency_debug, "latency", 0, "latency tracer");
33 #define gst_latency_tracer_parent_class parent_class
34 G_DEFINE_TYPE_WITH_CODE (GstLatencyTracer, gst_latency_tracer, GST_TYPE_TRACER,
35     _do_init);
36
37 static GQuark latency_probe_id;
38 static GQuark latency_probe_pad;
39 static GQuark latency_probe_ts;
40
41 /* logging */
42
43 static void
44 log_trace (GstStructure * s)
45 {
46   gchar *data;
47
48   // TODO(ensonic): use a GVariant?
49   data = gst_structure_to_string (s);
50   GST_TRACE ("%s", data);
51   g_free (data);
52   gst_structure_free (s);
53 }
54
55 /* data helpers */
56
57 /*
58  * Get the element/bin owning the pad. 
59  *
60  * in: a normal pad
61  * out: the element
62  *
63  * in: a proxy pad
64  * out: the element that contains the peer of the proxy
65  *
66  * in: a ghost pad
67  * out: the bin owning the ghostpad
68  */
69 /* TODO(ensonic): gst_pad_get_parent_element() would not work here, should we
70  * add this as new api, e.g. gst_pad_find_parent_element();
71  */
72 static GstElement *
73 get_real_pad_parent (GstPad * pad)
74 {
75   GstObject *parent;
76
77   if (!pad)
78     return NULL;
79
80   parent = GST_OBJECT_PARENT (pad);
81
82   /* if parent of pad is a ghost-pad, then pad is a proxy_pad */
83   if (parent && GST_IS_GHOST_PAD (parent)) {
84     pad = GST_PAD_CAST (parent);
85     parent = GST_OBJECT_PARENT (pad);
86   }
87   return GST_ELEMENT_CAST (parent);
88 }
89
90 /* tracer class */
91
92 static void gst_latency_tracer_invoke (GstTracer * obj, GstTracerHookId id,
93     GstTracerMessageId mid, va_list var_args);
94
95 static void
96 gst_latency_tracer_class_init (GstLatencyTracerClass * klass)
97 {
98   GstTracerClass *gst_tracer_class = GST_TRACER_CLASS (klass);
99
100   gst_tracer_class->invoke = gst_latency_tracer_invoke;
101
102   latency_probe_id = g_quark_from_static_string ("latency_probe.id");
103   latency_probe_pad = g_quark_from_static_string ("latency_probe.pad");
104   latency_probe_ts = g_quark_from_static_string ("latency_probe.ts");
105 }
106
107 static void
108 gst_latency_tracer_init (GstLatencyTracer * self)
109 {
110   g_object_set (self, "mask", GST_TRACER_HOOK_BUFFERS | GST_TRACER_HOOK_EVENTS,
111       NULL);
112 }
113
114 /* hooks */
115
116 static void
117 send_latency_probe (GstLatencyTracer * self, GstElement * parent, GstPad * pad,
118     guint64 ts)
119 {
120   if (parent && (!GST_IS_BIN (parent)) &&
121       GST_OBJECT_FLAG_IS_SET (parent, GST_ELEMENT_FLAG_SOURCE)) {
122     GstEvent *latency_probe = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
123         gst_structure_new_id (latency_probe_id,
124             latency_probe_pad, GST_TYPE_PAD, pad,
125             latency_probe_ts, G_TYPE_UINT64, ts,
126             NULL));
127     gst_pad_push_event (pad, latency_probe);
128   }
129 }
130
131 static void
132 do_push_buffer_pre (GstLatencyTracer * self, va_list var_args)
133 {
134   guint64 ts = va_arg (var_args, guint64);
135   GstPad *pad = va_arg (var_args, GstPad *);
136   GstElement *parent = get_real_pad_parent (pad);
137
138   send_latency_probe (self, parent, pad, ts);
139 }
140
141 static void
142 do_pull_buffer_pre (GstLatencyTracer * self, va_list var_args)
143 {
144   guint64 ts = va_arg (var_args, guint64);
145   GstPad *pad = va_arg (var_args, GstPad *);
146   GstPad *peer_pad = GST_PAD_PEER (pad);
147   GstElement *parent = get_real_pad_parent (peer_pad);
148
149   send_latency_probe (self, parent, peer_pad, ts);
150 }
151
152 static void
153 do_push_event_pre (GstLatencyTracer * self, va_list var_args)
154 {
155   guint64 ts = va_arg (var_args, guint64);
156   GstPad *pad = va_arg (var_args, GstPad *);
157   GstEvent *ev = va_arg (var_args, GstEvent *);
158   GstPad *peer_pad = GST_PAD_PEER (pad);
159   GstElement *parent = get_real_pad_parent (peer_pad);
160
161   if (parent && (!GST_IS_BIN (parent)) &&
162       GST_OBJECT_FLAG_IS_SET (parent, GST_ELEMENT_FLAG_SINK)) {
163     if (GST_EVENT_TYPE (ev) == GST_EVENT_CUSTOM_DOWNSTREAM) {
164       const GstStructure *data = gst_event_get_structure (ev);
165
166       if (gst_structure_get_name_id (data) == latency_probe_id) {
167         GstPad *origin_pad;
168         guint64 origin_ts;
169         gchar *from, *to;
170
171         /* TODO(ensonic): we'd like to do this when actually rendering */
172         gst_structure_id_get (data,
173             latency_probe_pad, GST_TYPE_PAD, &origin_pad,
174             latency_probe_ts, G_TYPE_UINT64, &origin_ts, NULL);
175
176         from = g_strdup_printf ("%s_%s", GST_DEBUG_PAD_NAME (origin_pad));
177         to = g_strdup_printf ("%s_%s", GST_DEBUG_PAD_NAME (peer_pad));
178
179         /* TODO(ensonic): report format is still unstable */
180         log_trace (gst_structure_new ("latency",
181                 "from", G_TYPE_STRING, from,
182                 "to", G_TYPE_STRING, to,
183                 "time", G_TYPE_UINT64, GST_CLOCK_DIFF (origin_ts, ts), NULL));
184         g_free (from);
185         g_free (to);
186       }
187     }
188   }
189 }
190
191 static void
192 gst_latency_tracer_invoke (GstTracer * obj, GstTracerHookId hid,
193     GstTracerMessageId mid, va_list var_args)
194 {
195   GstLatencyTracer *self = GST_LATENCY_TRACER_CAST (obj);
196
197   switch (mid) {
198     case GST_TRACER_MESSAGE_ID_PAD_PUSH_PRE:
199     case GST_TRACER_MESSAGE_ID_PAD_PUSH_LIST_PRE:
200       do_push_buffer_pre (self, var_args);
201       break;
202     case GST_TRACER_MESSAGE_ID_PAD_PULL_RANGE_PRE:
203       do_pull_buffer_pre (self, var_args);
204       break;
205     case GST_TRACER_MESSAGE_ID_PAD_PUSH_EVENT_PRE:
206       do_push_event_pre (self, var_args);
207       break;
208     default:
209       break;
210   }
211 }