GLib/Gstreamer代码浅析

GLib

概述

  • 实现跨平台
  • 避免重复造轮子

GLib的上下层结构:

GLib的组成部分:

 GLib代码组成:

 GModule代码组成:

GObject代码组成:

 GIO代码组成:530个文件,太多不列了!

如何使用glib

安装glib(ubuntu系统)

直接通过命令安装:

sudo apt install -y libglib2.0-dev

下载源码包自己编译安装:

git clone https://gitlab.gnome.org/GNOME/glib.git
cd glib
mkdir out
meson out
cd out
ninja
sudo ninja install

使用glib

包含相关的头文件,即可开展编程

#include <glib.h>                       //GLib
#include <gmodule.h>                    //GModlule
#include <glib-object.h>                //GObject
#include <gio/gdesktopappinfo.h>        //GIO
#include <gio/gfiledescriptorbased.h>
#include <gio/gio.h>
#include <gio/gunixfdlist.h>
#include <gio/gunixfdmessage.h>
#include <gio/gunixinputstream.h>
#include <gio/gunixmounts.h>
#include <gio/gunixoutputstream.h>

编译

--------------------------------------简易版--------------------------------------------------
gcc glib_test.c -o glib_test `pkg-config --libs --cflags glib-2.0`

fuqiang@fuqiang-virtual:~/workspace$ pkg-config --libs --cflags glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -lglib-2.0


--------------------------------------完整版--------------------------------------------------
gcc glib_test.c -o glib_test `pkg-config --libs --cflags glib-2.0 gmodule-2.0 gobject-2.0 gio-2.0`

fuqiang@fuqiang-virtual:~/workspace/test_gstreamer$ pkg-config --libs --cflags glib-2.0 gmodule-2.0 gobject-2.0 gio-2.0
-pthread -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -Wl,--export-dynamic -lgmodule-2.0 -pthread -lgio-2.0 -lgobject-2.0 -lglib-2.0

优势

  • 跨平台的动态库加载函数(GModule)
  • 事件循环分发处理机制GMainLoop (GLib)
  • 跨平台的线程(线程池)函数,信号量函数,互斥锁函数(GLib)
  • 提供了异步队列、线性表、动态数组、单/双向链表、哈希表、多叉树、平衡二叉树、字符串等常用容器(GLib)
  • 词法分析,xml语言解析、ini文件读写等功能(GLib)
  • 命令行参数解析(GLib)
  • 一套完整的log机制(GLib)
  • slice内存分配结构(GLib)
  • 简单的正则表达式功能(GLib)
  • GObject面向对象封装设计(GObject)
  • IO函数(GIO)

特性说明(docs.gtk.org/glib/index.html)

GModule(Dynamically loaded modules)

Gstreamer中的动态加载插件就是通过这个实现

#if     (G_MODULE_IMPL == G_MODULE_IMPL_DL)
#include "gmodule-dl.c"
#elif   (G_MODULE_IMPL == G_MODULE_IMPL_WIN32)
#include "gmodule-win32.c"
#elif   (G_MODULE_IMPL == G_MODULE_IMPL_AR)
#include "gmodule-ar.c"

GModule结构体定义:

struct _GModule
{
  gchar *file_name;
  gpointer handle;
  guint ref_count : 31;         //引用计数,多次open
  guint is_resident : 1;        //需要常驻,不能unload
  GModuleUnload unload;         //回调函数
  GModule *next;                //加载的modules链表
};

一些代码:

GModule*
g_module_open_full (const gchar   *file_name,
                    GModuleFlags   flags,
                    GError       **error)
{
...
  module = g_module_find_by_name (file_name);
  if (module)
  {
    module->ref_count++;

  }
...
  /* we don't call unload() if the initialization check failed. */
  if (!check_failed)
    g_module_symbol (module, "g_module_unload", (gpointer) &module->unload);
...
}


gboolean
g_module_close (GModule *module)
{
...
  module->ref_count--;
  if (!module->ref_count && !module->is_resident && module->unload)
  {
    GModuleUnload unload;

    unload = module->unload;
    module->unload = NULL;
    unload (module);
  }
...
}

官方示例:

// the function signature for 'say_hello'
typedef void (* SayHelloFunc) (const char *message);

gboolean
just_say_hello (const char *filename, GError **error)
{
  SayHelloFunc say_hello;
  GModule *module;

  module = g_module_open (filename, G_MODULE_BIND_LAZY);        //dlopen
  if (module == NULL)
    {
      g_set_error (error, FOO_ERROR, FOO_ERROR_BLAH,
                   "%s", g_module_error ());
      return FALSE;
    }

  if (!g_module_symbol (module, "say_hello", (gpointer *)&say_hello))        //dlsym
    {
      g_set_error (error, SAY_ERROR, SAY_ERROR_OPEN,
                   "%s: %s", filename, g_module_error ());

      if (!g_module_close (module))
        g_warning ("%s: %s", filename, g_module_error ());

      return FALSE;
    }

  if (say_hello == NULL)
    {
      g_set_error (error, SAY_ERROR, SAY_ERROR_OPEN,
                   "symbol say_hello is NULL");

      if (!g_module_close (module))
        g_warning ("%s: %s", filename, g_module_error ());

      return FALSE;
    }

  // call our function in the module
  say_hello ("Hello world!");

  if (!g_module_close (module))        //dlclose
    g_warning ("%s: %s", filename, g_module_error ());

  return TRUE;
 }

GMainLoop(gmain.h/gmain.c)

  • 基于IO多路复用(select/poll/epoll)
  • 事件动态添加
  • 优先级管理
#define G_PRIORITY_HIGH            -100
#define G_PRIORITY_DEFAULT          0
#define G_PRIORITY_HIGH_IDLE        100
#define G_PRIORITY_DEFAULT_IDLE     200
#define G_PRIORITY_LOW              300        //background

结构体:

struct _GMainLoop
{
  GMainContext *context;
  gboolean is_running; /* (atomic) */
  gint ref_count;  /* (atomic) */
};
...
struct _GSource
{
  /*< private >*/
  gpointer callback_data;
  GSourceCallbackFuncs *callback_funcs;

  const GSourceFuncs *source_funcs;
  guint ref_count;

  GMainContext *context;

  gint priority;        //优先级管理
  guint flags;
  guint source_id;

  GSList *poll_fds;

  GSource *prev;        //检索,添加,删除
  GSource *next;

  char    *name;

  GSourcePrivate *priv;
};

常规事件处理

#include<glib.h> 
GMainLoop* loop;

gint counter = 10;
gboolean callback(gpointer arg)
{
    if(--counter == 0){
        g_main_loop_quit(loop);
        return FALSE;
    }
    return TRUE;
}
 
int main(int argc, char* argv[])
{
    if(g_thread_supported() == 0)
        g_thread_init(NULL);
    loop = g_main_loop_new(NULL, FALSE);
    g_timeout_add(100, callback, NULL);  //增加一个定时器,100毫秒运行一次callback
    g_main_loop_run(loop);
    g_main_loop_unref(loop);
    return 0;
}

unref:

g_main_loop_unref (GMainLoop *loop)
{
  g_return_if_fail (loop != NULL);
  g_return_if_fail (g_atomic_int_get (&loop->ref_count) > 0);

  if (!g_atomic_int_dec_and_test (&loop->ref_count))
    return;

  g_main_context_unref (loop->context);
  g_free (loop);
}

自定义事件处理

#include <glib.h>
#include <stdio.h>
#include <strings.h>
GMainLoop* loop;
//当stdin有数据可读时被GSource调用的回调函数
gboolean callback(GIOChannel *channel)
{
    gchar* str;
    gsize len;
    //从stdin读取一行字符串
    g_io_channel_read_line(channel, &str, &len, NULL, NULL);
    //去掉回车键()
    while(len > 0 && (str[len-1] == '/r' || str[len-1] == '/n'))
        str[--len]='/0';
    //反转字符串
    for(;len;len--)
        g_print("%c",str[len-1]);
    g_print("/n");
    //判断结束符
    if(strcasecmp(str, "q") == 0){
        g_main_loop_quit(loop);
    }
    g_free(str);
}
void add_source(GMainContext *context)
{
    GIOChannel* channel;
    GSource* source;
    //这里我们监视stdin是否可读, stdin的fd默认等于1
    channel = g_io_channel_unix_new(1);
    //g_io_create_watch创建一个默认的io监视作用的GSource,参数G_IO_IN表示监视stdin的读取状态
    source = g_io_create_watch(channel, G_IO_IN);
    g_io_channel_unref(channel);
    //设置stdin可读的时候调用的回调函数
    g_source_set_callback(source, (GSourceFunc)callback, channel, NULL);
    //把GSource附加到GMainContext
    g_source_attach(source, context);
    g_source_unref(source);
}
int main(int argc, char* argv[])
{
    GMainContext *context;
    if(g_thread_supported() == 0)
        g_thread_init(NULL);
    context = g_main_context_new();
    add_source(context);
    //把Context赋给GMainLoop
    loop = g_main_loop_new(context, FALSE);
    g_print("input string('q' to quit)/n");
    g_main_loop_run(loop);
    g_main_loop_unref(loop);
    g_main_context_unref(context);
    return 0;
}

Thread(gthread.h/gthread.c)

typedef struct _GThread         GThread;

typedef struct _GMutex          GMutex;
typedef struct _GRecMutex       GRecMutex;
typedef struct _GRWLock         GRWLock;
typedef struct _GCond           GCond;

官方示例:

#include <glib.h>

void thread_call(gpointer data)
{
    gint i = GPOINTER_TO_INT(data);
    g_print("%d\n", i);
    g_print("Thread %d was created\n", i);
}

int main()
{
    gint i;
    GThread* thread = NULL;
    GError*   error = NULL;

if (!g_thread_supported())
{
    g_thread_init(NULL);
}

for (i = 1; i < 10; i++)
{
    thread = g_thread_create((GThreadFunc)thread_call,(gpointer)i, FALSE, &error);
    g_usleep(500);
    if (thread == NULL)
    {
        g_critical("Create thread error: %s\n",error->message);
        g_error_free(error);
    }
}

return 0;
}

ThreadPool(gthreadpool.h/gthreadpool.c)

频繁创建和销毁执行过程相似而数据不同的线程,系统的效率和资源的利用率将会受到很大的影响—>threadpool

#include <glib.h>
#include <glib/gprintf.h>
#include <unistd.h>
#include <sys/syscall.h>

//线程执行函数,进入后打印数据,并睡眠5秒
void thread_execute (gpointer data, gpointer user_data)
{
	g_printf("thread_execute %ld in\n", syscall(__NR_gettid));
	g_printf("thread_execute %ld data is : %d\n", syscall(__NR_gettid), *((gint *)data));
	g_usleep (5000000);
	g_printf("thread_execute %ld out\n", syscall(__NR_gettid));
}
 
gint data[10];
int main(int argc, char **argv)
{
	g_printf ("main in\n");
 
	gint count;
	GError *gerr = NULL;
	GThreadPool *gpool = NULL;
	//最大同时执行2个线程,由于exclusive设置为FALSE,所以不会有错误发生,error被设置为NULL
	gpool = g_thread_pool_new (thread_execute, NULL, 2, FALSE, NULL);

	//线程池中最大允许线程数3个
	if(!g_thread_pool_set_max_threads (gpool, 3, &gerr))
		g_printf("g_thread_pool_set_max_threads is error: %s\n", gerr->message);
 	
	/** 
	* 实际线程池中最多有3个线程同时运行 
	*/  
	for (count = 0; count < (sizeof(data)/sizeof(gint)); count++)
	{
		data[count] = count;
		g_thread_pool_push(gpool, (gpointer)(&(data[count])), &gerr);
		if(gerr != NULL)
		{
			g_printf("g_thread_pool_push is error: %s\n", gerr->message);
		}
	}
 
	g_usleep (100000);
	
	//插入优先级高的新任务
	if(g_thread_pool_move_to_front (gpool, (gpointer)(&(data[9]))))
		g_printf("g_thread_pool_move_to_front is 9\n");
	
	//立即释放,不继续执行任务队列
	//g_thread_pool_free (gpool, TRUE, TRUE);
	//等待任务队列中的任务全部执行完成
	g_thread_pool_free (gpool, FALSE, TRUE);

	g_printf ("main out\n");
        return 0;
}

AsyncQueue(gasyncqueue.h/gasyncqueue.c)

#include <glib.h>

static gpointer test_aqueue_push_func(gpointer data)
{
    GAsyncQueue *aqueue = NULL;

    g_print("%s \n", __FUNCTION__);
    g_print("delay 2 seconds to push data \n");
    g_usleep(2*1000*1000);

    aqueue = (GAsyncQueue *)data;
    g_async_queue_push(aqueue, GINT_TO_POINTER(99));

    return NULL;
}

static gpointer test_aqueue_pop_func(gpointer data)
{
    gpointer val = NULL;
    GAsyncQueue *aqueue = NULL;
    g_print("%s \n", __FUNCTION__);

    aqueue = (GAsyncQueue *)data;
    val = g_async_queue_pop(aqueue);
    g_print("val: %d \n", GPOINTER_TO_INT(val));

    return NULL;
}

gint main(gint argc, gchar **argv)
{
    GThread *thd_push, *thd_pop;
    GAsyncQueue *aqueue = NULL;

    aqueue = g_async_queue_new();

    thd_push = g_thread_new("thd_push", test_aqueue_push_func, aqueue);
    thd_pop  = g_thread_new("thd_pop", test_aqueue_pop_func, aqueue);

    g_thread_join(thd_push);
    g_thread_join(thd_pop);

    g_async_queue_unref(aqueue);

    return 0;
}

线性表

  • 顺序表GArray(garray.h/garray.c)
  • 单链表GSList(gslist.h/gslist.c)
  • 双链表GList(glist.h/glist.c)

词法分析

  • XML语言解析GMarkupParser(gmarkup.h/gmarkup.c)
  • ini文件解析GKeyFile(gkeyfile.h/gkeyfile.c)

命令行参数解析GOption(goption.h/goption.c)

#include <glib.h>
#include <locale.h>
 
static  gint repeats = 2;
static  gint max_size = 8;
static  gboolean verbose = FALSE;
static  gboolean beep = FALSE;
 
static  GOptionEntry entries[] =
{
//   longname shortname flag     arg           arg_data     description             arg_description
    {"repeats" ,   'r' , 0, G_OPTION_ARG_INT,  &repeats,  "Average over N repetitions" ,  "N" },
    {"max-size" ,  'm' , 0, G_OPTION_ARG_INT,  &max_size, "Test up to 2^M items" ,        "M" },
    {"verbose" ,   'v' , 0, G_OPTION_ARG_NONE, &verbose,  "Be verbose" ,                   NULL},
    {"beep" ,      'b' , 0, G_OPTION_ARG_NONE, &beep,     "Beep when done" ,               NULL},
    {NULL}
};
 
 
int main (int  argc,  char  *argv[])
{
    GError *error = NULL;
    GOptionContext *context = NULL;
    GOptionGroup *group = NULL;
 
    // 创建一个新的选项上下文
    context = g_option_context_new("- test tree model performance" );
    g_option_context_add_main_entries(context, entries, NULL);
 
    //添加要在选项列表之前的--help输出中显示的字符串。 这通常是程序功能的摘要
    g_option_context_set_summary(context, "This is a glib demo" );
 
    // 解析命令行参数,识别已添加到上下文的选项
    if  (!g_option_context_parse(context, &argc, &argv, &error))
    {
        exit (1);
    }
 
    // 释放被解析的参数
    g_option_context_free(context);
 
    g_print("Now value is: repeats=%d, max_size=%d, verbose=%d, beep=%d\n",
            repeats, max_size, verbose, beep);
 
    return  0;
}

执行情况:

fuqiang@fuqiang-virtual:~/workspace/test_gstreamer$ ./glib_test_option -h
Usage:
  glib_test_option [OPTION?] - test tree model performance

This is a glib demo

Help Options:
  -h, --help           Show help options

Application Options:
  -r, --repeats=N      Average over N repetitions
  -m, --max-size=M     Test up to 2^M items
  -v, --verbose        Be verbose
  -b, --beep           Beep when done

fuqiang@fuqiang-virtual:~/workspace/test_gstreamer$ ./glib_test_option
Now value is: repeats=2, max_size=8, verbose=0, beep=0
fuqiang@fuqiang-virtual:~/workspace/test_gstreamer$ ./glib_test_option -r 1000
Now value is: repeats=1000, max_size=8, verbose=0, beep=0

Log机制(gmessages.h/gmessages.c)

跨平台,统一linux和win32:

#ifdef G_OS_UNIX
#include <unistd.h>
#endif

#ifdef G_OS_WIN32
#include <process.h>            /* For getpid() */
#include <io.h>
#include <windows.h>
#endif

log等级划分:

#define g_error(...)  G_STMT_START {                                            \
                        g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__); \
                        for (;;) ;                                              \
                      } G_STMT_END
#define g_message(...)  g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__)
#define g_critical(...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__)
#define g_warning(...)  g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__)
#define g_info(...)     g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__)
#define g_debug(...)    g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__)

使用:

g_error ("%s: failed to allocate memory", G_STRLOC);

内存分配

  • gmem.h/gmem.c
  • gslice.h/gslice.c
  • 推荐使用gslice

gslice:

  • 分层设计
  • 用于优化许多大小相等的块的分配,线程内存空闲列表,快速满足已知结构的内存分配
  • 对齐约束而未使用的内存用于缓存着色(块地址的随机分布),以提高CPU缓存利用率
gchar *mem[10000];
gint i;

// Allocate 10000 blocks.
for (i = 0; i < 10000; i++)
{
    mem[i] = g_slice_alloc (50);

    // Fill in the memory with some junk.
    for (j = 0; j < 50; j++)
      mem[i][j] = i * j;
}

// Now free all of the blocks.
for (i = 0; i < 10000; i++)
    g_slice_free1 (50, mem[i]);

正则表达式GRegex(gregex.h/gregex.c)

#include <glib.h>

int main (int argc, char** argv)
{
    GRegex* regex;
    GMatchInfo *match_info;
    GError *error = NULL;
    const gchar *str = "11aa222bb33333cccc44444dddddddd";
    const gchar *pat = "[0-9]+";

    regex = g_regex_new(pat, 0, 0, &error);
    g_regex_match(regex, str, 0, &match_info);

    while (g_match_info_matches(match_info)) {
        gchar* word = g_match_info_fetch(match_info, 0);
        g_print("%s \n",word);
        g_free(word);

        g_match_info_next(match_info, NULL);
    }

    g_match_info_free(match_info);
    g_regex_unref(regex);

  return 0;
}

GObject(面向对象实现)

封装&继承:

 

G_DEFINE_TYPE:

G_DEFINE_TYPE
	G_DEFINE_TYPE_EXTENDED
		_G_DEFINE_TYPE_EXTENDED_BEGIN
			_G_DEFINE_TYPE_EXTENDED_BEGIN_PRE
			_G_DEFINE_TYPE_EXTENDED_BEGIN_REGISTER
		_G_DEFINE_TYPE_EXTENDED_END


// G_DEFINE_TYPE
#define G_DEFINE_TYPE(TN, t_n, T_P)			    G_DEFINE_TYPE_EXTENDED (TN, t_n, T_P, 0, {})
#define G_DEFINE_TYPE_EXTENDED(TN, t_n, T_P, _f_, _C_)	 _G_DEFINE_TYPE_EXTENDED_BEGIN (TN, t_n, T_P, _f_) {_C_;} _G_DEFINE_TYPE_EXTENDED_END()
//_G_DEFINE_TYPE_EXTENDED_BEGIN
#define _G_DEFINE_TYPE_EXTENDED_BEGIN(TypeName, type_name, TYPE_PARENT, flags) \
  _G_DEFINE_TYPE_EXTENDED_BEGIN_PRE(TypeName, type_name, TYPE_PARENT) \
  _G_DEFINE_TYPE_EXTENDED_BEGIN_REGISTER(TypeName, type_name, TYPE_PARENT, flags) \

//_G_DEFINE_TYPE_EXTENDED_END
#define _G_DEFINE_TYPE_EXTENDED_END()	\
        /* following custom code */	\
      }					\
      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \
    }					\
  return g_define_type_id__volatile;	\
} /* closes type_name##_get_type() */

// _G_DEFINE_TYPE_EXTENDED_BEGIN_PRE
#define _G_DEFINE_TYPE_EXTENDED_BEGIN_PRE(TypeName, type_name, TYPE_PARENT) \
\
static void     type_name##_init              (TypeName        *self); \
static void     type_name##_class_init        (TypeName##Class *klass); \
static gpointer type_name##_parent_class = NULL; \
static gint     TypeName##_private_offset; \
\
_G_DEFINE_TYPE_EXTENDED_CLASS_INIT(TypeName, type_name) \
\
G_GNUC_UNUSED \
static inline gpointer \
type_name##_get_instance_private (TypeName *self) \
{ \
  return (G_STRUCT_MEMBER_P (self, TypeName##_private_offset)); \
} \
\
GType \
type_name##_get_type (void) \
{ \
  static volatile gsize g_define_type_id__volatile = 0;
  /* Prelude goes here */

// _G_DEFINE_TYPE_EXTENDED_BEGIN_REGISTER
#define _G_DEFINE_TYPE_EXTENDED_BEGIN_REGISTER(TypeName, type_name, TYPE_PARENT, flags) \
  if (g_once_init_enter (&g_define_type_id__volatile))  \
    { \
      GType g_define_type_id = \
        g_type_register_static_simple (TYPE_PARENT, \
                                       g_intern_static_string (#TypeName), \
                                       sizeof (TypeName##Class), \
                                       (GClassInitFunc)(void (*)(void)) type_name##_class_intern_init, \
                                       sizeof (TypeName), \
                                       (GInstanceInitFunc)(void (*)(void)) type_name##_init, \
                                       (GTypeFlags) flags); \
      { /* custom code follows */

将下面代入:

  • TypeName:MyObject
  • type_name:my_object
  • TYPE_PARENT:G_TYPE_OBJECT
static void     my_object_init              (MyObject        *self);
static void     my_object_class_init        (MyObjectClass *klass);

static gpointer my_object_parent_class = NULL;
static gint     MyObject_private_offset;

static void     my_object_class_intern_init (gpointer klass)
{
  my_object_parent_class = g_type_class_peek_parent (klass);  //获取父类
  if (MyObject_private_offset != 0)
    g_type_class_adjust_private_offset (klass, &MyObject_private_offset);
  my_object_class_init ((MyObjectClass*) klass);              //子类初始化
}

G_GNUC_UNUSED
static inline gpointer
my_object_get_instance_private (TypeName *self)
{
  return (G_STRUCT_MEMBER_P (self, MyObject_private_offset));
}

GType
my_object_get_type (void)
{
  static volatile gsize g_define_type_id__volatile = 0;

  if (g_once_init_enter (&g_define_type_id__volatile))
  {
    GType g_define_type_id = g_type_register_static_simple (G_TYPE_OBJECT,
                                       g_intern_static_string ("MyObject"),
                                       sizeof (MyObjectClass),
                                       (GClassInitFunc)(void (*)(void)) my_object_class_intern_init,
                                       sizeof (TypeName),
                                       (GInstanceInitFunc)(void (*)(void)) my_object_init,
                                       (GTypeFlags) flags);

    g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
  }
  return g_define_type_id__volatile;
}

TypeNode:

/* --- structures --- */
struct _TypeNode
{
  guint        ref_count;  /* (atomic) */

  GTypePlugin *plugin;
  guint        n_children; /* writable with lock */
  guint        n_supers : 8;
  guint        n_prerequisites : 9;
  guint        is_classed : 1;
  guint        is_instantiatable : 1;
  guint        mutatable_check_cache : 1;	/* combines some common path checks */
  GType       *children; /* writable with lock */
  TypeData    *data;
  GQuark       qname;
  GData       *global_gdata;
  union {
    GAtomicArray iface_entries;		/* for !iface types */
    GAtomicArray offsets;
  } _prot;
  GType       *prerequisites;
  GType        supers[1]; /* flexible array */
};

gpointer
g_type_class_peek_parent (gpointer g_class)
{
  TypeNode *node;
  gpointer class = NULL;
  
  g_return_val_if_fail (g_class != NULL, NULL);
  
  node = lookup_type_node_I (G_TYPE_FROM_CLASS (g_class));

  g_return_val_if_fail (node != NULL, NULL);

  /* We used to acquire a read lock here. That is not necessary, since 
   * parent->data->class.class is constant as long as the derived class
   * exists. 
   */
  if (node->is_classed && node->data && NODE_PARENT_TYPE (node))
    {
      node = lookup_type_node_I (NODE_PARENT_TYPE (node)); //#define NODE_PARENT_TYPE(node) (node->supers[1])
      class = node->data->class.class;
    }
  else if (NODE_PARENT_TYPE (node))
    g_warning (G_STRLOC ": invalid class pointer '%p'", g_class);
  
  return class;
}

register:

GType
g_type_register_static (GType            parent_type,
			const gchar     *type_name,
			const GTypeInfo *info,
			GTypeFlags	 flags)
{
  TypeNode *pnode, *node;
  GType type = 0;
  
  g_assert_type_system_initialized ();
  g_return_val_if_fail (parent_type > 0, 0);
  g_return_val_if_fail (type_name != NULL, 0);
  g_return_val_if_fail (info != NULL, 0);
  
  if (!check_type_name_I (type_name) ||
      !check_derivation_I (parent_type, type_name))
    return 0;
  if (info->class_finalize)
    {
      g_warning ("class finalizer specified for static type '%s'",
		 type_name);
      return 0;
    }
  
  pnode = lookup_type_node_I (parent_type);
  G_WRITE_LOCK (&type_rw_lock);
  type_data_ref_Wm (pnode);
  if (check_type_info_I (pnode, NODE_FUNDAMENTAL_TYPE (pnode), type_name, info))
    {
      node = type_node_new_W (pnode, type_name, NULL);
      type_add_flags_W (node, flags);
      type = NODE_TYPE (node);
      type_data_make_W (node, info,
			check_value_table_I (type_name, info->value_table) ? info->value_table : NULL);
    }
  G_WRITE_UNLOCK (&type_rw_lock);
  
  return type;
}


static TypeNode*
type_node_any_new_W (TypeNode             *pnode,       //父节点
		     GType                 ftype,
		     const gchar          *name,        //要创建的子节点名称
		     GTypePlugin          *plugin,
		     GTypeFundamentalFlags type_flags)
{
  guint n_supers;
  GType type;
  TypeNode *node;
  guint i, node_size = 0;

  n_supers = pnode ? pnode->n_supers + 1 : 0;    //父节点树
  
  if (!pnode)
    node_size += SIZEOF_FUNDAMENTAL_INFO;	      /* fundamental type info */
  node_size += SIZEOF_BASE_TYPE_NODE ();	      /* TypeNode structure */
  node_size += (sizeof (GType) * (1 + n_supers + 1)); /* self + ancestors + (0) for ->supers[] */
  node = g_malloc0 (node_size);
  if (!pnode)					      /* offset fundamental types */
  {
    node = G_STRUCT_MEMBER_P (node, SIZEOF_FUNDAMENTAL_INFO);
    static_fundamental_type_nodes[ftype >> G_TYPE_FUNDAMENTAL_SHIFT] = node;
    type = ftype;
  }
  else
    type = (GType) node;
  
  g_assert ((type & TYPE_ID_MASK) == 0);
  
  node->n_supers = n_supers;
  if (!pnode)
  {
    node->supers[0] = type;
    node->supers[1] = 0;
      
    node->is_classed = (type_flags & G_TYPE_FLAG_CLASSED) != 0;
    node->is_instantiatable = (type_flags & G_TYPE_FLAG_INSTANTIATABLE) != 0;
      
    if (NODE_IS_IFACE (node))
    {
      IFACE_NODE_N_PREREQUISITES (node) = 0;
      IFACE_NODE_PREREQUISITES (node) = NULL;
    }
    else
      _g_atomic_array_init (CLASSED_NODE_IFACES_ENTRIES (node));
  }
  else
  {
    node->supers[0] = type;
    memcpy (node->supers + 1, pnode->supers, sizeof (GType) * (1 + pnode->n_supers + 1));
      
    node->is_classed = pnode->is_classed;
    node->is_instantiatable = pnode->is_instantiatable;
      
    if (NODE_IS_IFACE (node))
    {
      IFACE_NODE_N_PREREQUISITES (node) = 0;
      IFACE_NODE_PREREQUISITES (node) = NULL;
    }
    else
    {
      guint j;
      IFaceEntries *entries;

      entries = _g_atomic_array_copy (CLASSED_NODE_IFACES_ENTRIES (pnode),
					  IFACE_ENTRIES_HEADER_SIZE,
					  0);
      if (entries)
      {
        for (j = 0; j < IFACE_ENTRIES_N_ENTRIES (entries); j++)
        {
          entries->entry[j].vtable = NULL;
          entries->entry[j].init_state = UNINITIALIZED;
        }
        _g_atomic_array_update (CLASSED_NODE_IFACES_ENTRIES (node),
				      entries);
      }
    }

    i = pnode->n_children++;
    pnode->children = g_renew (GType, pnode->children, pnode->n_children);
    pnode->children[i] = type;
    }

  TRACE(GOBJECT_TYPE_NEW(name, node->supers[1], type));

  node->plugin = plugin;
  node->n_children = 0;
  node->children = NULL;
  node->data = NULL;
  node->qname = g_quark_from_string (name);
  node->global_gdata = NULL;
  g_hash_table_insert (static_type_nodes_ht,
		       (gpointer) g_quark_to_string (node->qname),
		       (gpointer) type);

  g_atomic_int_inc ((gint *)&type_registration_serial);

  return node;
}

目前为止,MyObject作为GObject的子类,已经完成了注册。

下面,看看当new一个instance的时候,进行那些操作

gpointer
g_object_new (GType	   object_type,
	      const gchar *first_property_name,
	      ...)
{
  GObject *object;
  va_list var_args;
  
  /* short circuit for calls supplying no properties */
  if (!first_property_name)
    return g_object_new_with_properties (object_type, 0, NULL, NULL);

  va_start (var_args, first_property_name);
  object = g_object_new_valist (object_type, first_property_name, var_args);
  va_end (var_args);
  
  return object;
}

GObject *
g_object_new_with_properties (GType          object_type,
                              guint          n_properties,
                              const char    *names[],
                              const GValue   values[])
{
  GObjectClass *class, *unref_class = NULL;
  GObject *object;

  g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL);

  /* Try to avoid thrashing the ref_count if we don't need to (since
   * it's a locked operation).
   */
  class = g_type_class_peek_static (object_type);

  if (class == NULL)
    class = unref_class = g_type_class_ref (object_type);

  if (n_properties > 0)
  {
      guint i, count = 0;
      GObjectConstructParam *params;

      params = g_newa (GObjectConstructParam, n_properties);
      for (i = 0; i < n_properties; i++)
      {
          GParamSpec *pspec = find_pspec (class, names[i]);

          if (!g_object_new_is_valid_property (object_type, pspec, names[i], params, count))
            continue;
          params[count].pspec = pspec;
          params[count].value = (GValue *) &values[i];
          count++;
      }
      object = g_object_new_internal (class, params, count);
    }
  else
    object = g_object_new_internal (class, NULL, 0);

  if (unref_class != NULL)
    g_type_class_unref (unref_class);

  return object;
}

static gpointer
g_object_new_internal (GObjectClass          *class,
                       GObjectConstructParam *params,
                       guint                  n_params)
{
  GObjectNotifyQueue *nqueue = NULL;
  GObject *object;
  guint i;

  if G_UNLIKELY (CLASS_HAS_CUSTOM_CONSTRUCTOR (class))
    return g_object_new_with_custom_constructor (class, params, n_params);

  object = (GObject *) g_type_create_instance (class->g_type_class.g_type);

  g_assert (g_object_is_aligned (object));
  ...
}

GTypeInstance*
g_type_create_instance (GType type)
{
  TypeNode *node;
  GTypeInstance *instance;
  GTypeClass *class;
  gchar *allocated;
  gint private_size;
  gint ivar_size;
  guint i;

  node = lookup_type_node_I (type);
  if (G_UNLIKELY (!node || !node->is_instantiatable))
  {
      g_error ("cannot create new instance of invalid (non-instantiatable) type '%s'",
		 type_descriptive_name_I (type));
  }
  /* G_TYPE_IS_ABSTRACT() is an external call: _U */
  if (G_UNLIKELY (!node->mutatable_check_cache && G_TYPE_IS_ABSTRACT (type)))
  {
      g_error ("cannot create instance of abstract (non-instantiatable) type '%s'",
		 type_descriptive_name_I (type));
  }
  
  class = g_type_class_ref (type);

 
  private_size = node->data->instance.private_size;
  ivar_size = node->data->instance.instance_size;

#ifdef ENABLE_VALGRIND
  if (G_UNLIKELY (private_size && RUNNING_ON_VALGRIND))
    {
      private_size += ALIGN_STRUCT (1);

      /* Allocate one extra pointer size... */
      allocated = g_slice_alloc0 (private_size + ivar_size + sizeof (gpointer));
      /* ... and point it back to the start of the private data. */
      *(gpointer *) (allocated + private_size + ivar_size) = allocated + ALIGN_STRUCT (1);

      /* Tell valgrind that it should treat the object itself as such */
      VALGRIND_MALLOCLIKE_BLOCK (allocated + private_size, ivar_size + sizeof (gpointer), 0, TRUE);
      VALGRIND_MALLOCLIKE_BLOCK (allocated + ALIGN_STRUCT (1), private_size - ALIGN_STRUCT (1), 0, TRUE);
    }
  else
#endif
    allocated = g_slice_alloc0 (private_size + ivar_size);


  instance = (GTypeInstance *) (allocated + private_size);

  for (i = node->n_supers; i > 0; i--)
  {
      TypeNode *pnode;
      
      pnode = lookup_type_node_I (node->supers[i]);
      if (pnode->data->instance.instance_init)
      {
	  instance->g_class = pnode->data->instance.class;
	  pnode->data->instance.instance_init (instance, class);
      }
  }

  instance->g_class = class;
  if (node->data->instance.instance_init)
    node->data->instance.instance_init (instance, class);

#ifdef	G_ENABLE_DEBUG
  IF_DEBUG (INSTANCE_COUNT)
    {
      g_atomic_int_inc ((int *) &node->instance_count);
    }
#endif

  TRACE(GOBJECT_OBJECT_NEW(instance, type));

  return instance;
}

看看GObject中有什么?

...
#define G_OBJECT_CLASS_TYPE(class)  (G_TYPE_FROM_CLASS (class))
#define G_OBJECT_CLASS_NAME(class)  (g_type_name (G_OBJECT_CLASS_TYPE (class)))
...
typedef struct _GObject                  GObject;
typedef struct _GObjectClass             GObjectClass;
typedef struct _GObject                  GInitiallyUnowned;
typedef struct _GObjectClass             GInitiallyUnownedClass;
...
struct  _GObject
{
  GTypeInstance  g_type_instance;
  
  /*< private >*/
  guint          ref_count;  /* (atomic) */   //引用计数,管理生命周期
  GData         *qdata;                       //处理property,signal
};

struct  _GObjectClass
{
  GTypeClass   g_type_class;

  /*< private >*/
  GSList      *construct_properties;

  /*< public >*/
  /* seldom overridden */
  GObject*   (*constructor)     (GType                  type,
                                 guint                  n_construct_properties,
                                 GObjectConstructParam *construct_properties);
  /* overridable methods */
  void       (*set_property)		(GObject        *object,
                                         guint           property_id,
                                         const GValue   *value,
                                         GParamSpec     *pspec);
  void       (*get_property)		(GObject        *object,
                                         guint           property_id,
                                         GValue         *value,
                                         GParamSpec     *pspec);
  void       (*dispose)			(GObject        *object);
  void       (*finalize)		(GObject        *object);
  /* seldom overridden */
  void       (*dispatch_properties_changed) (GObject      *object,
					     guint	   n_pspecs,
					     GParamSpec  **pspecs);
  /* signals */
  void	     (*notify)			(GObject	*object,
					 GParamSpec	*pspec);

  /* called when done constructing */
  void	     (*constructed)		(GObject	*object);

  /*< private >*/
  gsize		flags;

  gsize         n_construct_properties;

  gpointer pspecs;
  gsize n_pspecs;

  /* padding */
  gpointer	pdummy[3];
};

...

constructer:

#include <stdio.h>

void __attribute__((constructor)) begin(void);
void __attribute__((destructor)) end(void);

int main (void)
{
  printf ("\nInside main ()");
}
 
void begin (void)
{
  printf ("\nIn begin ()");
}
 
void end (void)
{
  printf ("\nIn end ()\n");
}

注册:

gtype.c
...
G_DEFINE_CONSTRUCTOR (gobject_init_ctor)

static void
gobject_init_ctor (void)
{
  gobject_init ();
}
...
static void
gobject_init (void)
{
  ...
  _g_object_type_init ();
...

gobject.c:

void
_g_object_type_init (void)
{
  static gboolean initialized = FALSE;
  static const GTypeFundamentalInfo finfo = {
    G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE,
  };
  GTypeInfo info = {
    sizeof (GObjectClass),
    (GBaseInitFunc) g_object_base_class_init,
    (GBaseFinalizeFunc) g_object_base_class_finalize,
    (GClassInitFunc) g_object_do_class_init,
    NULL	/* class_destroy */,
    NULL	/* class_data */,
    sizeof (GObject),
    0		/* n_preallocs */,
    (GInstanceInitFunc) g_object_init,
    NULL,	/* value_table */
  };
  static const GTypeValueTable value_table = {
    g_value_object_init,	  /* value_init */
    g_value_object_free_value,	  /* value_free */
    g_value_object_copy_value,	  /* value_copy */
    g_value_object_peek_pointer,  /* value_peek_pointer */
    "p",			  /* collect_format */
    g_value_object_collect_value, /* collect_value */
    "p",			  /* lcopy_format */
    g_value_object_lcopy_value,	  /* lcopy_value */
  };
  GType type G_GNUC_UNUSED  /* when compiling with G_DISABLE_ASSERT */;
  
  g_return_if_fail (initialized == FALSE);
  initialized = TRUE;
  
  /* G_TYPE_OBJECT
   */
  info.value_table = &value_table;
  type = g_type_register_fundamental (G_TYPE_OBJECT, g_intern_static_string ("GObject"), &info, &finfo, 0);
  g_assert (type == G_TYPE_OBJECT);
  g_value_register_transform_func (G_TYPE_OBJECT, G_TYPE_OBJECT, g_value_object_transform_value);
...
}

GType
g_type_register_fundamental (GType                       type_id,
			     const gchar                *type_name,
			     const GTypeInfo            *info,
			     const GTypeFundamentalInfo *finfo,
			     GTypeFlags			 flags)
{
  TypeNode *node;
  
  g_assert_type_system_initialized ();
  g_return_val_if_fail (type_id > 0, 0);
  g_return_val_if_fail (type_name != NULL, 0);
  g_return_val_if_fail (info != NULL, 0);
  g_return_val_if_fail (finfo != NULL, 0);
  
  if (!check_type_name_I (type_name))
    return 0;
  if ((type_id & TYPE_ID_MASK) ||
      type_id > G_TYPE_FUNDAMENTAL_MAX)
    {
      g_warning ("attempt to register fundamental type '%s' with invalid type id (%" G_GSIZE_FORMAT ")",
		 type_name,
		 type_id);
      return 0;
    }
  if ((finfo->type_flags & G_TYPE_FLAG_INSTANTIATABLE) &&
      !(finfo->type_flags & G_TYPE_FLAG_CLASSED))
    {
      g_warning ("cannot register instantiatable fundamental type '%s' as non-classed",
		 type_name);
      return 0;
    }
  if (lookup_type_node_I (type_id))
    {
      g_warning ("cannot register existing fundamental type '%s' (as '%s')",
		 type_descriptive_name_I (type_id),
		 type_name);
      return 0;
    }
  
  G_WRITE_LOCK (&type_rw_lock);
  node = type_node_fundamental_new_W (type_id, type_name, finfo->type_flags);
  type_add_flags_W (node, flags);
  
  if (check_type_info_I (NULL, NODE_FUNDAMENTAL_TYPE (node), type_name, info))
    type_data_make_W (node, info,
		      check_value_table_I (type_name, info->value_table) ? info->value_table : NULL);
  G_WRITE_UNLOCK (&type_rw_lock);
  
  return NODE_TYPE (node);
}

数据隐藏(private):

第一种:

typedef struct _PMDListNode PMDListNode;
struct  _PMDListNode {
/* private */
        PMDListNode *prev;
        PMDListNode *next;
/* public */
        void *data;
};
C语言认为,程序员应当知道自己正在干什么,而且保证自己的所作所为是正确的

第二种:

typedef struct _PMDListNode PMDListNode;    //保留在h文件
struct  _PMDListNode {                      //下面全部转到c文件中
        PMDListNode *prev;
        PMDListNode *next;
        void *data;
};

GIO

Interface:

GFilereference to a file
GFileInfoinformation about a file or filesystem
GFileEnumeratorlist files in directories
GDriverepresents a drive
GVolumerepresents a file system in an abstract way
GMountrepresents a mounted file system

Stream:

GInputStreamread data
GOutputstreamwrite date
GIOStreamread and write data
GSocketlowlevel platform independent socket object
GSocketClienthigh-level network client helper
GSocketServerhigh-level network server helper

 

Gstreamer重要概念

Pipeline

  • 通过代码搭建
  • 通过命令行搭建
gst-launch-1.0 filesrc location='/home/fuqiang/Videos/1080P.mov’  ! qtdemux name=demux \
demux.audio_0 ! decodebin ! audioconvert ! audioresample ! autoaudiosink \
demux.video_0 ! decodebin ! videoconvert ! videoscale ! autovideosink

Element

  • 基本组成单元,一切都是element,source/filter/sink/bin/pipeline
  • bin/pipeline也是element,循环嵌套

element的创建:

source = gst_element_factory_make ("videotestsrc", "source");
sink = gst_element_factory_make ("autovideosink", "sink");

Bin

  • 一组element链接成的逻辑element
  • pipeline也是bin

  GstElement *bin, *pipeline, *source, *sink;

  /* init */
  gst_init (&argc, &argv);

  /* create */
  pipeline = gst_pipeline_new ("my_pipeline");
  bin = gst_bin_new ("my_bin");
  source = gst_element_factory_make ("fakesrc", "source");
  sink = gst_element_factory_make ("fakesink", "sink");

  /* First add the elements to the bin */
  gst_bin_add_many (GST_BIN (bin), source, sink, NULL);
  /* add the bin to the pipeline */
  gst_bin_add (GST_BIN (pipeline), bin);

  /* link the elements */
  gst_element_link (source, sink);

Pad&GhostPad

  • element的接口
  • src&sink,可以存在多个src和多个sink
  • capbilities system

分类:

  • Always:element创建的时候就存在,整个生命周期都存在
  • Sometimes:element创建的时候不存在,后期根据具体情况创建

fuqiang@fuqiang-virtual:~/workspace/glib/glib$ gst-inspect-1.0 qtdemux

...

GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstQTDemux

Pad Templates:
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      video/quicktime
      video/mj2
      audio/x-m4a
      application/x-3gp

  SRC template: 'audio_%u'
    Availability: Sometimes
    Capabilities:
      ANY
  
  SRC template: 'subtitle_%u'
    Availability: Sometimes
    Capabilities:
      ANY
  
  SRC template: 'video_%u'
    Availability: Sometimes
    Capabilities:
      ANY
  • Request:element创建的时候不存在,程序员创建
fuqiang@fuqiang-virtual:~/workspace/glib/glib$ gst-inspect-1.0 tee

...

GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstTee

Pad Templates:
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      ANY
  
  SRC template: 'src_%u'
    Availability: On request
    Capabilities:
      ANY

  • Ghost:bin使用

 

  /* init */
  gst_init (&argc, &argv);
 
  /* create element, add to bin */
  sink = gst_element_factory_make ("fakesrc", "sink");
  bin = gst_bin_new ("mybin");
  gst_bin_add (GST_BIN (bin), sink);
 
  /* add ghostpad */
  pad = gst_element_get_static_pad (sink, "sink");
  gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
  gst_object_unref (GST_OBJECT (pad));

数据传输方式:

  • PUSH:gst_pad_push_data() —–> chain()
  • PULL:比较少用,一般都是demux element会使用pull模式,方便处理数据,gst_pad_pull_range() —–> getrange()
static GstFlowReturn
gst_pad_push_data (GstPad * pad, GstPadProbeType type, void *data)
{
  GstPad *peer;
  ...
  /* do block probes */
  PROBE_HANDLE (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped,
      probe_handled);
  ...
  PROBE_HANDLE (pad, type, data, probe_stopped, probe_handled);
  ...
  if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
    goto not_linked;
  ...
  ret = gst_pad_chain_data_unchecked (peer, type, data);
  ...
}


static inline GstFlowReturn
gst_pad_chain_data_unchecked (GstPad * pad, GstPadProbeType type, void *data)
{
  GstObject *parent;
  ...
  PROBE_HANDLE (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped,
      probe_handled);

  PROBE_HANDLE (pad, type, data, probe_stopped, probe_handled);

  ACQUIRE_PARENT (pad, parent, no_parent);
  ...
  GstPadChainFunction chainfunc;
  ...
  ret = chainfunc (pad, parent, GST_BUFFER_CAST (data));
  ...
}


void
gst_pad_set_chain_function_full (GstPad * pad, GstPadChainFunction chain,
    gpointer user_data, GDestroyNotify notify)
{
  g_return_if_fail (GST_IS_PAD (pad));
  g_return_if_fail (GST_PAD_IS_SINK (pad));

  if (pad->chainnotify)
    pad->chainnotify (pad->chaindata);
  GST_PAD_CHAINFUNC (pad) = chain;
  pad->chaindata = user_data;
  pad->chainnotify = notify;
}

#define GST_PAD_CHAINFUNC(pad)		(GST_PAD_CAST(pad)->chainfunc)

Caps

示例

代码实现(openh264):

...

/* pad templates */
static GstStaticPadTemplate gst_openh264dec_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS
    ("video/x-h264, stream-format=(string)byte-stream, alignment=(string)au, "
      SUPPORTED_PROFILE_STR
    ));

static GstStaticPadTemplate gst_openh264dec_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("I420")));

/* class initialization */

G_DEFINE_TYPE_WITH_CODE (GstOpenh264Dec, gst_openh264dec,
    GST_TYPE_VIDEO_DECODER,
    GST_DEBUG_CATEGORY_INIT (gst_openh264dec_debug_category, "openh264dec", 0,
        "debug category for openh264dec element"));
GST_ELEMENT_REGISTER_DEFINE_CUSTOM (openh264dec, openh264dec_element_init);

static void
gst_openh264dec_class_init (GstOpenh264DecClass * klass)
{
  GstVideoDecoderClass *video_decoder_class = GST_VIDEO_DECODER_CLASS (klass);

  gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
      &gst_openh264dec_sink_template);
  gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
      &gst_openh264dec_src_template);

  ...
}

实际效果:

fuqiang@fuqiang-virtual:~/workspace/gstreamer/subprojects/gst-plugins-bad/ext/openh264$ gst-inspect-1.0 openh264dec

...

GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstVideoDecoder
                         +----GstOpenh264Dec

Pad Templates:
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      video/x-h264
          stream-format: byte-stream
              alignment: au
                profile: { (string)constrained-baseline, (string)baseline, (string)main, (string)high, (string)constrained-high, (string)progressive-high }
  
  SRC template: 'src'
    Availability: Always
    Capabilities:
      video/x-raw
                 format: I420
                  width: [ 1, 2147483647 ]
                 height: [ 1, 2147483647 ]
              framerate: [ 0/1, 2147483647/1 ]

caps的协商

Fixed negotiation

static void
gst_wavenc_init (GstWavEnc * wavenc)
{
  wavenc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
  gst_pad_set_chain_function (wavenc->sinkpad,
      GST_DEBUG_FUNCPTR (gst_wavenc_chain));
  gst_pad_set_event_function (wavenc->sinkpad,
      GST_DEBUG_FUNCPTR (gst_wavenc_event));
  gst_pad_use_fixed_caps (wavenc->sinkpad);
  gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->sinkpad);

  wavenc->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
  gst_pad_use_fixed_caps (wavenc->srcpad);
  gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->srcpad);
}
...
static GstFlowReturn
gst_wavenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{

  ...
  if (G_UNLIKELY (!wavenc->sent_header)) {
    GstStructure *s;
    GstCaps *caps = gst_pad_get_allowed_caps (wavenc->srcpad);


    ...
    gst_pad_set_caps (wavenc->srcpad, caps);
    gst_caps_unref (caps);


    ...
  }


  ...
  flow = gst_pad_push (wavenc->srcpad, buf);

  return flow;
}

static inline gboolean
gst_pad_set_caps (GstPad * pad, GstCaps * caps)
{
  GstEvent *event;
  gboolean res = TRUE;

  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
  g_return_val_if_fail (caps != NULL && gst_caps_is_fixed (caps), FALSE);

  event = gst_event_new_caps (caps);

  if (GST_PAD_IS_SRC (pad))
    res = gst_pad_push_event (pad, event);
  else
    res = gst_pad_send_event (pad, event);

  return res;
}
  • 解码器
  • 类型探测器,typefinder等
  • 一些产生固定格式的源,filesrc等

Transform negotiation

static gboolean
gst_wavenc_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
  gboolean res = TRUE;
  GstWavEnc *wavenc;
  GstTagList *tags;
  GstToc *toc;

  wavenc = GST_WAVENC (parent);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);
      gst_wavenc_sink_setcaps (pad, caps);

      /* have our own src caps */
      gst_event_unref (event);
      break;
    }
...

static gboolean
gst_wavenc_sink_setcaps (GstPad * pad, GstCaps * caps)
{
  GstWavEnc *wavenc;
  GstStructure *structure;
  const gchar *name;
  gint chans, rate;
  GstCaps *ccaps;

  wavenc = GST_WAVENC (gst_pad_get_parent (pad));

  ccaps = gst_pad_get_current_caps (pad);
  if (wavenc->sent_header && ccaps && !gst_caps_can_intersect (caps, ccaps)) {
    gst_caps_unref (ccaps);
    GST_WARNING_OBJECT (wavenc, "cannot change format in middle of stream");
    goto fail;
  }
  if (ccaps)
    gst_caps_unref (ccaps);

  structure = gst_caps_get_structure (caps, 0);
  name = gst_structure_get_name (structure);

  if (!gst_structure_get_int (structure, "channels", &chans) ||
      !gst_structure_get_int (structure, "rate", &rate)) {
    GST_WARNING_OBJECT (wavenc, "caps incomplete");
    goto fail;
  }

  wavenc->channels = chans;
  wavenc->rate = rate;
  wavenc->channel_mask = 0;

  if (strcmp (name, "audio/x-raw") == 0) {
    GstAudioInfo info;
    guint64 gstmask;

    if (!gst_audio_info_from_caps (&info, caps)) {
      GST_WARNING_OBJECT (wavenc, "Could not retrieve audio info from caps");
      goto fail;
    }
    if (gst_audio_channel_positions_to_mask (info.position, wavenc->channels,
            FALSE, &gstmask)) {
      wavenc->channel_mask = gstmask_to_wavmask (gstmask, wavenc->destPos);
      memcpy (wavenc->srcPos, info.position, sizeof (info.position));
      GST_DEBUG_OBJECT (wavenc, "Channel mask input: 0x%" G_GINT64_MODIFIER "x"
          " output: 0x%" G_GINT64_MODIFIER "x", gstmask, wavenc->channel_mask);
    }
    wavenc->audio_format = GST_AUDIO_INFO_FORMAT (&info);

    if (GST_AUDIO_INFO_IS_INTEGER (&info))
      wavenc->format = GST_RIFF_WAVE_FORMAT_PCM;
    else if (GST_AUDIO_INFO_IS_FLOAT (&info))
      wavenc->format = GST_RIFF_WAVE_FORMAT_IEEE_FLOAT;
    else
      goto fail;

    wavenc->width = GST_AUDIO_INFO_WIDTH (&info);
  } else if (strcmp (name, "audio/x-alaw") == 0) {
    wavenc->format = GST_RIFF_WAVE_FORMAT_ALAW;
    wavenc->width = 8;
  } else if (strcmp (name, "audio/x-mulaw") == 0) {
    wavenc->format = GST_RIFF_WAVE_FORMAT_MULAW;
    wavenc->width = 8;
  } else {
    GST_WARNING_OBJECT (wavenc, "Unsupported format %s", name);
    goto fail;
  }

  gst_object_unref (wavenc);
  return TRUE;

fail:
  gst_object_unref (wavenc);
  return FALSE;
}

Dynamic negotiation

GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstBaseTransform
                         +----GstVideoFilter
                               +----GstVideoConvert


static gboolean
gst_base_transform_sink_eventfunc (GstBaseTransform * trans, GstEvent * event)
{
  gboolean ret = TRUE, forward = TRUE;
  GstBaseTransformPrivate *priv = trans->priv;

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);
      /* clear any pending reconfigure flag */
      gst_pad_check_reconfigure (trans->srcpad);
      ret = gst_base_transform_setcaps (trans, trans->sinkpad, caps);
      if (!ret)
        gst_pad_mark_reconfigure (trans->srcpad);

      forward = FALSE;
      break;
    }
...

static gboolean
gst_base_transform_setcaps (GstBaseTransform * trans, GstPad * pad,
    GstCaps * incaps)
{
  GstBaseTransformPrivate *priv = trans->priv;
  GstCaps *outcaps, *prev_incaps = NULL, *prev_outcaps = NULL;

  /* find best possible caps for the other pad */
  outcaps = gst_base_transform_find_transform (trans, pad, incaps);    //获取一个合适的outcaps
  if (!outcaps || gst_caps_is_empty (outcaps))
    goto no_transform_possible;

  /* configure the element now */

  /* if we have the same caps, we can optimize and reuse the input caps */
  if (gst_caps_is_equal (incaps, outcaps)) {
    GST_INFO_OBJECT (trans, "reuse caps");
    gst_caps_unref (outcaps);
    outcaps = gst_caps_ref (incaps);
  }

  prev_incaps = gst_pad_get_current_caps (trans->sinkpad);
  prev_outcaps = gst_pad_get_current_caps (trans->srcpad);
  if (prev_incaps && prev_outcaps && gst_caps_is_equal (prev_incaps, incaps)
      && gst_caps_is_equal (prev_outcaps, outcaps)) {
    GST_DEBUG_OBJECT (trans,
        "New caps equal to old ones: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT,
        incaps, outcaps);
    ret = TRUE;
  } else {
    /* call configure now */
    if (!(ret = gst_base_transform_configure_caps (trans, incaps, outcaps)))
      goto failed_configure;

    if (!prev_outcaps || !gst_caps_is_equal (outcaps, prev_outcaps))
      /* let downstream know about our caps */
      ret = gst_pad_set_caps (trans->srcpad, outcaps);
  }

  if (ret) {
    /* try to get a pool when needed */
    ret = gst_base_transform_do_bufferpool (trans, outcaps);
  }
...



static GstCaps *
gst_base_transform_find_transform (GstBaseTransform * trans, GstPad * pad,
    GstCaps * caps)
{
  GstBaseTransformClass *klass;
  GstPad *otherpad, *otherpeer;
  GstCaps *othercaps;
  gboolean is_fixed;

  klass = GST_BASE_TRANSFORM_GET_CLASS (trans);

  otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad;  //获取同element的另一个pad
  otherpeer = gst_pad_get_peer (otherpad);  //获取同element另一个pad的peer

  /* see how we can transform the input caps. We need to do this even for
   * passthrough because it might be possible that this element cannot support
   * passthrough at all. */
  othercaps = gst_base_transform_transform_caps (trans,
      GST_PAD_DIRECTION (pad), caps, NULL);  //获取同element的另一个pad的caps

  /* The caps we can actually output is the intersection of the transformed
   * caps with the pad template for the pad */
  if (othercaps && !gst_caps_is_empty (othercaps)) {
    GstCaps *intersect, *templ_caps;

    templ_caps = gst_pad_get_pad_template_caps (otherpad);  //获取同element的另一个pad的模板caps
    GST_DEBUG_OBJECT (trans,
        "intersecting against padtemplate %" GST_PTR_FORMAT, templ_caps);

    intersect =
        gst_caps_intersect_full (othercaps, templ_caps,
        GST_CAPS_INTERSECT_FIRST);

    gst_caps_unref (othercaps);
    gst_caps_unref (templ_caps);
    othercaps = intersect;
  }

  /* check if transform is empty */
  if (!othercaps || gst_caps_is_empty (othercaps))
    goto no_transform;

  /* if the othercaps are not fixed, we need to fixate them, first attempt
   * is by attempting passthrough if the othercaps are a superset of caps. */
  /* FIXME. maybe the caps is not fixed because it has multiple structures of
   * fixed caps */
  is_fixed = gst_caps_is_fixed (othercaps);
  if (!is_fixed) {
    GST_DEBUG_OBJECT (trans,
        "transform returned non fixed  %" GST_PTR_FORMAT, othercaps);

    /* Now let's see what the peer suggests based on our transformed caps */
    if (otherpeer) {
      GstCaps *peercaps, *intersection, *templ_caps;

      GST_DEBUG_OBJECT (trans,
          "Checking peer caps with filter %" GST_PTR_FORMAT, othercaps);

      peercaps = gst_pad_query_caps (otherpeer, othercaps);
      GST_DEBUG_OBJECT (trans, "Resulted in %" GST_PTR_FORMAT, peercaps);
      if (!gst_caps_is_empty (peercaps)) {
        templ_caps = gst_pad_get_pad_template_caps (otherpad);

        GST_DEBUG_OBJECT (trans,
            "Intersecting with template caps %" GST_PTR_FORMAT, templ_caps);

        intersection =
            gst_caps_intersect_full (peercaps, templ_caps,
            GST_CAPS_INTERSECT_FIRST);
        GST_DEBUG_OBJECT (trans, "Intersection: %" GST_PTR_FORMAT,
            intersection);
        gst_caps_unref (peercaps);
        gst_caps_unref (templ_caps);
        peercaps = intersection;

        GST_DEBUG_OBJECT (trans,
            "Intersecting with transformed caps %" GST_PTR_FORMAT, othercaps);
        intersection =
            gst_caps_intersect_full (peercaps, othercaps,
            GST_CAPS_INTERSECT_FIRST);
        GST_DEBUG_OBJECT (trans, "Intersection: %" GST_PTR_FORMAT,
            intersection);
        gst_caps_unref (peercaps);
        gst_caps_unref (othercaps);
        othercaps = intersection;
      } else {
        gst_caps_unref (othercaps);
        othercaps = peercaps;
      }

      is_fixed = gst_caps_is_fixed (othercaps);
    } else {
      GST_DEBUG_OBJECT (trans, "no peer, doing passthrough");
      gst_caps_unref (othercaps);
      othercaps = gst_caps_ref (caps);
      is_fixed = TRUE;
    }
  }
  if (gst_caps_is_empty (othercaps))
    goto no_transform_possible;

  GST_DEBUG ("have %sfixed caps %" GST_PTR_FORMAT, (is_fixed ? "" : "non-"),
      othercaps);

  /* second attempt at fixation, call the fixate vmethod */
  /* caps could be fixed but the subclass may want to add fields */
  if (klass->fixate_caps) {
    othercaps =
        klass->fixate_caps (trans, GST_PAD_DIRECTION (pad), caps, othercaps);
    is_fixed = othercaps && gst_caps_is_fixed (othercaps);
    GST_DEBUG_OBJECT (trans, "after fixating %" GST_PTR_FORMAT, othercaps);
  }

  return othercaps;
}

Plugins

element管理单元,一个插件(Plugin)包含1个或者多个element

fuqiang@fuqiang-virtual:~/workspace/glib/glib$ gst-inspect-1.0 video4linux2
Plugin Details:
  Name                     video4linux2
  Description              elements for Video 4 Linux
  Filename                 /usr/local/lib/x86_64-linux-gnu/gstreamer-1.0/libgstvideo4linux2.so
  Version                  1.20.3.1
  License                  LGPL
  Source module            gst-plugins-good
  Binary package           GStreamer Good Plug-ins git
  Origin URL               Unknown package origin

  v4l2deviceprovider: Video (video4linux2) Device Provider
  v4l2radio: Radio (video4linux2) Tuner
  v4l2sink: Video (video4linux2) Sink
  v4l2src: Video (video4linux2) Source

  4 features:
  +-- 3 elements
  +-- 1 device providers

Multithreading

  • 自动管理机制,自动创建和销毁,比如软件解码器(创建多线程解码)
  • 手动明确创建,queue

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_RECONFIGURE:
      GST_QUEUE_MUTEX_LOCK (queue);
      if (queue->srcresult == GST_FLOW_NOT_LINKED) {
        /* when we got not linked, assume downstream is linked again now and we
         * can try to start pushing again */
        queue->srcresult = GST_FLOW_OK;
        gst_pad_start_task (pad, (GstTaskFunction) gst_queue_loop, pad, NULL);
      }
      GST_QUEUE_MUTEX_UNLOCK (queue);

      res = gst_pad_push_event (queue->sinkpad, event);
      break;
    default:
      res = gst_pad_event_default (pad, parent, event);
      break;
  }

...

static void
gst_queue_loop (GstPad * pad)
{
  GstQueue *queue;
  GstFlowReturn ret;

  queue = (GstQueue *) GST_PAD_PARENT (pad);

  /* have to lock for thread-safety */
  GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing);

  while (gst_queue_is_empty (queue)) {
    GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "queue is empty");
    if (!queue->silent) {
      GST_QUEUE_MUTEX_UNLOCK (queue);
      g_signal_emit (queue, gst_queue_signals[SIGNAL_UNDERRUN], 0);
      GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing);
    }

    /* we recheck, the signal could have changed the thresholds */
    while (gst_queue_is_empty (queue)) {
      GST_QUEUE_WAIT_ADD_CHECK (queue, out_flushing);
    }

    GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "queue is not empty");
    if (!queue->silent) {
      GST_QUEUE_MUTEX_UNLOCK (queue);
      g_signal_emit (queue, gst_queue_signals[SIGNAL_RUNNING], 0);
      g_signal_emit (queue, gst_queue_signals[SIGNAL_PUSHING], 0);
      GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing);
    }
  }

  ret = gst_queue_push_one (queue);
  queue->srcresult = ret;
  if (ret != GST_FLOW_OK)
    goto out_flushing;

  GST_QUEUE_MUTEX_UNLOCK (queue);

  return;

  /* ERRORS */
out_flushing:
  ...
}

通信机制

 

BUS(Message)

  • pipeline主动报告运行状态,上行
  • Message队列
  • 应用程序获取感兴趣的Message并执行操作
  • GST提供,异步

2种方式进行通信:

  • 通过GMainLoop
#include <gst/gst.h>

static GMainLoop *loop;

static gboolean
my_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
{
  switch (GST_MESSAGE_TYPE (message)) {
    case GST_MESSAGE_ERROR:{
      GError *err;
      gchar *debug;

      gst_message_parse_error (message, &err, &debug);
      g_print ("Error: %s\n", err->message);
      g_error_free (err);
      g_free (debug);

      g_main_loop_quit (loop);
      break;
    }
    default:
      /* unhandled message */
      break;
  }

  /* we want to be notified again the next time there is a message
   * on the bus, so returning TRUE (FALSE means we want to stop watching
   * for messages on the bus and our callback should not be called again)
   */
  return TRUE;
}

gint
main (gint argc, gchar * argv[])
{
  GstElement *pipeline;
  GstBus *bus;
  guint bus_watch_id;

  /* init */
  gst_init (&argc, &argv);

  /* create pipeline, add handler */
  pipeline = gst_pipeline_new ("my_pipeline");

  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  bus_watch_id = gst_bus_add_watch (bus, my_bus_callback, NULL);
  gst_object_unref (bus);

  loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (loop);

  /* clean up */
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);
  g_source_remove (bus_watch_id);
  g_main_loop_unref (loop);

  return 0;
}
  • gst_bus_add_watch () 将消息处理程序附加到pipeline的消息总线
GstBus *bus;

bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
gst_bus_add_signal_watch (bus);
g_signal_connect (bus, "message::error", G_CALLBACK (cb_message_error), NULL);
g_signal_connect (bus, "message::eos", G_CALLBACK (cb_message_eos), NULL);

Event

pipeline的插件之间进行通信的机制,或者应用程序向某个插件发送

  • pipeline的element之间通信,上行,下行,双向
  • 与缓冲区数据并行,由pad接收
  • STREAM_LOCK

event列表如下(subprojects/gstreamer/gst/gstevent.h):

typedef enum {
  GST_EVENT_UNKNOWN               = GST_EVENT_MAKE_TYPE (0, 0),

  /* bidirectional events */
  GST_EVENT_FLUSH_START           = GST_EVENT_MAKE_TYPE (10, _FLAG(BOTH)),
  GST_EVENT_FLUSH_STOP            = GST_EVENT_MAKE_TYPE (20, _FLAG(BOTH) | _FLAG(SERIALIZED)),

  /* downstream serialized events */
  GST_EVENT_STREAM_START          = GST_EVENT_MAKE_TYPE (40, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED) | _FLAG(STICKY)),
  GST_EVENT_CAPS                  = GST_EVENT_MAKE_TYPE (50, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED) | _FLAG(STICKY)),
  GST_EVENT_SEGMENT               = GST_EVENT_MAKE_TYPE (70, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED) | _FLAG(STICKY)),
  GST_EVENT_STREAM_COLLECTION     = GST_EVENT_MAKE_TYPE (75, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED) | _FLAG(STICKY) | _FLAG(STICKY_MULTI)),
  GST_EVENT_TAG                   = GST_EVENT_MAKE_TYPE (80, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED) | _FLAG(STICKY) | _FLAG(STICKY_MULTI)),
  GST_EVENT_BUFFERSIZE            = GST_EVENT_MAKE_TYPE (90, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED) | _FLAG(STICKY)),
  GST_EVENT_SINK_MESSAGE          = GST_EVENT_MAKE_TYPE (100, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED) | _FLAG(STICKY) | _FLAG(STICKY_MULTI)),
  GST_EVENT_STREAM_GROUP_DONE     = GST_EVENT_MAKE_TYPE (105, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED) | _FLAG(STICKY)),
  GST_EVENT_EOS                   = GST_EVENT_MAKE_TYPE (110, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED) | _FLAG(STICKY)),
  GST_EVENT_TOC                   = GST_EVENT_MAKE_TYPE (120, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED) | _FLAG(STICKY) | _FLAG(STICKY_MULTI)),
  GST_EVENT_PROTECTION            = GST_EVENT_MAKE_TYPE (130, _FLAG (DOWNSTREAM) | _FLAG (SERIALIZED) | _FLAG (STICKY) | _FLAG (STICKY_MULTI)),

  /* non-sticky downstream serialized */
  GST_EVENT_SEGMENT_DONE          = GST_EVENT_MAKE_TYPE (150, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED)),
  GST_EVENT_GAP                   = GST_EVENT_MAKE_TYPE (160, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED)),

  /* sticky downstream non-serialized */
  GST_EVENT_INSTANT_RATE_CHANGE   = GST_EVENT_MAKE_TYPE (180, _FLAG(DOWNSTREAM) | _FLAG(STICKY)),

  /* upstream events */
  GST_EVENT_QOS                    = GST_EVENT_MAKE_TYPE (190, _FLAG(UPSTREAM)),
  GST_EVENT_SEEK                   = GST_EVENT_MAKE_TYPE (200, _FLAG(UPSTREAM)),
  GST_EVENT_NAVIGATION             = GST_EVENT_MAKE_TYPE (210, _FLAG(UPSTREAM)),
  GST_EVENT_LATENCY                = GST_EVENT_MAKE_TYPE (220, _FLAG(UPSTREAM)),
  GST_EVENT_STEP                   = GST_EVENT_MAKE_TYPE (230, _FLAG(UPSTREAM)),
  GST_EVENT_RECONFIGURE            = GST_EVENT_MAKE_TYPE (240, _FLAG(UPSTREAM)),
  GST_EVENT_TOC_SELECT             = GST_EVENT_MAKE_TYPE (250, _FLAG(UPSTREAM)),
  GST_EVENT_SELECT_STREAMS         = GST_EVENT_MAKE_TYPE (260, _FLAG(UPSTREAM)),
  GST_EVENT_INSTANT_RATE_SYNC_TIME = GST_EVENT_MAKE_TYPE (261, _FLAG(UPSTREAM)),

  /* custom events start here */
  GST_EVENT_CUSTOM_UPSTREAM          = GST_EVENT_MAKE_TYPE (270, _FLAG(UPSTREAM)),
  GST_EVENT_CUSTOM_DOWNSTREAM        = GST_EVENT_MAKE_TYPE (280, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED)),
  GST_EVENT_CUSTOM_DOWNSTREAM_OOB    = GST_EVENT_MAKE_TYPE (290, _FLAG(DOWNSTREAM)),
  GST_EVENT_CUSTOM_DOWNSTREAM_STICKY = GST_EVENT_MAKE_TYPE (300, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED) | _FLAG(STICKY) | _FLAG(STICKY_MULTI)),
  GST_EVENT_CUSTOM_BOTH              = GST_EVENT_MAKE_TYPE (310, _FLAG(BOTH) | _FLAG(SERIALIZED)),
  GST_EVENT_CUSTOM_BOTH_OOB          = GST_EVENT_MAKE_TYPE (320, _FLAG(BOTH))
} GstEventType;

发送event():

static gboolean
gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
    GstEvent * event)
{
  GstQTDemux *demux = GST_QTDEMUX (parent);
  gboolean res = TRUE;

  GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));

  switch (GST_EVENT_TYPE (event)) {
   case GST_EVENT_EOS:
      /* If we are in push mode, and get an EOS before we've seen any streams,
       * then error out - we have nowhere to send the EOS */
      if (!demux->pullbased) {
        gint i;
        gboolean has_valid_stream = FALSE;
        for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
          if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
            has_valid_stream = TRUE;
            break;
          }
        }
        if (!has_valid_stream)
          gst_qtdemux_post_no_playable_stream_error (demux);
        else {
          GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
              (guint) gst_adapter_available (demux->adapter));
          if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
            res = FALSE;
          }
        }
      }
      break;
...

static void
gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
{
  gboolean has_valid_stream = FALSE;
  GstEventType etype = GST_EVENT_TYPE (event);
  guint i;

  GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
      GST_EVENT_TYPE_NAME (event));

  for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
    GstPad *pad;
    QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
    GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id);

    if ((pad = stream->pad)) {
      has_valid_stream = TRUE;

      if (etype == GST_EVENT_EOS) {
        /* let's not send twice */
        if (stream->sent_eos)
          continue;
        stream->sent_eos = TRUE;
      }

      gst_pad_push_event (pad, gst_event_ref (event));
    }
  }

  gst_event_unref (event);

  /* if it is EOS and there are no pads, post an error */
  if (!has_valid_stream && etype == GST_EVENT_EOS) {
    gst_qtdemux_post_no_playable_stream_error (qtdemux);
  }
}

Signal

  • 应用程序控制插件运行状态
  • Gobject提供,同步
/* Connect to the pad-added signal */
g_signal_connect (data.source, "pad-added", G_CALLBACK (pad_added_handler), &data);
...
static void pad_added_handler (GstElement *src, GstPad *new_pad, CustomData *data) {
...
}

Probe

应用程序探测pad中的数据:

    GstElement *sink = gst_element_factory_make ("ximagesink", "videosink");
    //date probe
    GstPad *pad = gst_element_get_static_pad (sink, "sink");
    gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER,
        (GstPadProbeCallback) cb_have_data, NULL, NULL);
    gst_object_unref (pad);

Query

Query列表:

ypedef enum {
  GST_QUERY_UNKNOWN      = GST_QUERY_MAKE_TYPE (0, 0),
  GST_QUERY_POSITION     = GST_QUERY_MAKE_TYPE (10, _FLAG(BOTH)),
  GST_QUERY_DURATION     = GST_QUERY_MAKE_TYPE (20, _FLAG(BOTH)),
  GST_QUERY_LATENCY      = GST_QUERY_MAKE_TYPE (30, _FLAG(BOTH)),
  GST_QUERY_JITTER       = GST_QUERY_MAKE_TYPE (40, _FLAG(BOTH)),
  GST_QUERY_RATE         = GST_QUERY_MAKE_TYPE (50, _FLAG(BOTH)),
  GST_QUERY_SEEKING      = GST_QUERY_MAKE_TYPE (60, _FLAG(BOTH)),
  GST_QUERY_SEGMENT      = GST_QUERY_MAKE_TYPE (70, _FLAG(BOTH)),
  GST_QUERY_CONVERT      = GST_QUERY_MAKE_TYPE (80, _FLAG(BOTH)),
  GST_QUERY_FORMATS      = GST_QUERY_MAKE_TYPE (90, _FLAG(BOTH)),
  GST_QUERY_BUFFERING    = GST_QUERY_MAKE_TYPE (110, _FLAG(BOTH)),
  GST_QUERY_CUSTOM       = GST_QUERY_MAKE_TYPE (120, _FLAG(BOTH)),
  GST_QUERY_URI          = GST_QUERY_MAKE_TYPE (130, _FLAG(BOTH)),
  GST_QUERY_ALLOCATION   = GST_QUERY_MAKE_TYPE (140, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED)),
  GST_QUERY_SCHEDULING   = GST_QUERY_MAKE_TYPE (150, _FLAG(UPSTREAM)),
  GST_QUERY_ACCEPT_CAPS  = GST_QUERY_MAKE_TYPE (160, _FLAG(BOTH)),
  GST_QUERY_CAPS         = GST_QUERY_MAKE_TYPE (170, _FLAG(BOTH)),
  GST_QUERY_DRAIN        = GST_QUERY_MAKE_TYPE (180, _FLAG(DOWNSTREAM) | _FLAG(SERIALIZED)),
  GST_QUERY_CONTEXT      = GST_QUERY_MAKE_TYPE (190, _FLAG(BOTH)),
  GST_QUERY_BITRATE      = GST_QUERY_MAKE_TYPE (200, _FLAG(DOWNSTREAM)),
} GstQueryType;

应用程序查询pipeline的运行状态:

GstQuery *query;
gint64 start, end;
query = gst_query_new_seeking (GST_FORMAT_TIME);
if (gst_element_query (data->playbin, query)) {
    gst_query_parse_seeking (query, NULL, &data->seek_enabled, &start, &end);
    if (data->seek_enabled) {
        g_print ("Seeking is ENABLED from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT "\n",
                  GST_TIME_ARGS (start), GST_TIME_ARGS (end));
        } else {
              g_print ("Seeking is DISABLED for this stream.\n");
        }
    }
    else {
        g_printerr ("Seeking query failed.");
    }
    gst_query_unref (query);
}

响应SEEKING的Query:

static gboolean
gst_avi_demux_handle_src_query (GstPad * pad, GstObject * parent,
    GstQuery * query)
{
...
case GST_QUERY_SEEKING:{
      GstFormat fmt;

      gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
      if (fmt == GST_FORMAT_TIME) {
        gboolean seekable = TRUE;

        if (avi->streaming) {
          seekable = avi->seekable;
        }

        gst_query_set_seeking (query, GST_FORMAT_TIME, seekable,
            0, stream->duration);
        res = TRUE;
      }
      break;
    }

...

状态机

typedef enum {
  GST_STATE_VOID_PENDING        = 0, 
  GST_STATE_NULL                = 1,
  GST_STATE_READY               = 2,
  GST_STATE_PAUSED              = 3,
  GST_STATE_PLAYING             = 4
} GstState;

状态转换:

typedef enum /*< flags=0 >*/
{
  GST_STATE_CHANGE_NULL_TO_READY        = (GST_STATE_NULL<<3) | GST_STATE_READY,
  GST_STATE_CHANGE_READY_TO_PAUSED      = (GST_STATE_READY<<3) | GST_STATE_PAUSED,
  GST_STATE_CHANGE_PAUSED_TO_PLAYING    = (GST_STATE_PAUSED<<3) | GST_STATE_PLAYING,
  GST_STATE_CHANGE_PLAYING_TO_PAUSED    = (GST_STATE_PLAYING<<3) | GST_STATE_PAUSED,
  GST_STATE_CHANGE_PAUSED_TO_READY      = (GST_STATE_PAUSED<<3) | GST_STATE_READY,
  GST_STATE_CHANGE_READY_TO_NULL        = (GST_STATE_READY<<3) | GST_STATE_NULL
} GstStateChange;

应用实现

HelloWorld

#include <gst/gst.h>

int
main (int argc, char *argv[])
{
  GstElement *pipeline;
  GstBus *bus;
  GstMessage *msg;

  /* Initialize GStreamer */
  gst_init (&argc, &argv);

  /* Build the pipeline */
  pipeline =
      gst_parse_launch
      ("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm",
      NULL);

  /* Start playing */
  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  /* Wait until error or EOS */
  bus = gst_element_get_bus (pipeline);
  msg =
      gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
      GST_MESSAGE_ERROR | GST_MESSAGE_EOS);

  /* See next tutorial for proper error message handling/parsing */
  if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
    g_error ("An error occurred! Re-run with the GST_DEBUG=*:WARN environment "
        "variable set for more details.");
  }

  /* Free resources */
  gst_message_unref (msg);
  gst_object_unref (bus);
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);
  return 0;
}

gst_init(gst.c):只做了解析参数的动作

-----------------------------------------gst.c----------------------------------------------------------
gst_init (int *argc, char **argv[])
{
  GError *err = NULL;

  if (!gst_init_check (argc, argv, &err)) {
    g_print ("Could not initialize GStreamer: %s\n",
        err ? err->message : "unknown error occurred");
    if (err) {
      g_error_free (err);
    }
    exit (1);
  }
}


gboolean
gst_init_check (int *argc, char **argv[], GError ** error)
{
#ifndef GST_DISABLE_OPTION_PARSING
  GOptionGroup *group;
  GOptionContext *ctx;
#endif
  gboolean res;

  g_mutex_lock (&init_lock);

  if (gst_initialized) {
    GST_DEBUG ("already initialized gst");
    g_mutex_unlock (&init_lock);
    return TRUE;
  }
#ifndef GST_DISABLE_OPTION_PARSING
  ctx = g_option_context_new ("- GStreamer initialization");
  g_option_context_set_ignore_unknown_options (ctx, TRUE);
  g_option_context_set_help_enabled (ctx, FALSE);
  group = gst_init_get_option_group ();
  g_option_context_add_group (ctx, group);
  res = g_option_context_parse (ctx, argc, argv, error);
  g_option_context_free (ctx);
#else
  init_pre (NULL, NULL, NULL, NULL);
  init_post (NULL, NULL, NULL, NULL);
  res = TRUE;
#endif

  gst_initialized = res;

  g_mutex_unlock (&init_lock);

  return res;
}

gst_parse_launch(gstparse.c):

-----------------------------------------gstparse.c----------------------------------------------------------
gstElement *
gst_parse_launch (const gchar * pipeline_description, GError ** error)
{
  return gst_parse_launch_full (pipeline_description, NULL, GST_PARSE_FLAG_NONE, error);
}

GstElement *
gst_parse_launch_full (const gchar * pipeline_description,
    GstParseContext * context, GstParseFlags flags, GError ** error)
{
#ifndef GST_DISABLE_PARSE
  GstElement *element;
  GError *myerror = NULL;

  g_return_val_if_fail (pipeline_description != NULL, NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  GST_CAT_INFO (GST_CAT_PIPELINE, "parsing pipeline description '%s'",
      pipeline_description);

  element = priv_gst_parse_launch (pipeline_description, &myerror, context,
      flags);

  /* don't return partially constructed pipeline if FATAL_ERRORS was given */
  if (G_UNLIKELY (myerror != NULL && element != NULL)) {
    if ((flags & GST_PARSE_FLAG_FATAL_ERRORS)) {
      gst_object_unref (element);
      element = NULL;
    }
  }

  if (myerror)
    g_propagate_error (error, myerror);

  return element;
#else
  gchar *msg;

  GST_WARNING ("Disabled API called");

  msg = gst_error_get_message (GST_CORE_ERROR, GST_CORE_ERROR_DISABLED);
  g_set_error (error, GST_CORE_ERROR, GST_CORE_ERROR_DISABLED, "%s", msg);
  g_free (msg);

  return NULL;
#endif
}

-----------------------------------------grammer.tab.c------------------------------------------------------

GstElement *
priv_gst_parse_launch (const gchar *str, GError **error, GstParseContext *ctx,
    GstParseFlags flags)
{
  graph_t g;
  gchar *dstr;
  GSList *walk;
  GstElement *ret;
  yyscan_t scanner;

  g_return_val_if_fail (str != NULL, NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  g.chain = NULL;
  g.links = NULL;
  g.error = error;
  g.ctx = ctx;
  g.flags = flags;

  dstr = g_strdup (str);
  priv_gst_parse_yylex_init (&scanner);
  priv_gst_parse_yy_scan_string (dstr, scanner);

  if (yyparse (scanner, &g) != 0) {
    SET_ERROR (error, GST_PARSE_ERROR_SYNTAX,
        "Unrecoverable syntax error while parsing pipeline %s", str);

    priv_gst_parse_yylex_destroy (scanner);
    g_free (dstr);

    goto error1;
  }
  priv_gst_parse_yylex_destroy (scanner);
  g_free (dstr);


  /* ensure chain is not NULL */
  if (!g.chain){
    g.chain=gst_parse_chain_new ();
    g.chain->elements=NULL;
    g.chain->first.element=NULL;
    g.chain->first.name=NULL;
    g.chain->first.pads=NULL;
    g.chain->last.element=NULL;
    g.chain->last.name=NULL;
    g.chain->last.pads=NULL;
  };

  /* ensure elements is not empty */
  if(!g.chain->elements){
    g.chain->elements= g_slist_prepend (NULL, NULL);
  };

  /* put all elements in our bin if necessary */
  if(g.chain->elements->next){
    GstBin *bin;
    if (flags & GST_PARSE_FLAG_PLACE_IN_BIN)
      bin = GST_BIN (gst_element_factory_make ("bin", NULL));
    else
      bin = GST_BIN (gst_element_factory_make ("pipeline", NULL));
    g_assert (bin);

    for (walk = g.chain->elements; walk; walk = walk->next) {
      if (walk->data != NULL)
        gst_bin_add (bin, GST_ELEMENT (walk->data));
    }
    g_slist_free (g.chain->elements);
    g.chain->elements = g_slist_prepend (NULL, bin);
  }

  ret = (GstElement *) g.chain->elements->data;
  g_slist_free (g.chain->elements);
  g.chain->elements=NULL;
  gst_parse_free_chain (g.chain);
  g.chain = NULL;


  /* resolve and perform links */
  for (walk = g.links; walk; walk = walk->next) {
    link_t *l = (link_t *) walk->data;
    int err;
    err=gst_resolve_reference( &(l->src), ret);
    if (err) {
       if(-1==err){
          SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
              "No src-element named \"%s\" - omitting link", l->src.name);
       }else{
          /* probably a missing element which we've handled already */
          SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
              "No src-element found - omitting link");
       }
       gst_parse_free_link (l);
       continue;
    }

    err=gst_resolve_reference( &(l->sink), ret);
    if (err) {
       if(-1==err){
          SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
              "No sink-element named \"%s\" - omitting link", l->src.name);
       }else{
          /* probably a missing element which we've handled already */
          SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
              "No sink-element found - omitting link");
       }
       gst_parse_free_link (l);
       continue;
    }
    gst_parse_perform_link (l, &g);
  }
  g_slist_free (g.links);

out:
#ifdef __GST_PARSE_TRACE
  GST_CAT_DEBUG (GST_CAT_PIPELINE,
      "TRACE: %u strings, %u chains and %u links left", __strings, __chains,
      __links);
  if (__strings || __chains || __links) {
    g_warning ("TRACE: %u strings, %u chains and %u links left", __strings,
        __chains, __links);
  }
#endif /* __GST_PARSE_TRACE */

  return ret;

error1:
  if (g.chain) {
    gst_parse_free_chain (g.chain);
    g.chain=NULL;
  }

  g_slist_foreach (g.links, (GFunc)gst_parse_free_link, NULL);
  g_slist_free (g.links);

  if (error)
    g_assert (*error);
  ret = NULL;

  goto out;
}

自建pipeline

#include <gst/gst.h>

int
main (int argc, char *argv[])
{
  GstElement *pipeline, *source, *sink;
  GstBus *bus;
  GstMessage *msg;
  GstStateChangeReturn ret;

  /* Initialize GStreamer */
  gst_init (&argc, &argv);

  /* Create the elements */
  source = gst_element_factory_make ("videotestsrc", "source");
  sink = gst_element_factory_make ("autovideosink", "sink");

  /* Create the empty pipeline */
  pipeline = gst_pipeline_new ("test-pipeline");

  if (!pipeline || !source || !sink) {
    g_printerr ("Not all elements could be created.\n");
    return -1;
  }

  /* Build the pipeline */
  gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
  if (gst_element_link (source, sink) != TRUE) {
    g_printerr ("Elements could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
  }

  /* Modify the source's properties */
  g_object_set (source, "pattern", 0, NULL);

  /* Start playing */
  ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
  if (ret == GST_STATE_CHANGE_FAILURE) {
    g_printerr ("Unable to set the pipeline to the playing state.\n");
    gst_object_unref (pipeline);
    return -1;
  }

  /* Wait until error or EOS */
  bus = gst_element_get_bus (pipeline);
  msg =
      gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
      GST_MESSAGE_ERROR | GST_MESSAGE_EOS);

  /* Parse message */
  if (msg != NULL) {
    GError *err;
    gchar *debug_info;

    switch (GST_MESSAGE_TYPE (msg)) {
      case GST_MESSAGE_ERROR:
        gst_message_parse_error (msg, &err, &debug_info);
        g_printerr ("Error received from element %s: %s\n",
            GST_OBJECT_NAME (msg->src), err->message);
        g_printerr ("Debugging information: %s\n",
            debug_info ? debug_info : "none");
        g_clear_error (&err);
        g_free (debug_info);
        break;
      case GST_MESSAGE_EOS:
        g_print ("End-Of-Stream reached.\n");
        break;
      default:
        /* We should not reach here because we only asked for ERRORs and EOS */
        g_printerr ("Unexpected message received.\n");
        break;
    }
    gst_message_unref (msg);
  }

  /* Free resources */
  gst_object_unref (bus);
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);
  return 0;
}

动态pipeline

#include <gst/gst.h>

/* Structure to contain all our information, so we can pass it to callbacks */
typedef struct _CustomData {
  GstElement *pipeline;
  GstElement *source;
  GstElement *convert;
  GstElement *resample;
  GstElement *sink;
} CustomData;

/* Handler for the pad-added signal */
static void pad_added_handler (GstElement *src, GstPad *pad, CustomData *data);

int main(int argc, char *argv[]) {
  CustomData data;
  GstBus *bus;
  GstMessage *msg;
  GstStateChangeReturn ret;
  gboolean terminate = FALSE;

  /* Initialize GStreamer */
  gst_init (&argc, &argv);

  /* Create the elements */
  data.source = gst_element_factory_make ("uridecodebin", "source");
  data.convert = gst_element_factory_make ("audioconvert", "convert");
  data.resample = gst_element_factory_make ("audioresample", "resample");
  data.sink = gst_element_factory_make ("autoaudiosink", "sink");

  /* Create the empty pipeline */
  data.pipeline = gst_pipeline_new ("test-pipeline");

  if (!data.pipeline || !data.source || !data.convert || !data.resample || !data.sink) {
    g_printerr ("Not all elements could be created.\n");
    return -1;
  }

  /* Build the pipeline. Note that we are NOT linking the source at this
   * point. We will do it later. */
  gst_bin_add_many (GST_BIN (data.pipeline), data.source, data.convert, data.resample, data.sink, NULL);
  if (!gst_element_link_many (data.convert, data.resample, data.sink, NULL)) {
    g_printerr ("Elements could not be linked.\n");
    gst_object_unref (data.pipeline);
    return -1;
  }

  /* Set the URI to play */
  g_object_set (data.source, "uri", "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);

  /* Connect to the pad-added signal */
  g_signal_connect (data.source, "pad-added", G_CALLBACK (pad_added_handler), &data);

  /* Start playing */
  ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
  if (ret == GST_STATE_CHANGE_FAILURE) {
    g_printerr ("Unable to set the pipeline to the playing state.\n");
    gst_object_unref (data.pipeline);
    return -1;
  }

  /* Listen to the bus */
  bus = gst_element_get_bus (data.pipeline);
  do {
    msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
        GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS);

    /* Parse message */
    if (msg != NULL) {
      GError *err;
      gchar *debug_info;

      switch (GST_MESSAGE_TYPE (msg)) {
        case GST_MESSAGE_ERROR:
          gst_message_parse_error (msg, &err, &debug_info);
          g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
          g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
          g_clear_error (&err);
          g_free (debug_info);
          terminate = TRUE;
          break;
        case GST_MESSAGE_EOS:
          g_print ("End-Of-Stream reached.\n");
          terminate = TRUE;
          break;
        case GST_MESSAGE_STATE_CHANGED:
          /* We are only interested in state-changed messages from the pipeline */
          if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data.pipeline)) {
            GstState old_state, new_state, pending_state;
            gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
            g_print ("Pipeline state changed from %s to %s:\n",
                gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));
          }
          break;
        default:
          /* We should not reach here */
          g_printerr ("Unexpected message received.\n");
          break;
      }
      gst_message_unref (msg);
    }
  } while (!terminate);

  /* Free resources */
  gst_object_unref (bus);
  gst_element_set_state (data.pipeline, GST_STATE_NULL);
  gst_object_unref (data.pipeline);
  return 0;
}

/* This function will be called by the pad-added signal */
static void pad_added_handler (GstElement *src, GstPad *new_pad, CustomData *data) {
  GstPad *sink_pad = gst_element_get_static_pad (data->convert, "sink");
  GstPadLinkReturn ret;
  GstCaps *new_pad_caps = NULL;
  GstStructure *new_pad_struct = NULL;
  const gchar *new_pad_type = NULL;

  g_print ("Received new pad '%s' from '%s':\n", GST_PAD_NAME (new_pad), GST_ELEMENT_NAME (src));

  /* If our converter is already linked, we have nothing to do here */
  if (gst_pad_is_linked (sink_pad)) {
    g_print ("We are already linked. Ignoring.\n");
    goto exit;
  }

  /* Check the new pad's type */
  new_pad_caps = gst_pad_get_current_caps (new_pad);
  new_pad_struct = gst_caps_get_structure (new_pad_caps, 0);
  new_pad_type = gst_structure_get_name (new_pad_struct);
  if (!g_str_has_prefix (new_pad_type, "audio/x-raw")) {
    g_print ("It has type '%s' which is not raw audio. Ignoring.\n", new_pad_type);
    goto exit;
  }

  /* Attempt the link */
  ret = gst_pad_link (new_pad, sink_pad);
  if (GST_PAD_LINK_FAILED (ret)) {
    g_print ("Type is '%s' but link failed.\n", new_pad_type);
  } else {
    g_print ("Link succeeded (type '%s').\n", new_pad_type);
  }

exit:
  /* Unreference the new pad's caps, if we got them */
  if (new_pad_caps != NULL)
    gst_caps_unref (new_pad_caps);

  /* Unreference the sink pad */
  gst_object_unref (sink_pad);
}

插件实现

构建目录

fuqiang@fuqiang-VivoBook:~/workspace/gstreamer/subprojects/gst-plugins-bad/gst/myfilter$ tree
.
├── meson.build
├── myfilter.c
└── myfilter.h

实现meson.build

myfilter_sources = [
  'myfilter.c',
]

gstmyfilter = library('gstmyfilter',
  myfilter_sources,
  c_args : gst_plugins_bad_args,
  include_directories : [configinc],
  dependencies : [gstbase_dep, gsttag_dep],
  install : true,
  install_dir : plugins_install_dir,
)
pkgconfig.generate(gstmyfilter, install_dir : plugins_pkgconfig_install_dir)
plugins += [gstmyfilter]

添加编译相关选项

fuqiang@fuqiang-VivoBook:~/workspace/gstreamer/subprojects/gst-plugins-bad$ git diff
diff --git a/subprojects/gst-plugins-bad/gst/meson.build b/subprojects/gst-plugins-bad/gst/meson.build
index 9cf62db983..063fff3aef 100644
--- a/subprojects/gst-plugins-bad/gst/meson.build
+++ b/subprojects/gst-plugins-bad/gst/meson.build
@@ -12,7 +12,7 @@ foreach plugin : ['accurip', 'adpcmdec', 'adpcmenc', 'aiff', 'asfmux',
                   'segmentclip', 'siren', 'smooth', 'speed', 'subenc', 'switchbin',
                   'timecode', 'transcode', 'videofilters',
                   'videoframe_audiolevel', 'videoparsers', 'videosignal',
-                  'vmnc', 'y4m']
+                  'vmnc', 'y4m', 'myfilter']
   if not get_option(plugin).disabled()
     subdir(plugin)
   endif
diff --git a/subprojects/gst-plugins-bad/meson_options.txt b/subprojects/gst-plugins-bad/meson_options.txt
index b347dcb27b..320e4182c1 100644
--- a/subprojects/gst-plugins-bad/meson_options.txt
+++ b/subprojects/gst-plugins-bad/meson_options.txt
@@ -134,6 +134,7 @@ option('onnx', type : 'feature', value : 'auto', description : 'ONNX neural netw
 option('openal', type : 'feature', value : 'auto', description : 'OpenAL plugin')
 option('openexr', type : 'feature', value : 'auto', description : 'OpenEXR plugin')
 option('openh264', type : 'feature', value : 'auto', description : 'H.264 video codec plugin')
+option('myfilter', type : 'feature', value : 'auto', description : 'test filter')
 option('openjpeg', type : 'feature', value : 'auto', description : 'JPEG2000 image codec plugin')
 option('openmpt', type : 'feature', value : 'auto', description : 'OpenMPT module music library plugin')
 option('openni2', type : 'feature', value : 'auto', description : 'OpenNI2 library plugin')

实现头文件

#ifndef __GST_MYFILTER_H__
#define __GST_MYFILTER_H__

#include <gst/gst.h>

G_BEGIN_DECLS

#define GST_TYPE_MYFILTER (gst_my_filter_get_type())
G_DECLARE_FINAL_TYPE (GstMyFilter, gst_my_filter,
    GST, PLUGIN_TEMPLATE, GstElement)

struct _GstMyFilter
{
  GstElement element;
  GstPad *sinkpad, *srcpad;

  gboolean silent;
};

G_END_DECLS

#endif /* __GST_MYFILTER_H__ */

实现C文件

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gst/gst.h>

#include "gstmyfilter.h"

GST_DEBUG_CATEGORY_STATIC (gst_my_filter_debug);
#define GST_CAT_DEFAULT gst_my_filter_debug

/* Filter signals and args */
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

enum
{
  PROP_0,
  PROP_SILENT
};

/* the capabilities of the inputs and outputs.
 *
 * describe the real formats here.
 */
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("ANY")
    );

static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("ANY")
    );

#define gst_my_filter_parent_class parent_class
G_DEFINE_TYPE (GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT);


static void gst_my_filter_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_my_filter_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);

static gboolean gst_my_filter_sink_event (GstPad * pad,
    GstObject * parent, GstEvent * event);
static GstFlowReturn gst_my_filter_chain (GstPad * pad,
    GstObject * parent, GstBuffer * buf);

/* GObject vmethod implementations */

/* initialize the myfilter's class */
static void
gst_my_filter_class_init (GstMyFilterClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;

  gobject_class->set_property = gst_my_filter_set_property;
  gobject_class->get_property = gst_my_filter_get_property;

  g_object_class_install_property (gobject_class, PROP_SILENT,
      g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?",
          FALSE, G_PARAM_READWRITE));

  gst_element_class_set_details_simple (gstelement_class,
      "MyFilter",
      "FIXME:Generic",
      "FIXME:Generic Template Element", "fuqiang <<user@hostname.org>>");

  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&src_factory));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&sink_factory));
}

/* initialize the new element
 * instantiate pads and add them to element
 * set pad calback functions
 * initialize instance structure
 */
static void
gst_my_filter_init (GstMyFilter * filter)
{
  filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
  gst_pad_set_event_function (filter->sinkpad,
      GST_DEBUG_FUNCPTR (gst_my_filter_sink_event));
  gst_pad_set_chain_function (filter->sinkpad,
      GST_DEBUG_FUNCPTR (gst_my_filter_chain));
  GST_PAD_SET_PROXY_CAPS (filter->sinkpad);
  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);

  filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
  GST_PAD_SET_PROXY_CAPS (filter->srcpad);
  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);

  filter->silent = FALSE;
}

static void
gst_my_filter_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstMyFilter *filter = GST_MYFILTER (object);

  switch (prop_id) {
    case PROP_SILENT:
      filter->silent = g_value_get_boolean (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_my_filter_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstMyFilter *filter = GST_MYFILTER (object);

  switch (prop_id) {
    case PROP_SILENT:
      g_value_set_boolean (value, filter->silent);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

/* GstElement vmethod implementations */

/* this function handles sink events */
static gboolean
gst_my_filter_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
{
  GstMyFilter *filter;
  gboolean ret;

  filter = GST_MYFILTER (parent);

  GST_LOG_OBJECT (filter, "Received %s event: %" GST_PTR_FORMAT,
      GST_EVENT_TYPE_NAME (event), event);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);
      /* do something with the caps */

      /* and forward */
      ret = gst_pad_event_default (pad, parent, event);
      break;
    }
    default:
      ret = gst_pad_event_default (pad, parent, event);
      break;
  }
  return ret;
}

/* chain function
 * this function does the actual processing
 */
static GstFlowReturn
gst_my_filter_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
  GstMyFilter *filter;

  filter = GST_MYFILTER (parent);

  if (filter->silent == FALSE)
    g_print ("I'm plugged, therefore I'm in.\n");

  /* just push out the incoming buffer without touching it */
  return gst_pad_push (filter->srcpad, buf);
}


/* entry point to initialize the plug-in
 * initialize the plug-in itself
 * register the element factories and other features
 */
static gboolean
myfilter_init (GstPlugin * myfilter)
{
  /* debug category for filtering log messages
   *
   * exchange the string 'Template myfilter' with your description
   */
  GST_DEBUG_CATEGORY_INIT (gst_my_filter_debug, "myfilter",
      0, "Template myfilter");

  return gst_element_register (myfilter, "myfilter", GST_RANK_NONE,
      GST_TYPE_MYFILTER);
}

/* PACKAGE: this is usually set by meson depending on some _INIT macro
 * in meson.build and then written into and defined in config.h, but we can
 * just set it ourselves here in case someone doesn't use meson to
 * compile this code. GST_PLUGIN_DEFINE needs PACKAGE to be defined.
 */
#ifndef PACKAGE
#define PACKAGE "myfirstmyfilter"
#endif

/* gstreamer looks for this structure to register myfilters
 *
 * exchange the string 'Template myfilter' with your myfilter description
 */
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    myfilter,
    "Template myfilter",
    myfilter_init,
    PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

构建编译安装

meson --reconfigure -Dgst-plugins-bad:myfilter=enabled out
cd out
ninja
sudo ninja install

查看插件情况

fuqiang@fuqiang-VivoBook:~/workspace/gstreamer/subprojects/gst-plugins-bad/gst/myfilter$ gst-inspect-1.0 myfilter
Factory Details:
  Rank                     marginal (64)
  Long-name                obentul example plugin
  Klass                    Example/FirstExample
  Description              Shows the basic structure of a plugin
  Author                   fuqiang <<user@hostname.org>>

Plugin Details:
  Name                     myfilter
  Description              My filter plugin
  Filename                 /usr/local/lib/x86_64-linux-gnu/gstreamer-1.0/libgstmyfilter.so
  Version                  1.0
  License                  LGPL
  Source module            myfilter
  Binary package           GStreamer template Plug-ins
  Origin URL               http://gstreamer.net/

GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstMyFilter

Pad Templates:
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      ANY
  
  SRC template: 'src'
    Availability: Always
    Capabilities:
      ANY

Element has no clocking capabilities.
Element has no URI handling capabilities.

Pads:
  SRC: 'src_%u'
    Pad Template: 'src'
  SINK: 'sink_%u'
    Pad Template: 'sink'

Element Properties:
  name                : The name of the object
                        flags: 可读, 可写, 0x2000
                        String. Default: "myfilter0"
  parent              : The parent of the object
                        flags: 可读, 可写, 0x2000
                        Object of type "GstObject

运行

gst-launch-1.0 videotestsrc ! autovideosink
gst-launch-1.0 videotestsrc ! myfilter ! autovideosink

经典插件代码分析

openh264

openh264是开源的H.264编解码器,Gstreamer中集成了openh264的源码,并且实现了插件对接该编码器

编译

meson --reconfigure -Dgst-plugins-bad:openh264=enabled out

使用

gst-launch-1.0 filesrc location='/home/fuqiang/Videos/1080P.mov'  ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! openh264dec ! videoconvert ! videoscale ! autovideosink

openh264源码

openh264源码的代码结构如下:

fuqiang@fuqiang-virtual:~/workspace/gstreamer/subprojects/openh264$ tree -L 1
.
├── autotest
├── build
├── codec
├── code-coverage.sh
├── CODING_STYLE
├── CONTRIBUTORS
├── docs
├── gmpopenh264.info
├── include
├── LICENSE
├── Makefile
├── meson.build
├── meson_options.txt
├── module
├── openh264.def
├── openh264.pc.in
├── openh264.rc
├── openh264.rc.template
├── README.md
├── RELEASES
├── res
├── run_Test.sh
├── subprojects
├── test
├── testbin
└── ut.def

10 directories, 16 files

openh264编译完成后的生成目录:

fuqiang@fuqiang-virtual:~/workspace/gstreamer/out/subprojects/openh264$ tree -L 3
.
├── codec
│   ├── api
│   │   └── svc
│   ├── common
│   │   ├── libcommon.a
│   │   └── libcommon.a.p
│   ├── console
│   │   ├── common
│   │   ├── dec
│   │   └── enc
│   ├── decoder
│   │   ├── libdecoder.a
│   │   └── libdecoder.a.p
│   ├── encoder
│   │   ├── libencoder.a
│   │   └── libencoder.a.p
│   └── processing
│       ├── libprocessing.a
│       └── libprocessing.a.p
├── include
│   └── wels
│       ├── codec_api.h
│       ├── codec_app_def.h
│       ├── codec_def.h
│       └── codec_ver.h
├── libopenh264.a
├── libopenh264.a.p
├── libopenh264.so -> libopenh264.so.6
├── libopenh264.so.2.1.1
├── libopenh264.so.2.1.1.p
│   └── libopenh264.so.2.1.1.symbols
├── libopenh264.so.6 -> libopenh264.so.2.1.1
├── openh264.pc
├── openh264-static.pc
└── test
    ├── api
    │   ├── test_api
    │   └── test_api.p
    ├── common
    │   ├── test_common
    │   └── test_common.p
    ├── decoder
    │   ├── test_decoder
    │   └── test_decoder.p
    ├── encoder
    │   ├── test_encoder
    │   └── test_encoder.p
    └── processing
        ├── test_processing
        └── test_processing.p

30 directories, 20 files

openh264插件源码

GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstVideoDecoder
                         +----GstOpenh264Dec

openh264插件源码如下:

fuqiang@fuqiang-virtual:~/workspace/gstreamer/subprojects/gst-plugins-bad/ext/openh264$ tree
.
├── gstopenh264dec.cpp
├── gstopenh264dec.h
├── gstopenh264element.c
├── gstopenh264elements.h
├── gstopenh264enc.cpp
├── gstopenh264enc.h
├── gstopenh264plugin.c
└── meson.build

0 directories, 8 files

编译完成后生成目录:

fuqiang@fuqiang-virtual:~/workspace/gstreamer/out/subprojects/gst-plugins-bad/ext/openh264$ tree
.
├── libgstopenh264.so
└── libgstopenh264.so.p
    ├── gstopenh264dec.cpp.o
    ├── gstopenh264element.c.o
    ├── gstopenh264enc.cpp.o
    └── gstopenh264plugin.c.o

1 directory, 5 files

注册插件gstopenh264plugin.c:

static gboolean
plugin_init (GstPlugin * plugin)
{
  gboolean ret = FALSE;

  ret |= GST_ELEMENT_REGISTER (openh264dec, plugin);
  ret |= GST_ELEMENT_REGISTER (openh264enc, plugin);

  return ret;
}

GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    openh264,
    "OpenH264 encoder/decoder plugin",
    plugin_init, VERSION, "BSD", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

解码头文件:

#include <wels/codec_api.h>
#include <wels/codec_app_def.h>
#include <wels/codec_def.h>

G_BEGIN_DECLS

#define GST_TYPE_OPENH264DEC          (gst_openh264dec_get_type())
#define GST_OPENH264DEC(obj)          (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OPENH264DEC,GstOpenh264Dec))
#define GST_OPENH264DEC_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OPENH264DEC,GstOpenh264DecClass))
#define GST_IS_OPENH264DEC(obj)       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OPENH264DEC))
#define GST_IS_OPENH264DEC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OPENH264DEC))

typedef struct _GstOpenh264Dec GstOpenh264Dec;
typedef struct _GstOpenh264DecClass GstOpenh264DecClass;

struct _GstOpenh264Dec
{
  GstVideoDecoder base_openh264dec;

  /*< private >*/
  ISVCDecoder *decoder;
  GstVideoCodecState *input_state;
  guint width, height;
};

struct _GstOpenh264DecClass
{
    GstVideoDecoderClass base_openh264dec_class;
};

GType gst_openh264dec_get_type(void);

G_END_DECLS

源码分析:

...
/* prototypes */
static gboolean gst_openh264dec_start (GstVideoDecoder * decoder);
static gboolean gst_openh264dec_stop (GstVideoDecoder * decoder);

static gboolean gst_openh264dec_set_format (GstVideoDecoder * decoder,
    GstVideoCodecState * state);
static gboolean gst_openh264dec_reset (GstVideoDecoder * decoder,
    gboolean hard);
static GstFlowReturn gst_openh264dec_finish (GstVideoDecoder * decoder);
static GstFlowReturn gst_openh264dec_handle_frame (GstVideoDecoder * decoder,
    GstVideoCodecFrame * frame);
static gboolean gst_openh264dec_decide_allocation (GstVideoDecoder * decoder,
    GstQuery * query);
static gboolean openh264dec_element_init (GstPlugin * plugin);

...
/* pad templates */
static GstStaticPadTemplate gst_openh264dec_sink_template 
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS
    ("video/x-h264, stream-format=(string)byte-stream, alignment=(string)au, "
      SUPPORTED_PROFILE_STR
    ));

static GstStaticPadTemplate gst_openh264dec_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("I420")));

/* class initialization */

G_DEFINE_TYPE_WITH_CODE (GstOpenh264Dec, gst_openh264dec,
    GST_TYPE_VIDEO_DECODER,
    GST_DEBUG_CATEGORY_INIT (gst_openh264dec_debug_category, "openh264dec", 0,
        "debug category for openh264dec element"));
GST_ELEMENT_REGISTER_DEFINE_CUSTOM (openh264dec, openh264dec_element_init);

static void
gst_openh264dec_class_init (GstOpenh264DecClass * klass)
{
  GstVideoDecoderClass *video_decoder_class = GST_VIDEO_DECODER_CLASS (klass);

  gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
      &gst_openh264dec_sink_template);
  gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
      &gst_openh264dec_src_template);

  gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
      "OpenH264 video decoder", "Decoder/Video", "OpenH264 video decoder",
      "Ericsson AB, http://www.ericsson.com");

  video_decoder_class->start = GST_DEBUG_FUNCPTR (gst_openh264dec_start);
  video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_openh264dec_stop);

  video_decoder_class->set_format =
      GST_DEBUG_FUNCPTR (gst_openh264dec_set_format);
  video_decoder_class->reset = GST_DEBUG_FUNCPTR (gst_openh264dec_reset);
  video_decoder_class->finish = GST_DEBUG_FUNCPTR (gst_openh264dec_finish);
  video_decoder_class->handle_frame =
      GST_DEBUG_FUNCPTR (gst_openh264dec_handle_frame);
  video_decoder_class->decide_allocation =
      GST_DEBUG_FUNCPTR (gst_openh264dec_decide_allocation);
}

static void
gst_openh264dec_init (GstOpenh264Dec * openh264dec)
{
  openh264dec->decoder = NULL;

  gst_video_decoder_set_packetized (GST_VIDEO_DECODER (openh264dec), TRUE);
  gst_video_decoder_set_needs_format (GST_VIDEO_DECODER (openh264dec), TRUE);
}

...
static gboolean
gst_openh264dec_start (GstVideoDecoder * decoder)
{
  GstOpenh264Dec *openh264dec = GST_OPENH264DEC (decoder);
  gint ret;
  SDecodingParam dec_param = { 0 };

  if (openh264dec->decoder != NULL) {
    openh264dec->decoder->Uninitialize ()
    WelsDestroyDecoder (openh264dec->decoder);
    openh264dec->decoder = NULL;
  }
  WelsCreateDecoder (&(openh264dec->decoder));


  dec_param.uiTargetDqLayer = 255;
  dec_param.eEcActiveIdc = ERROR_CON_FRAME_COPY;
#if OPENH264_MAJOR == 1 && OPENH264_MINOR < 6
  dec_param.eOutputColorFormat = videoFormatI420;
#endif
  dec_param.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_AVC;

  ret = openh264dec->decoder->Initialize (&dec_param);

  GST_DEBUG_OBJECT (openh264dec,
      "openh264_dec_start called, openh264dec %sinitialized OK!",
      (ret != cmResultSuccess) ? "NOT " : "");

  return (ret == cmResultSuccess);
}

static gboolean
gst_openh264dec_stop (GstVideoDecoder * decoder)
{
  GstOpenh264Dec *openh264dec = GST_OPENH264DEC (decoder);

  if (openh264dec->decoder) {
    openh264dec->decoder->Uninitialize ();
    WelsDestroyDecoder (openh264dec->decoder);
    openh264dec->decoder = NULL;
  }

  if (openh264dec->input_state) {
    gst_video_codec_state_unref (openh264dec->input_state);
    openh264dec->input_state = NULL;
  }
  openh264dec->width = openh264dec->height = 0;

  return TRUE;
}

static gboolean
gst_openh264dec_set_format (GstVideoDecoder * decoder,
    GstVideoCodecState * state)
{
  GstOpenh264Dec *openh264dec = GST_OPENH264DEC (decoder);

  GST_DEBUG_OBJECT (openh264dec, "input caps: %" GST_PTR_FORMAT, state->caps);

  if (openh264dec->input_state) {
    gst_video_codec_state_unref (openh264dec->input_state);
    openh264dec->input_state = NULL;
  }
  openh264dec->input_state = gst_video_codec_state_ref (state);

  return TRUE;
}

static gboolean
gst_openh264dec_reset (GstVideoDecoder * decoder, gboolean hard)
{
  GstOpenh264Dec *openh264dec = GST_OPENH264DEC (decoder);

  GST_DEBUG_OBJECT (openh264dec, "reset");

  return TRUE;
}

static GstFlowReturn
gst_openh264dec_handle_frame (GstVideoDecoder * decoder,
    GstVideoCodecFrame * frame)
{
  GstOpenh264Dec *openh264dec = GST_OPENH264DEC (decoder);
  GstMapInfo map_info;
  GstVideoCodecState *state;
  SBufferInfo dst_buf_info;
  DECODING_STATE ret;
  guint8 *yuvdata[3];
  GstFlowReturn flow_status;
  GstVideoFrame video_frame;
  guint actual_width, actual_height;
  guint i;
  guint8 *p;
  guint row_stride, component_width, component_height, src_width, row;

  if (frame == NULL) {
#if OPENH264_VERSION_CHECK (1,9)
    /* Called with no videoframe for EOS logic. Drain out */
    int end_of_stream = 1;
    memset (&dst_buf_info, 0, sizeof (SBufferInfo));

    openh264dec->decoder->SetOption (DECODER_OPTION_END_OF_STREAM,
        &end_of_stream);
    ret = openh264dec->decoder->FlushFrame (yuvdata, &dst_buf_info);

    if (ret != dsErrorFree || dst_buf_info.iBufferStatus != 1) {
      GST_DEBUG_OBJECT (decoder, "No more frames to retrieve at EOS");
      return GST_FLOW_EOS;
    }
#else
    return GST_FLOW_EOS;
#endif
  } else {
    if (!gst_buffer_map (frame->input_buffer, &map_info, GST_MAP_READ)) {
      GST_ERROR_OBJECT (openh264dec, "Cannot map input buffer!");
      gst_video_codec_frame_unref (frame);
      return GST_FLOW_ERROR;
    }

    GST_LOG_OBJECT (openh264dec, "handle frame, 1st NAL type %d",
        map_info.size > 4 ? map_info.data[4] & 0x1f : -1);

    memset (&dst_buf_info, 0, sizeof (SBufferInfo));
    /* Use the unsigned long long OpenH264 timestamp to store the system_frame_number
     * to track the original frame through any OpenH264 reordering */
    dst_buf_info.uiInBsTimeStamp = frame->system_frame_number;

    GST_LOG_OBJECT (decoder, "Submitting frame with PTS %" GST_TIME_FORMAT
        " and frame ref %" G_GUINT64_FORMAT,
        GST_TIME_ARGS (frame->pts), (guint64) frame->system_frame_number);

    ret =
        openh264dec->decoder->DecodeFrameNoDelay (map_info.data, map_info.size,
        yuvdata, &dst_buf_info);
    gst_buffer_unmap (frame->input_buffer, &map_info);

    if (ret != dsErrorFree) {
      /* Request a key unit from upstream */
      GST_DEBUG_OBJECT (openh264dec, "Requesting a key unit");

      gst_video_decoder_request_sync_point (decoder, frame,
          (GstVideoDecoderRequestSyncPointFlags) 0);

      GST_LOG_OBJECT (openh264dec, "error decoding nal, return code: %d", ret);
      gst_video_codec_frame_unref (frame);

      /* Get back the frame that was reported as errored */
      frame =
          gst_video_decoder_get_frame (decoder, dst_buf_info.uiOutYuvTimeStamp);
      if (frame) {
        GST_LOG_OBJECT (decoder,
            "Dropping errored frame ref %" G_GUINT64_FORMAT,
            (guint64) dst_buf_info.uiOutYuvTimeStamp);
        return gst_video_decoder_drop_frame (decoder, frame);
      }
      return GST_FLOW_OK;
    }

    gst_video_codec_frame_unref (frame);
    frame = NULL;

    /* No output available yet */
    if (dst_buf_info.iBufferStatus != 1) {
      GST_LOG_OBJECT (decoder, "No buffer decoded yet");
      return GST_FLOW_OK;
    }
  }

  GST_LOG_OBJECT (decoder, "Got back frame with frame ref %" G_GUINT64_FORMAT,
      (guint64) dst_buf_info.uiOutYuvTimeStamp);

  /* OpenH264 lets us pass an int reference through
   * so we can retrieve the input frame now */
  frame = gst_video_decoder_get_frame (decoder, dst_buf_info.uiOutYuvTimeStamp);
  if (!frame) {
    /* Where did our frame go? This is a reference tracking error. */
    GST_WARNING_OBJECT (decoder,
        "Failed to look up frame ref %" G_GUINT64_FORMAT,
        (guint64) dst_buf_info.uiOutYuvTimeStamp);
    return GST_FLOW_OK;
  }

  actual_width = dst_buf_info.UsrData.sSystemBuffer.iWidth;
  actual_height = dst_buf_info.UsrData.sSystemBuffer.iHeight;

  if (!gst_pad_has_current_caps (GST_VIDEO_DECODER_SRC_PAD (openh264dec))
      || actual_width != openh264dec->width
      || actual_height != openh264dec->height) {
    state =
        gst_video_decoder_set_output_state (decoder, GST_VIDEO_FORMAT_I420,
        actual_width, actual_height, openh264dec->input_state);
    openh264dec->width = actual_width;
    openh264dec->height = actual_height;

    if (!gst_video_decoder_negotiate (decoder)) {
      GST_ERROR_OBJECT (openh264dec,
          "Failed to negotiate with downstream elements");
      gst_video_codec_state_unref (state);
      gst_video_codec_frame_unref (frame);
      return GST_FLOW_NOT_NEGOTIATED;
    }
  } else {
    state = gst_video_decoder_get_output_state (decoder);
  }

  flow_status = gst_video_decoder_allocate_output_frame (decoder, frame);
  if (flow_status != GST_FLOW_OK) {
    gst_video_codec_state_unref (state);
    gst_video_codec_frame_unref (frame);
    return flow_status;
  }

  if (!gst_video_frame_map (&video_frame, &state->info, frame->output_buffer,
          GST_MAP_WRITE)) {
    GST_ERROR_OBJECT (openh264dec, "Cannot map output buffer!");
    gst_video_codec_state_unref (state);
    gst_video_codec_frame_unref (frame);
    return GST_FLOW_ERROR;
  }

  for (i = 0; i < 3; i++) {
    p = GST_VIDEO_FRAME_COMP_DATA (&video_frame, i);
    row_stride = GST_VIDEO_FRAME_COMP_STRIDE (&video_frame, i);
    component_width = GST_VIDEO_FRAME_COMP_WIDTH (&video_frame, i);
    component_height = GST_VIDEO_FRAME_COMP_HEIGHT (&video_frame, i);
    src_width =
        i <
        1 ? dst_buf_info.UsrData.sSystemBuffer.
        iStride[0] : dst_buf_info.UsrData.sSystemBuffer.iStride[1];
    for (row = 0; row < component_height; row++) {
      memcpy (p, yuvdata[i], component_width);
      p += row_stride;
      yuvdata[i] += src_width;
    }
  }
  gst_video_codec_state_unref (state);
  gst_video_frame_unmap (&video_frame);

  return gst_video_decoder_finish_frame (decoder, frame);
}

static GstFlowReturn
gst_openh264dec_finish (GstVideoDecoder * decoder)
{
  GstOpenh264Dec *openh264dec = GST_OPENH264DEC (decoder);

  GST_DEBUG_OBJECT (openh264dec, "finish");

  /* Decoder not negotiated yet */
  if (openh264dec->width == 0)
    return GST_FLOW_OK;

  /* Drain all pending frames */
  while ((gst_openh264dec_handle_frame (decoder, NULL)) == GST_FLOW_OK);

  return GST_FLOW_OK;
}

static gboolean
gst_openh264dec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
{
  GstVideoCodecState *state;
  GstBufferPool *pool;
  guint size, min, max;
  GstStructure *config;

  if (!GST_VIDEO_DECODER_CLASS (gst_openh264dec_parent_class)->decide_allocation
      (decoder, query))
    return FALSE;

  state = gst_video_decoder_get_output_state (decoder);

  gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);

  config = gst_buffer_pool_get_config (pool);
  if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
    gst_buffer_pool_config_add_option (config,
        GST_BUFFER_POOL_OPTION_VIDEO_META);
  }

  gst_buffer_pool_set_config (pool, config);

  gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);

  gst_object_unref (pool);
  gst_video_codec_state_unref (state);

  return TRUE;
}

static gboolean
openh264dec_element_init (GstPlugin * plugin)
{
  if (openh264_element_init (plugin))
    return gst_element_register (plugin, "openh264dec", GST_RANK_MARGINAL,
        GST_TYPE_OPENH264DEC);

 GST_ERROR ("Incorrect library version loaded, expecting %s", g_strCodecVer);
 return FALSE;
}

音视频同步策略分析

  • 以音频时钟做为参考时钟(要求参考时钟上的时间是线性递增的)
  • 生成数据流时依据参考时钟上的时间给每个数据块都打上时间戳(一般包括开始时间和结束时间)
  • 在播放时,读取数据上的时间戳,同时参考当前参考时钟上的时间来安排播放
  • 如果数据块上的时间大于参考时钟的时间,则不急于播放,直到参考时钟达到数据块的开始时间
  • 如果数据块上的时间小于参考时钟的时间,则应”尽快”播放或者干脆”丢弃”该数据块,以使得播放赶上播放进度

 

audiosink同步播放:

  • 人对音频的敏感程度高于视频
  • 视频由一帧帧离散的视频帧构成,音频是连续的PCM数据流
  • 必须保证音频在播放的时候,能够严格的按照速率连续来进行,PCM流不能被延迟和打断
  • 软件调度操作在时间上必定会存在一定程度的误差,所以音频播放的时效性不能依靠软件保证,只能由硬件来准确完成
  • 音频播放时,只需要硬件从一个时刻有数据的buffer中去取数据,有硬件来保证播放的速率,软件只需要往这个buffer里面填写数据就可以

GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstBaseSink
                         +----GstAudioBaseSink
                               +----GstAudioSink
                                     +----GstAlsaSink

buffer传输流程:

static GstFlowReturn
gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
    gpointer obj, gboolean is_list)
{
  ...
  if (!is_list) {
    /* For buffer lists do not set last buffer for now. */
    gst_base_sink_set_last_buffer (basesink, GST_BUFFER_CAST (obj));
    gst_base_sink_set_last_buffer_list (basesink, NULL);

    if (bclass->render)
      ret = bclass->render (basesink, GST_BUFFER_CAST (obj));
  } else {
    GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj);

    if (bclass->render_list)
      ret = bclass->render_list (basesink, buffer_list);

    /* Set the first buffer and buffer list to be included in last sample */
    gst_base_sink_set_last_buffer (basesink, sync_buf);
    gst_base_sink_set_last_buffer_list (basesink, buffer_list);
  }
...


static GstFlowReturn
gst_audio_base_sink_render (GstBaseSink * bsink, GstBuffer * buf)
{
  ... //大量同步操作
  {
    written =
        gst_audio_ring_buffer_commit (ringbuf, &sample_offset,
        info.data + offset, samples, out_samples, &accum);

    if (out_samples > written) {
      out_samples -= written;
      accum = 0;
    } else
      break;

    samples -= written;
    offset += written * bpf;
  } while (TRUE);

-----------------------------------------------------------------------------------

static void
audioringbuffer_thread_func (GstAudioRingBuffer * buf)
{
  GstAudioSink *sink;
  GstAudioSinkClass *csink;
  GstAudioSinkRingBuffer *abuf = GST_AUDIO_SINK_RING_BUFFER_CAST (buf);
  WriteFunc writefunc;
  GstMessage *message;
  GValue val = { 0 };
  gpointer handle;

  sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf));
  csink = GST_AUDIO_SINK_GET_CLASS (sink);


  writefunc = csink->write;
  if (writefunc == NULL)
    goto no_function;

  while (TRUE) {
    gint left, len;
    guint8 *readptr;
    gint readseg;

    /* buffer must be started */
    if (gst_audio_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) {
      gint written;

      left = len;
      do {
        written = writefunc (sink, readptr, left);


        ...

        left -= written;
        readptr += written;
      } while (left > 0);
...



static gint
gst_alsasink_write (GstAudioSink * asink, gpointer data, guint length)
{
  GstAlsaSink *alsa;
  gint err;
  gint cptr;
  guint8 *ptr = data;

  alsa = GST_ALSA_SINK (asink);

  cptr = length / alsa->bpf;

  GST_ALSA_SINK_LOCK (asink);
  while (cptr > 0) {
    /* start by doing a blocking wait for free space. Set the timeout
     * to 4 times the period time */
    err = snd_pcm_wait (alsa->handle, (4 * alsa->period_time / 1000));
    if (err < 0) {
      GST_DEBUG_OBJECT (asink, "wait error, %d", err);
    } else {
      GST_DELAY_SINK_LOCK (asink);
      err = snd_pcm_writei (alsa->handle, ptr, cptr);
      GST_DELAY_SINK_UNLOCK (asink);
    }
    ..
  }

videosink同步播放:

GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstBaseSink
                         +----GstVideoSink
                               +----GstWaylandSink

buffer传输流程:

static GstFlowReturn
gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
    gpointer obj, gboolean is_list)
{
  ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (sync_buf),
      &late, &step_end);
  ...
  if (!is_list) {
    /* For buffer lists do not set last buffer for now. */
    gst_base_sink_set_last_buffer (basesink, GST_BUFFER_CAST (obj));
    gst_base_sink_set_last_buffer_list (basesink, NULL);

    if (bclass->render)
      ret = bclass->render (basesink, GST_BUFFER_CAST (obj));
  } else {
    GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj);

    if (bclass->render_list)
      ret = bclass->render_list (basesink, buffer_list);

    /* Set the first buffer and buffer list to be included in last sample */
    gst_base_sink_set_last_buffer (basesink, sync_buf);
    gst_base_sink_set_last_buffer_list (basesink, buffer_list);
  }
...


static GstFlowReturn
gst_video_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
{
  GstVideoSinkClass *klass;

  klass = GST_VIDEO_SINK_GET_CLASS (bsink);

  if (klass->show_frame == NULL) {
    if (parent_class->render != NULL)
      return parent_class->render (bsink, buf);
    else
      return GST_FLOW_OK;
  }

  return klass->show_frame (GST_VIDEO_SINK_CAST (bsink), buf);
}


static GstFlowReturn
gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
{
  ...
  

同步流程:

static GstFlowReturn
gst_base_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
  GstBaseSink *basesink;

  basesink = GST_BASE_SINK (parent);

  return gst_base_sink_chain_main (basesink, pad, buf, FALSE);
}


static GstFlowReturn
gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad, gpointer obj,
    gboolean is_list)
{
  GstFlowReturn result;

  if (G_UNLIKELY (basesink->pad_mode != GST_PAD_MODE_PUSH))
    goto wrong_mode;

  GST_BASE_SINK_PREROLL_LOCK (basesink);
  result = gst_base_sink_chain_unlocked (basesink, pad, obj, is_list);
  GST_BASE_SINK_PREROLL_UNLOCK (basesink);

done:
  return result;
}

static GstFlowReturn
gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
    gpointer obj, gboolean is_list)
{
  GstBaseSinkClass *bclass;
  GstBaseSinkPrivate *priv = basesink->priv;
  GstFlowReturn ret = GST_FLOW_OK;
  GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE;
  GstSegment *segment;
  GstBuffer *sync_buf;
  gboolean late, step_end, prepared = FALSE;

  ...
  sync_buf = GST_BUFFER_CAST (obj);
  ...
  bclass = GST_BASE_SINK_GET_CLASS (basesink);

  if (bclass->get_times)
    bclass->get_times (basesink, sync_buf, &start, &end);
...
  syncable =
        gst_base_sink_get_sync_times (basesink, obj, &sstart, &sstop, &rstart,
        &rstop, &rnext, &do_sync, &stepped, current, &step_end);
...
  ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (sync_buf),
      &late, &step_end);
...
  ret = bclass->render (basesink, GST_BUFFER_CAST (obj));
...

代码细节分析

gstpad之间是如何link的?

代码实现在gstreamer/subprojects/gstreamer/gst/gstpad.h(gstpad.c)

GstPadLinkReturn
gst_pad_link (GstPad * srcpad, GstPad * sinkpad)
{
  return gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_DEFAULT);
}

GstPadLinkReturn
gst_pad_link_full (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags)
{
  GstPadLinkReturn result;
  GstElement *parent;
  GstPadLinkFunction srcfunc, sinkfunc;

  //输入参数合规化检查,确保输入参数确实为pad,并且src和sink的顺序没错
  //#define GST_IS_PAD(obj)    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PAD))
  //#define GST_PAD_IS_SRC(pad)    (GST_PAD_DIRECTION(pad) == GST_PAD_SRC)
  //#define GST_PAD_IS_SINK(pad)    (GST_PAD_DIRECTION(pad) == GST_PAD_SINK)

  g_return_val_if_fail (GST_IS_PAD (srcpad), GST_PAD_LINK_REFUSED);
  g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), GST_PAD_LINK_WRONG_DIRECTION);
  g_return_val_if_fail (GST_IS_PAD (sinkpad), GST_PAD_LINK_REFUSED);
  g_return_val_if_fail (GST_PAD_IS_SINK (sinkpad), GST_PAD_LINK_WRONG_DIRECTION);

  GST_TRACER_PAD_LINK_PRE (srcpad, sinkpad);

  /* Notify the parent early. See gst_pad_unlink for details. */
  if (G_LIKELY ((parent = GST_ELEMENT_CAST (gst_pad_get_parent (srcpad))))) {
    if (G_LIKELY (GST_IS_ELEMENT (parent))) {
      gst_element_post_message (parent,
          gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
              GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, parent, TRUE));
    } else {
      gst_object_unref (parent);
      parent = NULL;
    }
  }

  //link检查,后面详细说明
  /* prepare will also lock the two pads */
  result = gst_pad_link_prepare (srcpad, sinkpad, flags);

  if (G_UNLIKELY (result != GST_PAD_LINK_OK)) {
    GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed: %s",
        GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad),
        gst_pad_link_get_name (result));
    goto done;
  }

  //配对赋值,分别将对端赋值给自己的peer
  /* must set peers before calling the link function */
  GST_PAD_PEER (srcpad) = sinkpad;
  GST_PAD_PEER (sinkpad) = srcpad;

  //检查事件,遍历srcpad中的event,不在sinkpad中的event进行标记
  /* check events, when something is different, mark pending */
  schedule_events (srcpad, sinkpad);

  //获取linkfunc,并执行
  /* get the link functions */
  srcfunc = GST_PAD_LINKFUNC (srcpad);
  sinkfunc = GST_PAD_LINKFUNC (sinkpad);

  if (G_UNLIKELY (srcfunc || sinkfunc)) {
    /* custom link functions, execute them */
    GST_OBJECT_UNLOCK (sinkpad);
    GST_OBJECT_UNLOCK (srcpad);

    if (srcfunc) {
      GstObject *tmpparent;

      ACQUIRE_PARENT (srcpad, tmpparent, no_parent);
      /* this one will call the peer link function */
      result = srcfunc (srcpad, tmpparent, sinkpad);
      RELEASE_PARENT (tmpparent);
    } else if (sinkfunc) {
      GstObject *tmpparent;

      ACQUIRE_PARENT (sinkpad, tmpparent, no_parent);
      /* if no source link function, we need to call the sink link
       * function ourselves. */
      result = sinkfunc (sinkpad, tmpparent, srcpad);
      RELEASE_PARENT (tmpparent);
    }
  no_parent:

    GST_OBJECT_LOCK (srcpad);
    GST_OBJECT_LOCK (sinkpad);

    /* we released the lock, check if the same pads are linked still */
    if (GST_PAD_PEER (srcpad) != sinkpad || GST_PAD_PEER (sinkpad) != srcpad)
      goto concurrent_link;

    if (G_UNLIKELY (result != GST_PAD_LINK_OK))
      goto link_failed;
  }
  GST_OBJECT_UNLOCK (sinkpad);
  GST_OBJECT_UNLOCK (srcpad);

  //发送signal给2个pad,PAD_LINKED
  /* fire off a signal to each of the pads telling them
   * that they've been linked */
  g_signal_emit (srcpad, gst_pad_signals[PAD_LINKED], 0, sinkpad);
  g_signal_emit (sinkpad, gst_pad_signals[PAD_LINKED], 0, srcpad);

  GST_CAT_INFO (GST_CAT_PADS, "linked %s:%s and %s:%s, successful",
      GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));

  if (!(flags & GST_PAD_LINK_CHECK_NO_RECONFIGURE))
    gst_pad_send_event (srcpad, gst_event_new_reconfigure ());

done:
  if (G_LIKELY (parent)) {
    gst_element_post_message (parent,
        gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
            GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, parent, FALSE));
    gst_object_unref (parent);
  }

  GST_TRACER_PAD_LINK_POST (srcpad, sinkpad, result);
  return result;

  /* ERRORS */
concurrent_link:
  {
    GST_CAT_INFO (GST_CAT_PADS, "concurrent link between %s:%s and %s:%s",
        GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
    GST_OBJECT_UNLOCK (sinkpad);
    GST_OBJECT_UNLOCK (srcpad);

    /* The other link operation succeeded first */
    result = GST_PAD_LINK_WAS_LINKED;
    goto done;
  }
link_failed:
  {
    GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed: %s",
        GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad),
        gst_pad_link_get_name (result));

    GST_PAD_PEER (srcpad) = NULL;
    GST_PAD_PEER (sinkpad) = NULL;

    GST_OBJECT_UNLOCK (sinkpad);
    GST_OBJECT_UNLOCK (srcpad);

    goto done;
  }
}


static GstPadLinkReturn
gst_pad_link_prepare (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags)
{
  GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s",
      GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));

  GST_OBJECT_LOCK (srcpad);

  //确保2个pad还没有进行link
  if (G_UNLIKELY (GST_PAD_PEER (srcpad) != NULL))
    goto src_was_linked;

  GST_OBJECT_LOCK (sinkpad);

  if (G_UNLIKELY (GST_PAD_PEER (sinkpad) != NULL))
    goto sink_was_linked;

  //检查pad的父类和祖父类,确保父类不同,并且祖父相同
  /* check hierarchy, pads can only be linked if the grandparents
   * are the same. */
  if ((flags & GST_PAD_LINK_CHECK_HIERARCHY)
      && !gst_pad_link_check_hierarchy (srcpad, sinkpad))
    goto wrong_hierarchy;


  //检查2个pad的交集,确保有交集
  /* check pad caps for non-empty intersection */
  if (!gst_pad_link_check_compatible_unlocked (srcpad, sinkpad, flags))
    goto no_format;

  /* FIXME check pad scheduling for non-empty intersection */

  return GST_PAD_LINK_OK;

src_was_linked:
  {
    GST_CAT_INFO (GST_CAT_PADS, "src %s:%s was already linked to %s:%s",
        GST_DEBUG_PAD_NAME (srcpad),
        GST_DEBUG_PAD_NAME (GST_PAD_PEER (srcpad)));
    /* we do not emit a warning in this case because unlinking cannot
     * be made MT safe.*/
    GST_OBJECT_UNLOCK (srcpad);
    return GST_PAD_LINK_WAS_LINKED;
  }
sink_was_linked:
  {
    GST_CAT_INFO (GST_CAT_PADS, "sink %s:%s was already linked to %s:%s",
        GST_DEBUG_PAD_NAME (sinkpad),
        GST_DEBUG_PAD_NAME (GST_PAD_PEER (sinkpad)));
    /* we do not emit a warning in this case because unlinking cannot
     * be made MT safe.*/
    GST_OBJECT_UNLOCK (sinkpad);
    GST_OBJECT_UNLOCK (srcpad);
    return GST_PAD_LINK_WAS_LINKED;
  }
wrong_hierarchy:
  {
    GST_CAT_INFO (GST_CAT_PADS, "pads have wrong hierarchy");
    GST_OBJECT_UNLOCK (sinkpad);
    GST_OBJECT_UNLOCK (srcpad);
    return GST_PAD_LINK_WRONG_HIERARCHY;
  }
no_format:
  {
    GST_CAT_INFO (GST_CAT_PADS, "caps are incompatible");
    GST_OBJECT_UNLOCK (sinkpad);
    GST_OBJECT_UNLOCK (srcpad);
    return GST_PAD_LINK_NOFORMAT;
  }
}


static gboolean
gst_pad_link_check_hierarchy (GstPad * src, GstPad * sink)
{
  GstObject *psrc, *psink;

  
  //获取父类
  psrc = GST_OBJECT_PARENT (src);
  psink = GST_OBJECT_PARENT (sink);

  /* if one of the pads has no parent, we allow the link */
  if (G_UNLIKELY (psrc == NULL || psink == NULL))
    goto no_parent;

  /* only care about parents that are elements */
  if (G_UNLIKELY (!GST_IS_ELEMENT (psrc) || !GST_IS_ELEMENT (psink)))
    goto no_element_parent;

  /* if the parents are the same, we have a loop */
  if (G_UNLIKELY (psrc == psink))
    goto same_parents;

  //获取祖父类
  /* if they both have a parent, we check the grandparents. We can not lock
   * the parent because we hold on the child (pad) and the locking order is
   * parent >> child. */
  psrc = GST_OBJECT_PARENT (psrc);
  psink = GST_OBJECT_PARENT (psink);

  /* if they have grandparents but they are not the same */
  if (G_UNLIKELY (psrc != psink))
    goto wrong_grandparents;

  return TRUE;

  /* ERRORS */
no_parent:
  {
    GST_CAT_DEBUG (GST_CAT_CAPS,
        "one of the pads has no parent %" GST_PTR_FORMAT " and %"
        GST_PTR_FORMAT, psrc, psink);
    return TRUE;
  }
no_element_parent:
  {
    GST_CAT_DEBUG (GST_CAT_CAPS,
        "one of the pads has no element parent %" GST_PTR_FORMAT " and %"
        GST_PTR_FORMAT, psrc, psink);
    return TRUE;
  }
same_parents:
  {
    GST_CAT_DEBUG (GST_CAT_CAPS, "pads have same parent %" GST_PTR_FORMAT,
        psrc);
    return FALSE;
  }
wrong_grandparents:
  {
    GST_CAT_DEBUG (GST_CAT_CAPS,
        "pads have different grandparents %" GST_PTR_FORMAT " and %"
        GST_PTR_FORMAT, psrc, psink);
    return FALSE;
  }
}

pipeline中buffer传递流程

我们以一个简单的pipeline来进行分析,pipeline如下所示:

gst-launch-1.0 filesrc location="1080P.mov" ! qtdemux name=demux demux.video_0 ! h264parse ! openh264dec ! waylandsink

几个插件的代码位置分别在:

basesrc----------------------->gstreamer/libs/gst/base
  |---->filesrc--------------->gstreamer/plugins/elements

qtdemux----------------------->gst-plugins-good/gst/isomp4

baseparse--------------------->gstreamer/libs/gst/base
  |---->h264parse------------->gst-plugins-bad/gst/videoparsers

videodecoder------------------>gst-plugins-base/gst-libs/gst/video
  |---->openh264dec----------->gst-plugins-bad/ext/openh264

gstbasesink------------------->gstreamer/libs/gst/base
  |---->gstvideosink---------->gst-plugins-base/gst-libs/gst/video
          |---->waylandsink- ->gst-plugins-bad/ext/wayland

filesrc——>gstreamer/plugins/elements:

gstfilesrc.c

//向GstBuffer中填充从文件中读取的数据流
static GstFlowReturn
gst_file_src_fill (GstBaseSrc * basesrc, guint64 offset, guint length,
    GstBuffer * buf)
{
  GstFileSrc *src;
  guint to_read, bytes_read;
  int ret;
  GstMapInfo info;
  guint8 *data;

  src = GST_FILE_SRC_CAST (basesrc);

  if (G_UNLIKELY (offset != -1 && src->read_position != offset)) {
    off_t res;

    res = lseek (src->fd, offset, SEEK_SET);
    if (G_UNLIKELY (res < 0 || res != offset))
      goto seek_failed;

    src->read_position = offset;
  }

  if (!gst_buffer_map (buf, &info, GST_MAP_WRITE))
    goto buffer_write_fail;
  data = info.data;

  bytes_read = 0;
  to_read = length;
  while (to_read > 0) {
    GST_LOG_OBJECT (src, "Reading %d bytes at offset 0x%" G_GINT64_MODIFIER "x",
        to_read, offset + bytes_read);
    errno = 0;
    ret = read (src->fd, data + bytes_read, to_read);
    if (G_UNLIKELY (ret < 0)) {
      if (errno == EAGAIN || errno == EINTR)
        continue;
      goto could_not_read;
    }

    /* files should eos if they read 0 and more was requested */
    if (G_UNLIKELY (ret == 0)) {
      /* .. but first we should return any remaining data */
      if (bytes_read > 0)
        break;
      goto eos;
    }

    to_read -= ret;
    bytes_read += ret;

    src->read_position += ret;
  }

  gst_buffer_unmap (buf, &info);
  if (bytes_read != length)
    gst_buffer_resize (buf, 0, bytes_read);

  GST_BUFFER_OFFSET (buf) = offset;
  GST_BUFFER_OFFSET_END (buf) = offset + bytes_read;

  return GST_FLOW_OK;

  /* ERROR */
}

gstbasesrc.c

//创建,开始fill数据
static GstFlowReturn
gst_base_src_default_create (GstBaseSrc * src, guint64 offset,
    guint size, GstBuffer ** buffer)
{
  GstBaseSrcClass *bclass;
  GstFlowReturn ret;
  GstBuffer *res_buf;

  bclass = GST_BASE_SRC_GET_CLASS (src);

  if (G_UNLIKELY (!bclass->alloc))
    goto no_function;
  if (G_UNLIKELY (!bclass->fill))
    goto no_function;

  if (*buffer == NULL) {
    /* downstream did not provide us with a buffer to fill, allocate one
     * ourselves */
    ret = bclass->alloc (src, offset, size, &res_buf);
    if (G_UNLIKELY (ret != GST_FLOW_OK))
      goto alloc_failed;
  } else {
    res_buf = *buffer;
  }

  if (G_LIKELY (size > 0)) {
    /* only call fill when there is a size */
    ret = bclass->fill (src, offset, size, res_buf);
    if (G_UNLIKELY (ret != GST_FLOW_OK))
      goto not_ok;
  }

  *buffer = res_buf;

  return GST_FLOW_OK;

  /* ERRORS */
}

//分配数据buffer的内存
static GstFlowReturn
gst_base_src_default_alloc (GstBaseSrc * src, guint64 offset,
    guint size, GstBuffer ** buffer)
{
  GstFlowReturn ret;
  GstBaseSrcPrivate *priv = src->priv;
  GstBufferPool *pool = NULL;
  GstAllocator *allocator = NULL;
  GstAllocationParams params;

  GST_OBJECT_LOCK (src);
  if (priv->pool) {
    pool = gst_object_ref (priv->pool);
  } else if (priv->allocator) {
    allocator = gst_object_ref (priv->allocator);
  }
  params = priv->params;
  GST_OBJECT_UNLOCK (src);

  if (pool) {
    ret = gst_buffer_pool_acquire_buffer (pool, buffer, NULL);
  } else if (size != -1) {
    *buffer = gst_buffer_new_allocate (allocator, size, &params);
    if (G_UNLIKELY (*buffer == NULL))
      goto alloc_failed;

    ret = GST_FLOW_OK;
  } else {
    GST_WARNING_OBJECT (src, "Not trying to alloc %u bytes. Blocksize not set?",
        size);
    goto alloc_failed;
  }

done:
  if (pool)
    gst_object_unref (pool);
  if (allocator)
    gst_object_unref (allocator);

  return ret;

  /* ERRORS */
alloc_failed:
  {
    GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", size);
    ret = GST_FLOW_ERROR;
    goto done;
  }
}


//循环向外push数据
static void
gst_base_src_loop (GstPad * pad)
{
  GstBaseSrc *src;
  GstBuffer *buf = NULL;
  GstFlowReturn ret;
  gint64 position;
  gboolean eos;
  guint blocksize;
  GList *pending_events = NULL, *tmp;

  eos = FALSE;

  src = GST_BASE_SRC (GST_OBJECT_PARENT (pad));

  /* Just leave immediately if we're flushing */
  GST_LIVE_LOCK (src);
  if (G_UNLIKELY (src->priv->flushing || GST_PAD_IS_FLUSHING (pad)))
    goto flushing;
  GST_LIVE_UNLOCK (src);

  /* Just return if EOS is pushed again, as the app might be unaware that an
   * EOS have been sent already */
  if (GST_PAD_IS_EOS (pad)) {
    GST_DEBUG_OBJECT (src, "Pad is marked as EOS, pause the task");
    gst_pad_pause_task (pad);
    goto done;
  }

  gst_base_src_send_stream_start (src);

  /* The stream-start event could've caused something to flush us */
  GST_LIVE_LOCK (src);
  if (G_UNLIKELY (src->priv->flushing || GST_PAD_IS_FLUSHING (pad)))
    goto flushing;
  GST_LIVE_UNLOCK (src);

  /* check if we need to renegotiate */
  if (gst_pad_check_reconfigure (pad)) {
    if (!gst_base_src_negotiate_unlocked (src)) {
      gst_pad_mark_reconfigure (pad);
      if (GST_PAD_IS_FLUSHING (pad)) {
        GST_LIVE_LOCK (src);
        goto flushing;
      } else {
        goto negotiate_failed;
      }
    }
  }

  GST_LIVE_LOCK (src);

  if (G_UNLIKELY (src->priv->flushing || GST_PAD_IS_FLUSHING (pad)))
    goto flushing;

  blocksize = src->blocksize;

  /* if we operate in bytes, we can calculate an offset */
  if (src->segment.format == GST_FORMAT_BYTES) {
    position = src->segment.position;
    /* for negative rates, start with subtracting the blocksize */
    if (src->segment.rate < 0.0) {
      /* we cannot go below segment.start */
      if (position > src->segment.start + blocksize)
        position -= blocksize;
      else {
        /* last block, remainder up to segment.start */
        blocksize = position - src->segment.start;
        position = src->segment.start;
      }
    }
  } else
    position = -1;

  GST_LOG_OBJECT (src, "next_ts %" GST_TIME_FORMAT " size %u",
      GST_TIME_ARGS (position), blocksize);

  /* clean up just in case we got interrupted or so last time round */
  if (src->priv->pending_bufferlist != NULL) {
    gst_buffer_list_unref (src->priv->pending_bufferlist);
    src->priv->pending_bufferlist = NULL;
  }

  ret = gst_base_src_get_range (src, position, blocksize, &buf);
  if (G_UNLIKELY (ret != GST_FLOW_OK)) {
    GST_INFO_OBJECT (src, "pausing after gst_base_src_get_range() = %s",
        gst_flow_get_name (ret));
    GST_LIVE_UNLOCK (src);
    goto pause;
  }

  /* Note: at this point buf might be a single buf returned which we own or
   * the first buf of a pending buffer list submitted via submit_buffer_list(),
   * in which case the buffer is owned by the pending buffer list and not us. */
  g_assert (buf != NULL);

  /* push events to close/start our segment before we push the buffer. */
  if (G_UNLIKELY (src->priv->segment_pending)) {
    GstEvent *seg_event = gst_event_new_segment (&src->segment);

    gst_event_set_seqnum (seg_event, src->priv->segment_seqnum);
    src->priv->segment_seqnum = gst_util_seqnum_next ();
    gst_pad_push_event (pad, seg_event);
    src->priv->segment_pending = FALSE;
  }

  if (g_atomic_int_get (&src->priv->have_events)) {
    GST_OBJECT_LOCK (src);
    /* take the events */
    pending_events = src->priv->pending_events;
    src->priv->pending_events = NULL;
    g_atomic_int_set (&src->priv->have_events, FALSE);
    GST_OBJECT_UNLOCK (src);
  }

  /* Push out pending events if any */
  if (G_UNLIKELY (pending_events != NULL)) {
    for (tmp = pending_events; tmp; tmp = g_list_next (tmp)) {
      GstEvent *ev = (GstEvent *) tmp->data;
      gst_pad_push_event (pad, ev);
    }
    g_list_free (pending_events);
  }

  /* figure out the new position */
  switch (src->segment.format) {
    case GST_FORMAT_BYTES:
    {
      guint bufsize = gst_buffer_get_size (buf);

      /* we subtracted above for negative rates */
      if (src->segment.rate >= 0.0)
        position += bufsize;
      break;
    }
    case GST_FORMAT_TIME:
    {
      GstClockTime start, duration;

      start = GST_BUFFER_TIMESTAMP (buf);
      duration = GST_BUFFER_DURATION (buf);

      if (GST_CLOCK_TIME_IS_VALID (start))
        position = start;
      else
        position = src->segment.position;

      if (GST_CLOCK_TIME_IS_VALID (duration)) {
        if (src->segment.rate >= 0.0)
          position += duration;
      }
      break;
    }
    case GST_FORMAT_DEFAULT:
      if (src->segment.rate >= 0.0)
        position = GST_BUFFER_OFFSET_END (buf);
      else
        position = GST_BUFFER_OFFSET (buf);
      break;
    default:
      position = -1;
      break;
  }
  if (position != -1) {
    if (src->segment.rate >= 0.0) {
      /* positive rate, check if we reached the stop */
      if (src->segment.stop != -1) {
        if (position >= src->segment.stop) {
          if (g_atomic_int_get (&src->priv->automatic_eos))
            eos = TRUE;
          position = src->segment.stop;
        }
      }
    } else {
      /* negative rate, check if we reached the start. start is always set to
       * something different from -1 */
      if (position <= src->segment.start) {
        if (g_atomic_int_get (&src->priv->automatic_eos))
          eos = TRUE;
        position = src->segment.start;
      }
      /* when going reverse, all buffers are DISCONT */
      src->priv->discont = TRUE;
    }
    GST_OBJECT_LOCK (src);
    src->segment.position = position;
    GST_OBJECT_UNLOCK (src);
  }

  if (G_UNLIKELY (src->priv->discont)) {
    GST_INFO_OBJECT (src, "marking pending DISCONT");
    buf = gst_buffer_make_writable (buf);
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
    src->priv->discont = FALSE;
  }
  GST_LIVE_UNLOCK (src);

  /* push buffer or buffer list */
  if (src->priv->pending_bufferlist != NULL) {
    ret = gst_pad_push_list (pad, src->priv->pending_bufferlist);
    src->priv->pending_bufferlist = NULL;
  } else {
    ret = gst_pad_push (pad, buf);
  }

  if (G_UNLIKELY (ret != GST_FLOW_OK)) {
    if (ret == GST_FLOW_NOT_NEGOTIATED) {
      goto not_negotiated;
    }
    GST_INFO_OBJECT (src, "pausing after gst_pad_push() = %s",
        gst_flow_get_name (ret));
    goto pause;
  }

  /* Segment pending means that a new segment was configured
   * during this loop run */
  if (G_UNLIKELY (eos && !src->priv->segment_pending)) {
    GST_INFO_OBJECT (src, "pausing after end of segment");
    ret = GST_FLOW_EOS;
    goto pause;
  }

done:
  return;

  /* special cases */
not_negotiated:
  {
    if (gst_pad_needs_reconfigure (pad)) {
      GST_DEBUG_OBJECT (src, "Retrying to renegotiate");
      return;
    }
    /* fallthrough when push returns NOT_NEGOTIATED and we don't have
     * a pending negotiation request on our srcpad */
  }
negotiate_failed:
  {
    GST_DEBUG_OBJECT (src, "Not negotiated");
    ret = GST_FLOW_NOT_NEGOTIATED;
    goto pause;
  }
flushing:
  {
    GST_DEBUG_OBJECT (src, "we are flushing");
    GST_LIVE_UNLOCK (src);
    ret = GST_FLOW_FLUSHING;
    goto pause;
  }
pause:
  {
    GstEvent *event;

    GST_DEBUG_OBJECT (src, "pausing task, reason %s", gst_flow_get_name (ret));
    src->running = FALSE;
    gst_pad_pause_task (pad);
    if (ret == GST_FLOW_EOS) {
      gboolean flag_segment;
      GstFormat format;
      gint64 position;

      flag_segment = (src->segment.flags & GST_SEGMENT_FLAG_SEGMENT) != 0;
      format = src->segment.format;
      position = src->segment.position;

      /* perform EOS logic */
      if (src->priv->forced_eos) {
        g_assert (g_atomic_int_get (&src->priv->has_pending_eos));
        GST_OBJECT_LOCK (src);
        event = src->priv->pending_eos;
        src->priv->pending_eos = NULL;
        GST_OBJECT_UNLOCK (src);

      } else if (flag_segment) {
        GstMessage *message;

        message = gst_message_new_segment_done (GST_OBJECT_CAST (src),
            format, position);
        gst_message_set_seqnum (message, src->priv->seqnum);
        gst_element_post_message (GST_ELEMENT_CAST (src), message);
        event = gst_event_new_segment_done (format, position);
        gst_event_set_seqnum (event, src->priv->seqnum);

      } else {
        event = gst_event_new_eos ();
        gst_event_set_seqnum (event, src->priv->seqnum);
      }

      gst_pad_push_event (pad, event);
      src->priv->forced_eos = FALSE;

    } else if (ret == GST_FLOW_NOT_LINKED || ret <= GST_FLOW_EOS) {
      event = gst_event_new_eos ();
      gst_event_set_seqnum (event, src->priv->seqnum);
      /* for fatal errors we post an error message, post the error
       * first so the app knows about the error first.
       * Also don't do this for FLUSHING because it happens
       * due to flushing and posting an error message because of
       * that is the wrong thing to do, e.g. when we're doing
       * a flushing seek. */
      GST_ELEMENT_FLOW_ERROR (src, ret);
      gst_pad_push_event (pad, event);
    }
    goto done;
  }
}

流程图如下:

 

qtdemux——>gst-plugins-good/gst/isomp4:

 h264parse—->gst-plugins-bad/gst/videoparsers:

 openh264dec–>gst-plugins-bad/ext/openh264:

 

waylandsink–>gst-plugins-bad/ext/wayland:Gstreamer

 

基础框架代码分析

gstquark

  • 基于GLib中的GQuark
  • 实现Gstreamer中常用的数字和字符串的映射
-------------------------------------------------------gstquark.h---------------------------------------------
typedef enum _GstQuarkId
{
  GST_QUARK_FORMAT = 0,
  GST_QUARK_CURRENT = 1,
  GST_QUARK_DURATION = 2,
  GST_QUARK_RATE = 3,
  ...
  GST_QUARK_GAP_FLAGS = 202,
  GST_QUARK_MAX = 203
} GstQuarkId;

extern GQuark _priv_gst_quark_table[GST_QUARK_MAX];

#define GST_QUARK(q) _priv_gst_quark_table[GST_QUARK_##q]  //通过这个宏来使用

-------------------------------------------------------gstquark.c---------------------------------------------

static const gchar *_quark_strings[] = {
  "format", "current", "duration", "rate",
  ...
  "gap-flags"
};

GQuark _priv_gst_quark_table[GST_QUARK_MAX];

void
_priv_gst_quarks_initialize (void)    //初始化
{
  gint i;

  if (G_N_ELEMENTS (_quark_strings) != GST_QUARK_MAX)
    g_warning ("the quark table is not consistent! %d != %d",
        (int) G_N_ELEMENTS (_quark_strings), GST_QUARK_MAX);

  for (i = 0; i < GST_QUARK_MAX; i++) {
    _priv_gst_quark_table[i] = g_quark_from_static_string (_quark_strings[i]);  //实现枚举和字符串的一一对应
  }
}

-------------------------------------------------------gst.c---------------------------------------------

init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
    GError ** error)
{
  GLogLevelFlags llf;

  if (gst_initialized) {
    GST_DEBUG ("already initialized");
    return TRUE;
  }

  llf = G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL;
  /* TODO: should we also set up a handler for the other gst libs/domains? */
  g_log_set_handler (G_LOG_DOMAIN, llf, debug_log_handler, NULL);

  _priv_gst_mini_object_initialize ();
  _priv_gst_quarks_initialize ();    //这里进行初始化
  _priv_gst_allocator_initialize ();
  ...

gsterror

  • 定义了一些通用的error类型及其标准打印
-----------------------------------------gsterror.h----------------------------------------------------
typedef enum
{
  GST_CORE_ERROR_FAILED = 1,
  GST_CORE_ERROR_TOO_LAZY,
  GST_CORE_ERROR_NOT_IMPLEMENTED,
  ...
  GST_CORE_ERROR_DISABLED,
  GST_CORE_ERROR_NUM_ERRORS
} GstCoreError;
...
typedef enum
{
  GST_LIBRARY_ERROR_FAILED = 1,
  GST_LIBRARY_ERROR_TOO_LAZY,
  GST_LIBRARY_ERROR_INIT,
  ...
  GST_LIBRARY_ERROR_NUM_ERRORS
} GstLibraryError;
...
typedef enum
{
  GST_RESOURCE_ERROR_FAILED = 1,
  GST_RESOURCE_ERROR_TOO_LAZY,
  GST_RESOURCE_ERROR_NOT_FOUND,
  ...
  GST_RESOURCE_ERROR_NOT_AUTHORIZED,
  GST_RESOURCE_ERROR_NUM_ERRORS
} GstResourceError;
...
typedef enum
{
  GST_STREAM_ERROR_FAILED = 1,
  GST_STREAM_ERROR_TOO_LAZY,
  GST_STREAM_ERROR_NOT_IMPLEMENTED,
  ...
  GST_STREAM_ERROR_DECRYPT_NOKEY,
  GST_STREAM_ERROR_NUM_ERRORS
} GstStreamError;
...
#define GST_LIBRARY_ERROR   gst_library_error_quark ()

#define GST_RESOURCE_ERROR  gst_resource_error_quark ()

#define GST_CORE_ERROR      gst_core_error_quark ()
#define GST_STREAM_ERROR    gst_stream_error_quark ()

-----------------------------------------gsterror.c----------------------------------------------------

...
gchar *
gst_error_get_message (GQuark domain, gint code)    //通过这个调用,domain是4种主要类型,code是里面的枚举类型
{
  const gchar *message = NULL;

  if (domain == GST_CORE_ERROR)
    message = gst_error_get_core_error ((GstCoreError) code);
  else if (domain == GST_LIBRARY_ERROR)
    message = gst_error_get_library_error ((GstLibraryError) code);
  else if (domain == GST_RESOURCE_ERROR)
    message = gst_error_get_resource_error ((GstResourceError) code);
  else if (domain == GST_STREAM_ERROR)
    message = gst_error_get_stream_error ((GstStreamError) code);
  else {
    g_warning ("No error messages for domain %s", g_quark_to_string (domain));
    return g_strdup_printf (_("No error message for domain %s."),
        g_quark_to_string (domain));
  }
  if (message)
    return g_strdup (message);
  else
    return
        g_strdup_printf (_
        ("No standard error message for domain %s and code %d."),
        g_quark_to_string (domain), code);
}

...

gstformat

  • 注册一些常用format
-----------------------------------------------gstformat.h------------------------------------------------

typedef enum {
  GST_FORMAT_UNDEFINED  =  0, /* must be first in list */
  GST_FORMAT_DEFAULT    =  1,
  GST_FORMAT_BYTES      =  2,
  GST_FORMAT_TIME       =  3,
  GST_FORMAT_BUFFERS    =  4,
  GST_FORMAT_PERCENT    =  5
} GstFormat;    //一些基本format

struct _GstFormatDefinition
{
  GstFormat    value;
  const gchar *nick;
  const gchar *description;
  GQuark       quark;
};    //format格式定义

GST_API const gchar*    gst_format_get_name(GstFormat format);
GST_API GQuark          gst_format_to_quark(GstFormat format);
GST_API GstFormat       gst_format_register(const gchar *nick, const gchar *description);
GST_API GstFormat       gst_format_get_by_nick(const gchar *nick);
GST_API gboolean        gst_formats_contains(const GstFormat *formats, GstFormat format);
GST_API const GstFormatDefinition* gst_format_get_details(GstFormat format);
GST_API GstIterator*    gst_format_iterate_definitions(void);
//外部API

-------------------------------------------------gstformat.c-----------------------------------------------

static GMutex mutex;
static GList *_gst_formats = NULL;
static GHashTable *_nick_to_format = NULL;
static GHashTable *_format_to_nick = NULL;
static guint32 _n_values = 1;   /* we start from 1 because 0 reserved for UNDEFINED */

static GstFormatDefinition standard_definitions[] = {
  {GST_FORMAT_DEFAULT, "default", "Default format for the media type", 0},
  {GST_FORMAT_BYTES, "bytes", "Bytes", 0},
  {GST_FORMAT_TIME, "time", "Time", 0},
  {GST_FORMAT_BUFFERS, "buffers", "Buffers", 0},
  {GST_FORMAT_PERCENT, "percent", "Percent", 0},
  {GST_FORMAT_UNDEFINED, NULL, NULL, 0}
};

void
_priv_gst_format_initialize (void)  //初始化,在gst.c的init_post中调用
{
  GstFormatDefinition *standards = standard_definitions;

  if (_nick_to_format == NULL) {
    _nick_to_format = g_hash_table_new (g_str_hash, g_str_equal);
    _format_to_nick = g_hash_table_new (NULL, NULL);  //新建2个hash表
  }

  while (standards->nick) {
    standards->quark = g_quark_from_static_string (standards->nick);
    g_hash_table_insert (_nick_to_format, (gpointer) standards->nick,
        standards);
    g_hash_table_insert (_format_to_nick, GINT_TO_POINTER (standards->value),
        standards);    //初始化2个hash表

    _gst_formats = g_list_append (_gst_formats, standards);  //初始化1个list
    standards++;
    _n_values++;
  }
  ...
}

GstFormat
gst_format_register (const gchar * nick, const gchar * description)  //使用昵称和描述注册1个format
{
  GstFormatDefinition *format;
  GstFormat query;
  ...
  query = gst_format_get_by_nick (nick);  //查找是否已经存在
  if (query != GST_FORMAT_UNDEFINED)
    return query;

  g_mutex_lock (&mutex);
  format = g_slice_new (GstFormatDefinition);  //分配内存
  format->value = (GstFormat) _n_values;
  format->nick = g_strdup (nick);
  format->description = g_strdup (description);
  format->quark = g_quark_from_static_string (format->nick);

  g_hash_table_insert (_nick_to_format, (gpointer) format->nick, format);
  g_hash_table_insert (_format_to_nick, GINT_TO_POINTER (format->value),  //插入Hash表
      format);
  _gst_formats = g_list_append (_gst_formats, format);  //插入list
  _n_values++;
  g_mutex_unlock (&mutex);

  return format->value;
}

gstclock

  • 维护时钟对象
  • gstpipeline维护一个gstclock,pipeline管理其生命周期,pipeline将这个clock传递给它的element
  • 多时钟源
  • clock time&running time&stream time
GstClockTime
gst_clock_get_time (GstClock * clock)  //获取给定时钟的当前时间,并根据偏移量和速率进行调整
{
  GstClockTime ret;
  gint seq;

  do {
    /* reget the internal time when we retry to get the most current
     * timevalue */
    ret = gst_clock_get_internal_time (clock);  //获取给定时钟的当前时间

    seq = read_seqbegin (clock);  //顺序锁操作
    /* this will scale for rate and offset */
    ret = gst_clock_adjust_unlocked (clock, ret);
  } while (read_seqretry (clock, seq));

  return ret;
}

GstClockTime
gst_clock_get_internal_time (GstClock * clock)
{
  GstClockTime ret;
  GstClockClass *cclass;



  cclass = GST_CLOCK_GET_CLASS (clock);

  if (G_UNLIKELY (cclass->get_internal_time == NULL))
    goto not_supported;

  ret = cclass->get_internal_time (clock);  //根据时钟的类型,转到子类中去获取各种clock的时间,比如CPU/AUDIO等


  return ret;

}

GstClockTime
gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)  //internal时间调整,转为external时间
{
  GstClockTime ret, cinternal, cexternal, cnum, cdenom;
  GstClockPrivate *priv = clock->priv;

  /* get calibration values for readability */
  cinternal = priv->internal_calibration;
  cexternal = priv->external_calibration;
  cnum = priv->rate_numerator;
  cdenom = priv->rate_denominator;  //调整参数

  ret =
      gst_clock_adjust_with_calibration (clock, internal, cinternal, cexternal,
      cnum, cdenom);  //具体的调整接口

  /* make sure the time is increasing */
  priv->last_time = MAX (ret, priv->last_time);  //确保时间递增

  return priv->last_time;
}

GstClockTime
gst_clock_adjust_with_calibration (GstClock * clock,
    GstClockTime internal_target, GstClockTime cinternal,
    GstClockTime cexternal, GstClockTime cnum, GstClockTime cdenom)
{
  GstClockTime ret;


  /* The formula is (internal - cinternal) * cnum / cdenom + cexternal
   *
   * Since we do math on unsigned 64-bit ints we have to special case for
   * internal < cinternal to get the sign right. this case is not very common,
   * though.
   */
  if (G_LIKELY (internal_target >= cinternal)) {
    ret = internal_target - cinternal;
    ret = gst_util_uint64_scale (ret, cnum, cdenom);
    ret += cexternal;
  } else {
    ret = cinternal - internal_target;
    ret = gst_util_uint64_scale (ret, cnum, cdenom);
    /* clamp to 0 */
    if (G_LIKELY (cexternal > ret))
      ret = cexternal - ret;
    else
      ret = 0;
  }

  return ret;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值