1. Gstreamer基本介紹
Gstreamer是linux上的多媒體框架。如下所示:
從上面這個圖中可以看到,底層是以plugin插件形式存在包括codec標準,parser,audio, protocol等,
也包括用戶自己開發的plugin和第三方開發的plugin。
core framework提供了plugin之間的交互機制和管理,通過將一些plugin連接起來形成一個系統,並且對上
提供訪問的接口。APP是構建在framework上的。
通過這個框架,底層開發者可以專注於開發plugin,APP開發者通過調用這個plugin來組成完成某種功能的
APP,plugin之間的通信都是由gstreamer framework提供的。
目前已經有一些成熟的plugin已經開發,並且作為library提供給了用戶:
gst-plugins-base: an essential exemplary set of elements
gst-plugins-good: a set of good-quality plug-ins under LGPL
gst-plugins-ugly: a set of good-quality plug-ins that might pose distribution problems
gst-plugins-bad: a set of plug-ins that need more quality
plugin中的element實際上就是實現該element支持的API,供上層來調用.
Gstreamer中的幾個術語:
Elements: plugin的實例,在一個APP中可能需要創建多個elements並且把這些elements連接在一起形成系統
elements可以分為:
source element: 沒有輸入,只有輸出pad,用來產生數據。
sink element: 只有輸入pad,沒有輸出pad,是數據的目的地。如disk,soundcard
filter element: 包含輸入pad和輸出pad,接收輸入的數據並且產生輸出數據
輸入輸出Pad的數目可以是N個(N >= 1)
queue element: 是一個特殊的element,作為thread的邊界存在。Gstreamer是可以
支持多線程的,線程的邊界通過queue來隔開。
element state:
element有4個狀態:
Pads:element的輸入輸出端口。elements之間就是通過Pad來進行連接的。數據通過Pads在elements之間
進行傳遞。
輸入pad稱為 sink pad
輸出pad稱為 source pad
element並不禁止自己的source pad和sink pad連接在一起形成一個loop。
pad的capability 定義了該pad上能夠處理的data的類型和一些參數(Gstcaps數據結構):
bin: 是一些elements的集合。對這個bin進行的操作會影響到該bin包含的所有的elements。
pipeline: pipeline也是一個bin,不過它是一個top level bin。
Bus: Bus是pipeline傳輸message給APP時的通路,從下面的圖中可以看到,從pipeline中發給APP的message
需要通過BUS(events/Queries 不需要通過BUS)。在創建pipeline時缺省會創建bus,因此用戶不需要
去單獨創建bus。APP需要做的就是為message設置message handler(APP提供callback函數給
pipeline調用),當pipeline需要發信息給APP時,調用這些APP提供的callback函數。
gst_bus_add_watch () or gst_bus_add_signal_watch ()
例子:
如果使用的是GLIB,那麼還可以有另外一種方式來聲明callback,見文檔。
Gstreamer定義了一些特殊的message包括error/EOS/State-change/element message,plugin也可以自
定義一些message。
通信 communication:
從框架來看,APP需要和pipeline進行數據和控制信息的通信包括進行play、pause等的控制以及數據的傳輸
pipeline中的elements之間也需要進行數據和信息的傳輸:
buffers: 在pad上傳輸的data是通過buffer傳輸的。 elements <-> elements
buffer的創建有2種方式,一種是由當前的element自己創建,然後把這個buffer傳遞給下一個element。
另外一種方式就是dwonstream-allocated buffers,就是由下一個element來創建要求大小的buffer,並提供buffer操作函數,當前element通過調用buffer操作函數將數據寫入這個buffer中完成buffer數據傳遞。
區別在於buffer的創建是在數據傳輸的源端element創建還是在數據接收端element來創建。
events: APP向elements發出的或者elements之間的傳輸都可以通過events。 APP -> elements, elements<->
messages: elements向APP傳輸的信息。 elements -> APP
queries: APP向elements請求信息,或者elements之間的信息請求。APP->Elements, elements<->
注意方向,APP和elements之間的傳輸是有方向的。
Gstreamer的數據驅動(schedule):
Gstreamer是一個多thread的框架。但是為了performance的原因,不會對每一個element都創建一個thread,
而是根據應用的特點和element的工作特點來進行thread的劃分,thread的邊界必須是queue element。
Gstreamer中pad支持兩種schedule方式:docs/design/part-activation.txt
push-based scheduling: 這種scheduling方法中,downstream elements的sink pad上需要定義chain函數
(gst_pad_set_chain_function ),upstream elements調用這個chain函數來完成
將buffer從upstream(source pad)到downstream elements(sink pad)的傳遞。
這種scheduling方式中source elements遞歸調用downstream elements的chain函
數,最後一直調用到目的elements的才能函數。
(由於chain函數是定義在sink pad上,而source element是沒有sink pad的,因此
source element是不提供chain函數的).
調用的順序是從sink element到source element。(遞歸調用).
sink-to-source elements order。
B_chain_function(C_chain_function(buffer2),buffer1)
在這種模式下,upstream elements通過調用downstream elements sink pad上定義的chain函數
主動的將數據傳輸給downstream elements,因此數據驅動是由upstream element發起的。
Pull-based scheduling:
在這種模式下,upstream elements 的source pad上提供了數據訪問函數,downstream elements通過
sink pad主動的去調用upstream elements的函數來要數據,
因此數據驅動是由downstream elements發起的(在sink pad上調用source pad 上的
gst_pad_pull_range())。
具體到某一個element上的PAD可以有下面幾種情況:
(1) 該element的所有PAD全部使用push-based mode
(2) 該element的所有pad都採用pull-based mode。
(3) 該element的sinkpad採用pull-based mode,而該element的sourcepad採用push-based
mode. 這種elements只能是queue element。在queue element的sink pad和source pad
上各有一個thread,每一個thread只能有一種數據驅動mode。(GstTask)
核心代碼:
push-based mode://source pad主動調用chain函數
#define GST_PAD_CHAINFUNC(pad) (GST_PAD_CAST(pad)->chainfunc)
GstFlowReturn gst_pad_push (GstPad * pad, GstBuffer * buffer)
{
g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
g_return_val_if_fail (GST_PAD_IS_SRC (pad), GST_FLOW_ERROR); //source pad調用chain函數
cache = pad_take_cache (pad, cache_ptr);
peer = cache->peer;//得到連接在這個sourcePad上的sink pad的list
ret = GST_PAD_CHAINFUNC (peer) (peer, buffer);//調用sink pad上的chain函數
}
//給sink Pad設置chain函數
void gst_pad_set_chain_function (GstPad * pad, GstPadChainFunction chain)
{
g_return_if_fail (GST_IS_PAD (pad));
g_return_if_fail (GST_PAD_IS_SINK (pad));
GST_PAD_CHAINFUNC (pad) = chain;
}
pull-based mode:sinkpad主動調用get_range()函數
GstFlowReturn gst_pad_pull_range (GstPad * pad, guint64 offset, guint size, GstBuffer ** buffer)
{
g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
g_return_val_if_fail (GST_PAD_IS_SINK (pad), GST_FLOW_ERROR);//由sinkpad來調用
if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL)) //通過該sinkpad找到和它連接的sourcepad
goto not_connected;
ret = gst_pad_get_range_unchecked (peer, offset, size, buffer);//調用定義在source pad上的
get_range函數
}
GstFlowReturn gst_pad_get_range (GstPad * pad, guint64 offset, guint size, GstBuffer ** buffer)
{
return gst_pad_get_range_unchecked (pad, offset, size, buffer);
}
#define GST_PAD_GETRANGEFUNC(pad) (GST_PAD_CAST(pad)->getrangefunc)
static GstFlowReturn gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
GstBuffer ** buffer)
{
if (G_UNLIKELY ((getrangefunc = GST_PAD_GETRANGEFUNC (pad)) == NULL))
goto no_function;
ret = getrangefunc (pad, offset, size, buffer);
}
那麼如何設置當前element的pad上採用哪一種scheduling mode,這就是pad-activation stage:
(1) 首先gstreamer需要去查詢當前pad支持幾種scheduling mode。
(2) Gstreamer來設置當前pad採用的scheduling mode方式,並通知當前pad知道。
PAD上需要實現notice函數供gstreamer來調用:
gst_pad_set_activatepull_function ()
gst_pad_set_activatepush_function ()
#define GST_PAD_ACTIVATEPUSHFUNC(pad) (GST_PAD_CAST(pad)->activatepushfunc)
void gst_pad_set_activatepush_function (GstPad * pad, GstPadActivateModeFuncti on activatepush)
{
g_return_if_fail (GST_IS_PAD (pad));
GST_PAD_ACTIVATEPUSHFUNC (pad) = activatepush; //函數指針
}
在activatepush()中調用下面的函數來設置mode。
gboolean gst_pad_activate_push/pull (GstPad * pad, gboolean active)
{
......
}
2 . 基於Gstreamer構建應用APP
第2部分:如何註冊一個plugin
一個plugin中可以包含多個element。每一個element作為plugin的一個feature。
gst_element_register (GstPlugin * plugin, const gchar * name, guint rank,GType type)
->gst_plugin_feature_set_name (GST_PLUGIN_FEATURE_CAST (factory), name);
首先從APP的角度來看,如何調用一個plugin(使用plugin feature name來調用如fakesink):
sink = gst_element_factory_make ("fakesink", "swallow_audio");
GstElement * gst_element_factory_make (const gchar * factoryname, const gchar * name)
{
factory = gst_element_factory_find (factoryname);//根據factorName找到
GstPluginFeature *feature;
element = gst_element_factory_create (factory, name);//通過factory得到plugin並
創建element(name)
}
GstElementFactory * gst_element_factory_find (const gchar * name)
{
feature = gst_registry_find_feature (gst_registry_get_default (), name,
GST_TYPE_ELEMENT_FACTORY);
}
GstElement * gst_element_factory_create (GstElementFactory * factory, const gchar * name)
{
//gst_plugin_feature_load調用plugin = gst_plugin_load_by_name (feature->plugin_name);
newfactory = GST_ELEMENT_FACTORY (gst_plugin_feature_load (GST_PLUGIN_FEATURE
(factory)));
factory = newfactory;
if (name) //創建的instance的name
element =
GST_ELEMENT_CAST (g_object_new (factory->type, "name", name, NULL));
else
element = GST_ELEMENT_CAST (g_object_newv (factory->type, 0, NULL));
}
gst_element_factory_create->gst_plugin_feature_load()
->plugin = gst_plugin_load_by_name (feature->plugin_name);
-> plugin = gst_registry_find_plugin (gst_registry_get_default (), name);
newplugin = gst_plugin_load_file (plugin->filename, &error);
-> gst_plugin_register_func (plugin, plugin->orig_desc, NULL)
-> (desc->plugin_init) (plugin)
總結下來就是:先通過factoryname找到該plugin的factory數據結構(GstPluginFeature factor->feature),再找到對應的plugin,並調用該plugin提供的plugin_init()函數。
(1) gst_init()
Initializes the GStreamer library, setting up internal path lists,
registering built-in elements, and loading standard plugins.
gst_init_check()
{
group = gst_init_get_option_group ();
}
在plugin編寫中:有2種註冊plugin的方式:
如下面的例子中的macro GST_PLUGIN_DEFINE:
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"avi",
"AVI stream handling",
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
* This macro needs to be used to define the entry point and meta data of a
* plugin. One would use this macro to export a plugin, so that it can be used
* by other applications.
*
* The macro uses a define named PACKAGE for the #GstPluginDesc,source field.
* When using autoconf, this is usually set automatically via the AC_INIT
* macro, and set in config.h. If you are not using autoconf, you will need to
* define PACKAGE yourself and set it to a short mnemonic string identifying
* your application/package, e.g. 'someapp' or 'my-plugins-foo.
*
* If defined, the GST_PACKAGE_RELEASE_DATETIME will also be used for the
* #GstPluginDesc,release_datetime field.
#define GST_PLUGIN_DEFINE(major,minor,name,description,init,version,license,package,origin) \
G_BEGIN_DECLS \
GST_PLUGIN_EXPORT GstPluginDesc gst_plugin_desc = { \
major, \
minor, \
name, \
(gchar *) description, \
init, \
version, \
license, \
PACKAGE, \
package, \
origin, \
__GST_PACKAGE_RELEASE_DATETIME, \
GST_PADDING_INIT \
}; \
G_END_DECLS
類似的有一個對應的靜態註冊函數:
#define GST_PLUGIN_DEFINE_STATIC(major,minor,name,description,init,version,license,package,origin) \
static void GST_GNUC_CONSTRUCTOR \
_gst_plugin_static_init__ ##init (void) \
{ \
static GstPluginDesc plugin_desc_ = { \
major, \
minor, \
name, \
(gchar *) description, \
init, \
version, \
license, \
PACKAGE, \
package, \
origin, \
NULL, \
GST_PADDING_INIT \
}; \
_gst_plugin_register_static (&plugin_desc_); \ //調用了靜態註冊函數
}
編譯自己的plugin插件:http://blog.csdn.net/dyzhu/article/details/4357037
Gstreamer是linux上的多媒體框架。如下所示:
plugin中的element實際上就是實現該element支持的API,供上層來調用.
Gstreamer中的幾個術語:
Pads:element的輸入輸出端口。elements之間就是通過Pad來進行連接的。數據通過Pads在elements之間
另外一種方式就是dwonstream-allocated buffers,就是由下一個element來創建要求大小的buffer,並提供buffer操作函數,當前element通過調用buffer操作函數將數據寫入這個buffer中完成buffer數據傳遞。
區別在於buffer的創建是在數據傳輸的源端element創建還是在數據接收端element來創建。
Gstreamer中pad支持兩種schedule方式:docs/design/part-activation.txt
核心代碼:
push-based mode://source pad主動調用chain函數
#define GST_PAD_CHAINFUNC(pad)
GstFlowReturn
{
}
//給sink Pad設置chain函數
void
{
}
pull-based mode:sinkpad主動調用get_range()函數
GstFlowReturn gst_pad_pull_range (GstPad * pad, guint64 offset, guint size, GstBuffer ** buffer)
{
}
GstFlowReturn gst_pad_get_range (GstPad * pad, guint64 offset, guint size, GstBuffer ** buffer)
{
}
#define GST_PAD_GETRANGEFUNC(pad)
static GstFlowReturn gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
{
}
那麼如何設置當前element的pad上採用哪一種scheduling mode,這就是pad-activation stage:
#define GST_PAD_ACTIVATEPUSHFUNC(pad)
{
}
2 . 基於Gstreamer構建應用APP
第2部分:如何註冊一個plugin
在plugin編寫中:有2種註冊plugin的方式:
* This macro needs to be used to define the entry point and meta data of a
G_BEGIN_DECLS \
GST_PLUGIN_EXPORT GstPluginDesc gst_plugin_desc = {
}; \
G_END_DECLS
static void GST_GNUC_CONSTRUCTOR
_gst_plugin_static_init__ ##init (void)
{
}
編譯自己的plugin插件:http://blog.csdn.net/dyzhu/article/details/4357037
1. 從模板生成gstreamer插件
gst-template是gstreamer插件的開發模板,在gst-plugin/tools目錄下有 一個make_element,在gst-plugin/src目錄下,運行../tools/make_element myfilter,就可以生成一個myfilter插件。
編譯:
#libtool --mode=compile cc `pkg-config --cflags gstreamer-0.10` -DPACKAGE="Gstreamer" -DHAVE_USER_MTU -Wall -Wimplicit -g -o gstmyfilter.o -c gstmyfilter.c
鏈接:
#libtool --mode=link cc -module -avoid-version -rpath /usr/local/lib/gstreamer-0.10/ -export-symbols-regex gst_plugin_desc -o gstmyfilter.la gstmyfilter.lo `pkg-config --libs gstreamer-0.10`
安裝:
#libtool --mode=install install gstmyfilter.la /usr/local/lib/gstreamer-0.10/
之後,就可以在自己的應用程序中創建myfilter的element。
轉載兩篇相關的文章:http://blog.csdn.net/dyzhu/article/details/4362865
http://blog.csdn.net/dyzhu/article/details/4362865
由於在嵌入式系統中運行gstreamer,受到資源的限制,所以打算只安裝gstreamer核心庫和一些必須的element,其它的element用到的時候再添加。我的想法是,把base,good,。。。插件包中的需要用到的elment編譯成插件。
struct _GstPluginDesc {
};
第3部分:GST-OMX
"
"
"
"
"
"
"
"
通過解析這個config可以得到library name(.so),component name等信息。config文件的路徑可以由環境變量OMX_CONFIG設置,沒有設置就使用default config(Gstomx.config)。
GST-OMX中的element做了一些抽象:
在例化時會調用type_instance_init,由於基類是base filter,因此會先調用基類的type_instance_init函數
//Gstomx_h264dec.c
static void
type_instance_init (GTypeInstance * instance, gpointer g_class)
{
}
//GstOmxBaseVideoDec 構造函數
static void
type_instance_init (GTypeInstance * instance, gpointer g_class)
{
}
基類basefilter的該函數:
static void
type_instance_init (GTypeInstance * instance, gpointer g_class)
{
}
void *
gstomx_core_new (void *object, GType type)
{
}
//對core進行初始化
gboolean
gstomx_get_component_info (void *core, GType type)
{
}
void
g_omx_core_init (GOmxCore * core)
{
}
通過這些函數的調用,完成了element的例化。其中的關鍵函數:
core->imp = request_imp (core->library_name);//這裡的library_name就是config文件中的library name
如何得到config文件?
在GST-OMX中plugin_init()->fetch_element_table()->get_config_path()中去查找config文件:
尋找的優先級如下:
{
}
config的內容:
上面的config中還可以設置一個component_role,在gst-omx.c的plugin_init()函數中有下面的代碼:
在很多的omx component中需要這個component_role的設置:
}
//初始化
static inline GOmxImp *
request_imp (const gchar * name)
{
}
static GOmxImp *
imp_new (const gchar * name)
{
struct GstOmxBaseVideoDec
{
};
//GstOmxBaseFilter 構造函數
static void
type_class_init (gpointer g_class, gpointer class_data)
{
}
在base filter中定義了這些property設置函數:
static void
set_property (GObject * obj,
{
}
數據結構中:
struct GstOmxBaseFilter
{
};
struct GOmxCore
{
};
struct GOmxPort
{
};
在stagefright中是libomxCore.SO,在GST-OMX中通過config文件中的library_name來指定使用的core的so文件
這是因為在omx_core.c中的一些API的實現是同實現相關的,因此需要開發這個core.so由開發者來實現,在
Gst-omx中可以通過config來定義使用的core.so文件:
比如stagefright中的TI的實現中:
從上面這個圖中可以看到,GSTREMAER的GST-OMX確實只是通過GOMXCORE(omxcore)來調用GetHandler得到component 的handle後來操作omx component。因此可以認為GST-OMX只是一個OMX IL Client而已。
GST-OMX和OMX IL 工作機制:
在pad_chain()函數中:完成OMX從loaded->idle的跳轉和port上資源的分配工作
static GstFlowReturn pad_chain (GstPad * pad, GstBuffer * buf)
{
}
//PORT相關的函數
static void
setup_ports (GstOmxBaseFilter * self)
{
}
void
g_omx_core_prepare (GOmxCore * core)
{
}
static void
port_allocate_buffers (GOmxPort * port)
{
}
然而,對於很多情況來說,上面port上的參數很多都是default值,比如buffer的大小和數目,在實際中這個
設置可能不是正確的,因此如果實際buffer的需求超過了現在使用default參數初始化的port上的設置,底層
會發出"portSettingChange"來通知gst-omx,要求gst-omx重新根據實際的需求來分配port上的buffer.
Gst-omx需要提供幾個callback函數:
在get_handler()是註冊給component來使用。其中EventHandler()中就是需要來處理component發給client(gst-omx)的event,其中就包括OMX_EventPortSettingsChanged
buffer數據驅動:
在OMX狀態機狀態從loaded->idle後,資源分配完成。下面開始進入
static GstFlowReturn pad_chain (GstPad * pad, GstBuffer * buf)
{
}
static void output_loop (gpointer data)
{
}
//調用fillthisbuffer和emptythisbuffer,開始傳遞buffer和數據
void g_omx_port_release_buffer (GOmxPort * port, OMX_BUFFERHEADERTYPE * omx_buffer)
{
}
沒有留言:
張貼留言