GStreamer 的调试工具
目標
有時一些事情沒有按照預期的運行,但從總線(bus)獲得的錯誤消息也沒有提供足夠的信息。幸運地是,GStreamer 帶有大量的調試信息,它們通常可以對哪里出了問題給出一些提示。這里將介紹:
- 如何從 GStreamer 獲得更多調試信息。
- 如何把自己的調試信息打印到 GStreamer 日志里。
- 如何獲得管線圖。
打印調試信息
調試日志
GStreamer 和它的插件充滿了調試追蹤,即,放置在有趣的特定代碼片段處,信息片段將被打印到終端,伴隨著時間戳,進程,種類,源碼文件,函數,和元素信息。
調試輸出由 GST_DEBUG 環境變量控制。這里有一個 GST_DEBUG=2 的例子:
0:00:00.868050000 1592 09F62420 WARN filesrc gstfilesrc.c:1044:gst_file_src_start:<filesrc0> error: No such file "non-existing-file.webm"如你所見,這是相當多的信息。事實上,GStreamer 調試日志是如此的詳細,當完全啟用時,它會使應用程序失去響應(由于控制臺滾動)或者填滿數兆字節的文本文件(當重定向到一個文件時)。因此,日志是分類的,我們很少需要一次啟用所有類別。
第一類是調試級別,它是一個指定了想要的輸出的數字:
| # | Name | Description | |---|---------|----------------------------------------------------------------| | 0 | none | No debug information is output. | | 1 | ERROR | Logs all fatal errors. These are errors that do not allow the | | | | core or elements to perform the requested action. The | | | | application can still recover if programmed to handle the | | | | conditions that triggered the error. | | 2 | WARNING | Logs all warnings. Typically these are non-fatal, but | | | | user-visible problems are expected to happen. | | 3 | FIXME | Logs all "fixme" messages. Those typically that a codepath that| | | | is known to be incomplete has been triggered. It may work in | | | | most cases, but may cause problems in specific instances. | | 4 | INFO | Logs all informational messages. These are typically used for | | | | events in the system that only happen once, or are important | | | | and rare enough to be logged at this level. | | 5 | DEBUG | Logs all debug messages. These are general debug messages for | | | | events that happen only a limited number of times during an | | | | object's lifetime; these include setup, teardown, change of | | | | parameters, etc. | | 6 | LOG | Logs all log messages. These are messages for events that | | | | happen repeatedly during an object's lifetime; these include | | | | streaming and steady-state conditions. This is used for log | | | | messages that happen on every buffer in an element for example.| | 7 | TRACE | Logs all trace messages. Those are message that happen very | | | | very often. This is for example is each time the reference | | | | count of a GstMiniObject, such as a GstBuffer or GstEvent, is | | | | modified. | | 9 | MEMDUMP | Logs all memory dump messages. This is the heaviest logging and| | | | may include dumping the content of blocks of memory. | +------------------------------------------------------------------------------+為了開啟調試輸出,可以把 GST_DEBUG 環境變量設置為需要的調試級別。所有設置的級別之下級別的日志也將被展示(比如,如果設置 GST_DEBUG=2,你將同時獲得 ERROR 和 WARNING 的消息)。
此外,每個插件或 GStreamer 的部分定義了它們自己的類別,因此,你可以給每一個單獨的類別指定一個調試等級。比如,GST_DEBUG=2,audiotestsrc:6,將為 audiotestsrc 元素使用調試等級 6,為其它部分使用調試等級 2。
然后,GST_DEBUG 環境變量,是一個逗號分隔的 category:level 對的列表,在開頭有一個可選的 level,表示所有類別的默認調試等級。
使用 '*' 通配符也是可以的。比如,GST_DEBUG=2,audio*:6 將為所有以 audio 開頭的類別使用調試等級 5。GST_DEBUG=*:2 等價于 GST_DEBUG=2。
使用 gst-launch-1.0 --gst-debug-help 來獲取所有已經注冊的類別。考慮到每個插件注冊它自己的類別,因此,當安裝或移除插件時,獲得的這個列表可能會變化。
當在 GStreamer 總線上拋出的錯誤信息無助于幫你縮小問題的范圍時使用 GST_DEBUG。將輸出日志重定向到一個文件,稍后檢查它,并從中搜索特定的消息是非常常見的一種實踐。
GStreamer 允許定制調試信息處理程序,但當使用默認的處理程序時,調試輸出中每一行的內容看起來像這樣:
0:00:00.868050000 1592 09F62420 WARN filesrc gstfilesrc.c:1044:gst_file_src_start:<filesrc0> error: No such file "non-existing-file.webm"這里是信息被格式化的方式:
| Example | Explained | |------------------|-----------------------------------------------------------| |0:00:00.868050000 | Time stamp in HH:MM:SS.sssssssss format since the start of| | | the program. | |1592 | Process ID from which the message was issued. Useful when | | | your problem involves multiple processes. | |09F62420 | Thread ID from which the message was issued. Useful when | | | your problem involves multiple threads. | |WARN | Debug level of the message. | |filesrc | Debug Category of the message. | |gstfilesrc.c:1044 | Source file and line in the GStreamer source code where | | | this message was issued. | |gst_file_src_start| Function that issued the message. | |<filesrc0> | Name of the object that issued the message. It can be an | | | element, a pad, or something else. Useful when you have | | | multiple elements of the same kind and need to distinguish| | | among them. Naming your elements with the name property | | | makes this debug output more readable but GStreamer | | | assigns each new element a unique name by default. | | error: No such | | | file .... | The actual message. | +------------------------------------------------------------------------------+添加你自己的調試信息
在你自己的與 GStreamer 交互的代碼部分,使用 GStreamer 的調試工具也很有趣。用這種方式,你將所有調試輸出都保存在同一個文件中,并且保留了不同消息之間的臨時關系。
要做到這一點,可以使用 GST_ERROR(),GST_WARNING(),GST_INFO(),GST_LOG() 和 GST_DEBUG() 宏。它們接受與 printf 相同的參數,且它們使用 default 類別(在輸出日志中 default 將被顯示為調試類別)。
要想修改類別為其它更有意義的東西,則在你的代碼的頂部添加如下兩行:
GST_DEBUG_CATEGORY_STATIC (my_category); #define GST_CAT_DEFAULT my_category在用 gst_init() 初始化 GStreamer 之后的一行添加如下的行:
GST_DEBUG_CATEGORY_INIT (my_category, "my category", 0, "This is my very own");這注冊一個新的類別(也就是說,在應用程序運行期間:它不存儲在任何文件中),并把它設置為你的代碼的默認類別。參考文檔來了解更多關于 GST_DEBUG_CATEGORY_INIT() 的內容。
獲取管線的圖
對于那種你的管線已經開始變得太大,且你已經丟失了對于哪個節點與另一個什么節點連接的追蹤的情況,GStreamer 具有輸出圖文件的能力。它們是 .dot 文件,它們可以通過自由程序如 GraphViz 來讀,它描述了你的管線的拓撲,以及每個連接上協商的能力。
當使用如 playbin 或 uridecodebin 這樣的在它們內部初始化多個元素的一體式元素的時候,這也非常方便。使用 .dot 文件學習它們已經在內部創建了什么樣的管線(并在此過程中學習一些 GStreamer 相關的東西)。
為了獲得 .dot 文件,簡單地設置 GST_DEBUG_DUMP_DOT_DIR 環境變量指向你想要文件被放置的目錄即可。gst-launch-1.0 將在每次狀態改變時創建一個 .dot 文件,因此你可以看到功能協商的進化。重置該變量以禁用這個功能。在你的應用程序內,你可以使用 GST_DEBUG_BIN_TO_DOT_FILE() 和 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS() 宏來在你方便的時候生成 .dot 文件。
這里有一個 playbin 生成的管線的種類的例子。它非常復雜,因為 playbin 可以處理非常多種情況:你手動搭建的管線通常不需要這么長。如果你手動創建的管線開始變得非常大,則可以考慮使用 playbin。
要下載全尺寸圖片,使用本頁頂部的附件鏈接(即回形針圖標)。
GST_DEBUG_DUMP_DOT_DIR 環境變量在 gstreamer/tools/gst-launch.c 源文件中有獲取,在 gstreamer/tools/gst-launch.c 源文件中可以看到如下這段代碼:
#ifdef G_OS_UNIX static gboolean hup_handler (gpointer user_data) {GstElement *pipeline = (GstElement *) user_data;if (g_getenv ("GST_DEBUG_DUMP_DOT_DIR") != NULL) {PRINT ("SIGHUP: dumping dot file snapshot ...\n");} else {PRINT ("SIGHUP: not dumping dot file snapshot, GST_DEBUG_DUMP_DOT_DIR ""environment variable not set.\n");}/* dump graph on hup */GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),GST_DEBUG_GRAPH_SHOW_ALL, "gst-launch.snapshot");return G_SOURCE_CONTINUE; } #endif可以看到,這個環境變量接口只有在類 Unix 系統中才工作。但這里也僅僅是獲取了一下,并檢查了獲取的值而已。這里我們沒有看到任何有關打開文件或者寫文件之類的操作。將媒體流處理的管線寫入文件的操作也是通過宏 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS() 完成的。
除了 gstreamer/tools/gst-launch.c 源文件外,在源文件 gstreamer/gst/gst.c 中也獲取了這個環境變量:
static gboolean init_pre (GOptionContext * context, GOptionGroup * group, gpointer data,GError ** error) {gchar *libdir;if (gst_initialized) {GST_DEBUG ("already initialized");return TRUE;}find_executable_path ();_priv_gst_start_time = gst_util_get_timestamp ();#ifndef GST_DISABLE_GST_DEBUG_priv_gst_debug_init ();priv_gst_dump_dot_dir = g_getenv ("GST_DEBUG_DUMP_DOT_DIR"); #endifinit_pre() 這個函數在 GStreamer 初始化時,將獲取的環境變量 GST_DEBUG_DUMP_DOT_DIR 的值,即要寫入管線圖文件的目錄保存在全局變量 priv_gst_dump_dot_dir 中。
在源文件 gstreamer/gst/gstdebugutils.c 的用于將管線的圖寫入文件的 gst_debug_bin_to_dot_file() 函數中,讀取全局變量 priv_gst_dump_dot_dir 的值,來獲得要保存文件的目錄,并將管線圖寫入文件:
void gst_debug_bin_to_dot_file (GstBin * bin, GstDebugGraphDetails details,const gchar * file_name) {gchar *full_file_name = NULL;FILE *out;g_return_if_fail (GST_IS_BIN (bin));if (G_LIKELY (priv_gst_dump_dot_dir == NULL))return;if (!file_name) {file_name = g_get_application_name ();if (!file_name)file_name = "unnamed";}full_file_name = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.dot",priv_gst_dump_dot_dir, file_name);if ((out = fopen (full_file_name, "wb"))) {gchar *buf;buf = gst_debug_bin_to_dot_data (bin, details);fputs (buf, out);g_free (buf);fclose (out);GST_INFO ("wrote bin graph to : '%s'", full_file_name);} else {GST_WARNING ("Failed to open file '%s' for writing: %s", full_file_name,g_strerror (errno));}g_free (full_file_name); }由此可見,環境變量 GST_DEBUG_DUMP_DOT_DIR 是 GStreamer 框架的配置接口,而不僅僅是 gst-launch 這個工具的配置接口。
GST_DEBUG_BIN_TO_DOT_FILE() 和 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS() 宏的原型如下:
#define GST_DEBUG_BIN_TO_DOT_FILE(bin, details, file_name) #define GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(bin, details, file_name)其中 bin 為應該分析的頂級的管線, details 為圖中顯示的詳細信息,如 GST_DEBUG_GRAPH_SHOW_ALL 或一個或多個 GstDebugGraphDetails 標記,file_name 為輸出的基名,也就是輸出文件的文件名的模式,而不是精確的文件名。
這兩個宏的定義為:
#define GST_DEBUG_BIN_TO_DOT_FILE(bin, details, file_name) gst_debug_bin_to_dot_file (bin, details, file_name) #define GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(bin, details, file_name) gst_debug_bin_to_dot_file_with_ts (bin, details, file_name)它們是對兩個 C 函數的調用。前面我們已經看到了函數 gst_debug_bin_to_dot_file() 的定義,這里再來看下 gst_debug_bin_to_dot_file_with_ts() 函數的定義:
void gst_debug_bin_to_dot_file_with_ts (GstBin * bin,GstDebugGraphDetails details, const gchar * file_name) {gchar *ts_file_name = NULL;GstClockTime elapsed;g_return_if_fail (GST_IS_BIN (bin));if (!file_name) {file_name = g_get_application_name ();if (!file_name)file_name = "unnamed";}/* add timestamp */elapsed = GST_CLOCK_DIFF (_priv_gst_start_time, gst_util_get_timestamp ());/* we don't use GST_TIME_FORMAT as such filenames would fail on some* filesystems like fat */ts_file_name =g_strdup_printf ("%u.%02u.%02u.%09u-%s", GST_TIME_ARGS (elapsed),file_name);gst_debug_bin_to_dot_file (bin, details, ts_file_name);g_free (ts_file_name); }gst_debug_bin_to_dot_file_with_ts() 函數是對函數 gst_debug_bin_to_dot_file() 的封裝。
由此我們看到,要讓 GStreamer 將管線圖寫入文件要完成的兩個步驟:
這兩個步驟缺一不可。只是對于 gst-launch 工具來說,第 2 步由工具執行。
如對于 GStreamer 的示例程序 helloworld,我們為它加上將管線圖寫入文件的代碼:
loop = g_main_loop_new (NULL, FALSE);bus = gst_element_get_bus (playbin);bus_watch_id = gst_bus_add_watch (bus, bus_call, loop);gst_object_unref (bus);GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (playbin),GST_DEBUG_GRAPH_SHOW_ALL, "gst-helloworld");/* start play back and listed to events */gst_element_set_state (playbin, GST_STATE_PLAYING);GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (playbin),GST_DEBUG_GRAPH_SHOW_ALL, "gst-helloworld");g_main_loop_run (loop);同時設置環境變量:
~/Data/opensource/gstreamer$ export GST_DEBUG_DUMP_DOT_DIR=/home/hanpfei/Data/opensource/gstreamer/當我們將 helloworld 重新編譯并運行起來時,
~/Data/opensource/gstreamer$ build/tests/examples/helloworld/helloworld ~/music.mp3將在 /home/hanpfei/Data/opensource/gstreamer/ 目錄下生成兩個 .dot 文件,類似于下面這樣:
0.00.00.205294209-gst-helloworld.dot 0.00.00.206333290-gst-helloworld.dot其中的 0.00.00.206333290-gst-helloworld.dot 文件的內容如下:
digraph pipeline {rankdir=LR;fontname="sans";fontsize="10";labelloc=t;nodesep=.1;ranksep=.2;label="<GstPlayBin>\nplaybin0\n[-] -> [>]\ncurrent-uri=\"file:///home/hanpfei/music.mp3\"\nsource=(GstFileSrc) source";node [style="filled,rounded", shape=box, fontsize="9", fontname="sans", margin="0.0,0.0"];edge [labelfontsize="6", fontsize="9", fontname="monospace"];legend [pos="0,0!",margin="0.05,0.05",style="filled",label="Legend\lElement-States: [~] void-pending, [0] null, [-] ready, [=] paused, [>] playing\lPad-Activation: [-] none, [>] push, [<] pull\lPad-Flags: [b]locked, [f]lushing, [b]locking, [E]OS; upper-case is set\lPad-Task: [T] has started task, [t] has paused task\l",];subgraph cluster_uridecodebin0_0x557be77e20d0 {fontname="Bitstream Vera Sans";fontsize="8";style="filled,rounded";color=black;label="GstURIDecodeBin\nuridecodebin0\n[-] -> [=]\nuri=\"file:///home/hanpfei/music.mp3\"\nsource=(GstFileSrc) source\ncaps=video/x-raw(ANY); audio/x-raw(ANY); text/x-raw(ANY); subpicture/x-dvd; subpictur…";fillcolor="#ffffff";subgraph cluster_decodebin0_0x557be77e80f0 {fontname="Bitstream Vera Sans";fontsize="8";style="filled,rounded";color=black;label="GstDecodeBin\ndecodebin0\n[-] -> [=]\ncaps=video/x-raw(ANY); audio/x-raw(ANY); text/x-raw(ANY); subpicture/x-dvd; subpictur…";subgraph cluster_decodebin0_0x557be77e80f0_sink {label="";style="invis";_proxypad0_0x557be77e2360 [color=black, fillcolor="#ddddff", label="proxypad0\n[<][bfb]", height="0.2", style="filled,solid"];decodebin0_0x557be77e80f0_sink_0x557be77ee060 -> _proxypad0_0x557be77e2360 [style=dashed, minlen=0]decodebin0_0x557be77e80f0_sink_0x557be77ee060 [color=black, fillcolor="#ddddff", label="sink\n[<][bfb]", height="0.2", style="filled,solid"];}fillcolor="#ffffff";subgraph cluster_typefind_0x557be77ea010 {fontname="Bitstream Vera Sans";fontsize="8";style="filled,rounded";color=black;label="GstTypeFindElement\ntypefind\n[=]\ncaps=application/x-id3";subgraph cluster_typefind_0x557be77ea010_sink {label="";style="invis";typefind_0x557be77ea010_sink_0x557be77e6440 [color=black, fillcolor="#aaaaff", label="sink\n[<][bfb][T]", height="0.2", style="filled,solid"];}subgraph cluster_typefind_0x557be77ea010_src {label="";style="invis";typefind_0x557be77ea010_src_0x557be77e6690 [color=black, fillcolor="#ffaaaa", label="src\n[>][bfb]", height="0.2", style="filled,solid"];}typefind_0x557be77ea010_sink_0x557be77e6440 -> typefind_0x557be77ea010_src_0x557be77e6690 [style="invis"];fillcolor="#aaffaa";}_proxypad0_0x557be77e2360 -> typefind_0x557be77ea010_sink_0x557be77e6440 [label="ANY"]}subgraph cluster_source_0x557be77e4220 {fontname="Bitstream Vera Sans";fontsize="8";style="filled,rounded";color=black;label="GstFileSrc\nsource\n[=]\nlocation=\"/home/hanpfei/music.mp3\"";subgraph cluster_source_0x557be77e4220_src {label="";style="invis";source_0x557be77e4220_src_0x557be77e61f0 [color=black, fillcolor="#ffaaaa", label="src\n[<][bfb]", height="0.2", style="filled,solid"];}fillcolor="#ffaaaa";}source_0x557be77e4220_src_0x557be77e61f0 -> decodebin0_0x557be77e80f0_sink_0x557be77ee060 [label="ANY"]}subgraph cluster_playsink_0x557be6b00290 {fontname="Bitstream Vera Sans";fontsize="8";style="filled,rounded";color=black;label="GstPlaySink\nplaysink\n[-] -> [=]\nflags=video+audio+text+soft-volume+deinterlace+soft-colorbalance\nsend-event-mode=first";fillcolor="#ffffff";subgraph cluster_streamsynchronizer0_0x557be6b02020 {fontname="Bitstream Vera Sans";fontsize="8";style="filled,rounded";color=black;label="GstStreamSynchronizer\nstreamsynchronizer0\n[=]";fillcolor="#ffffff";}}}.dot 文件是圖的一種文本形式的描述。
要看到圖,還需要通過 GraphViz 將 .dot 文件轉為 png 等格式。
安裝 graphviz:
~/Data/opensource/gstreamer$ sudo apt-get install graphviz通過 graphviz 包中的 dot 工具將 .dot 文件轉為 PNG 圖:
~/Data/opensource/gstreamer$ dot -Tpng -o test1.png 0.00.00.205294209-gst-helloworld.dot ~/Data/opensource/gstreamer$ dot -Tpng -o test2.png 0.00.00.206333290-gst-helloworld.dot生成的圖如下:
GStreamer 的媒體處理管線在不同時刻其狀態不太一樣,使得獲得的圖也不太一樣。
結論
這里展示了:
- 如何使用 GST_DEBUG 環境變量從 GStreamer 獲取更多調試信息
- 如何通過 GST_ERROR() 及其相關的宏將你自己的調試信息打印到 GStreamer 日志中。
- 如何通過 GST_DEBUG_DUMP_DOT_DIR 環境變量獲得管線圖。
參考文檔:
Basic tutorial 11: Debugging tools
Gstreamer 管道可視化
總結
以上是生活随笔為你收集整理的GStreamer 的调试工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GStreamer 入门 - Hello
- 下一篇: strace 哇,好多系统调用