Gstreamer-对象关系类型

对象关系类型

本文档描述了GStreamer中存在的对象之间的关系。它还将描述处理与锁定和重新计数关系的方式。

父-子关系

     +---------+    +-------+
     | parent  |    | child |
*--->|       *----->|       |
     |       F1|<-----*    1|
     +---------+    +-------+
特性
- 父对象拥有多个子对象的引用
- 子对象有对父对象的引用
- 引用字段使用锁保护
- 每个子对象对父对象的引用都不会反映在父对象的引用计数。
- 父对象在获得子对象的所有权时会删除子对象的浮动标志.
- -应用程序具有对父对象的有效引用
- 创建/销毁需要两个未嵌套的锁和1个引用计数
GStreamer中的用法
* `GstBin` -> `GstElement`
* `GstElement` -> `GstRealPad`
生命周期
object 创建

该应用程序创建两个对象并保存指向它们的指针。这些对象在初始时为FLOATING并且引用计数为1。

     +---------+              +-------+
*--->| parent  |         *--->| child |
     |       * |              |       |
     |       F1|              | *   F1|
     +---------+              +-------+
建立parent-child关系

然后,应用程序在父对象上调用方法以取得子对象的所有权。父级执行以下操作:

result = _set_parent (child, parent);
if (result) {
  lock (parent);
  ref_pointer = child;

  1.  update other data structures .. unlock (parent);
} else {

  2.  child had parent ..
}

该_set_parent()方法执行以下操作:

lock (child);
if (child->parent != null) {
  unlock (child);
  return false;
}
if (is_floating (child)) {
  unset (child, floating);
}
else {
  _ref (child);
}
child->parent = parent;
unlock (child);
_signal (parent_set, child, parent);
return true;

该函数自动检查子对象是否还没有父对象,如果没有则将设置父对象。它还将接管子对象,这意味着对该子对象的所有floating references现在都无效,因为它接管了对象的引用计数。

视觉上:
当_set_parent()返回TRUE之后:

      +---------+            +-------+
*---->| parent  |      *-//->| child |
      |       * |            |       |
      |       F1|<-------------*    1|
      +---------+            +-------+

父对象ref_pointer更新ref_pointer到子对象之后。

      +---------+        +-------+
*---->| parent  |  *-//->| child |
      |       *--------->|       |
      |       F1|<---------*    1|
      +---------+        +-------+
  • 因为_set_parent()方法是原子性的,所以只有一个父对象能够_sink同一个对象。
  • 因为只有一个父对象能够成功对子对象调用_set_parent(),所以只会有一个会添加对该对象的引用。
  • 因为父对象可以拥有对子对象的多个引用,所以在锁定子对象时不需要锁定父对象。多个线程可以对具有相同父对象的子对象调用_set_parent(),然后父对象可以将所有这些子对象添加到它的列表中。

注意:信号是在父级将元素添加到其内部数据结构之前发出的。这不是问题,因为父对象通常会有自己的信号通知应用程序子对象被引用。如果_set_parent()执行失败,一种可能的解决方案是先更新内部结构,然后再执行回滚。这不是一个好的解决方案,因为迭代器可能过早的捕获“half-added”子对象。

使用父-子关系
  • 由于对子对象的初始floating reference 在将其交给父对象后变得无效,因此任何对子对象的的引用至少具有refcount> 1。
  • 这意味着取消对子对象的引用不能将refcount减小为0。实际上,只有父对象才能撤销和释放该子对象。
  • 给定对子对象的引用,父指针仅在保持子对象的LOCK时有效。的确,在解锁子对象的锁之后,父对象可以解除和子对象父子关系,或者甚至父对象可以释放。为了避免父对象释放问题,在获取父对象指针时,在释放子对象LOCK之前取得对父对象的引用。
  • 获取父对象的引用。
    子对象持有父对象的一个引用,所以它不能被释放。
    LOCK (child);
    parent = _ref (child->parent);
    UNLOCK (child);

   .. use parent ..

   _unref (parent);
  • 获取子对象的引用
    1. 可以通过将子对象添加到父对象之前引用它或查询父对象来获得子对象的引用。
    2. 当从父节点请求子节点时,引用被保留到父节点,因此不能被释放。父元素将使用其内部数据结构来定位子元素,并将返回对子元素的引用,其中包含一个递增的refcount。请求者应该在使用后_unref()子对象。
  • 销毁父-子关系
    1. 只有父对象可以主动销毁父-子的关系,这种情况通常发生在对父对象调用方法以释放子对象的所有权时。
    2. 子对象永远不会将它自己从父对象中移除。
    3. 因为以子对象为参数在父对象上调用方法需要调用者获得对子对象的有效引用,所以子对象的引用计数至少> 1。
    4. 父对象将执行以下操作:
    LOCK (parent);
    if (ref_pointer == child) {
      ref_pointer = NULL;

      ..update other data structures ..
      UNLOCK (parent);

      _unparent (child);
    } else {
      UNLOCK (parent);
      .. not our child ..
    }
    ```

该_unparent()方法执行以下操作:

```c
LOCK (child);
if (child->parent != NULL) {
  child->parent = NULL;
  UNLOCK (child);
  _signal (PARENT_UNSET, child, parent);

  _unref (child);
} else {
  UNLOCK (child);
}

由于_unparent()会取消对子对象的引用,在这个函数之后子指针可能无效。如果父节点想要在子节点上执行其他操作(比如信号发射),它应该先_ref()子对象。

single-reffed关系

     +---------+        +---------+
*--->| object1 |   *--->| object2 |
     |       *--------->|         |
     |        1|        |        2|
     +---------+        +---------+
特性
-一个对象对另一个对象有引用
-使用LOCK保护的引用字段
-对象持有的引用反映在其他对象的refcount中。
-通常其他对象可以在多个其他对象中共享,在refcount中对每个ref进行计数。
-没有对象拥有对方的所有权。
-共享状态或写时拷贝。
-创建/销毁需要一个锁和一个引用计数。
用法
GstRealPad -> GstCaps
GstBuffer -> GstCaps
GstEvent -> GstCaps
GstEvent -> GstObject
GstMessage -> GstCaps
GstMessage -> GstObject
生命周期
存在两个未链接的对象。
     +---------+        +---------+
*--->| object1 |   *--->| object2 |
     |      *  |        |         |
     |        1|        |        1|
     +---------+        +---------+
建立single-reffed 关系

在第一个对象上的调用函数将第二个对象附加到第一个对象上。第二个对象被引用,并使用以下算法更新第一个对象中的指针:

LOCK (object1);
if (object1->pointer)
  _unref (object1->pointer);
object1->pointer = _ref (object2);
UNLOCK (object1);

释放第一个对象上的锁后,不确定对象2是否仍对对象1有引用。

     +---------+        +---------+
*--->| object1 |   *--->| object2 |
     |       *--------->|         |
     |        1|        |        2|
     +---------+        +---------+
使用single-reffed关系

访问object2的唯一方法是持有对它的引用或从object1获取引用。可以像这样读取由object1指向的对象:

LOCK (object1);
object2 = object1->pointer;
_ref (object2);
UNLOCK (object1);

… use object2 …
_unref (object2);

根据对象的类型,可以通过写时复制或直接在对象中进行修改。
写入复制实际上只能通过以下方式完成:

LOCK (object1);
object2 = object1->pointer;
object2 = _copy_on_write (object2);
... make modifications to object2 ...
UNLOCK (object1);

Releasing the lock has only a very small window where the copy_on_write
actually does not perform a copy:

LOCK (object1);
object2 = object1->pointer;
_ref (object2);
UNLOCK (object1);

/* object2 now has at least 2 refcounts making the next
copy-on-write make a real copy, unless some other thread writes
another object2 to object1 here … */

object2 = _copy_on_write (object2);

/* make modifications to object2 … */

LOCK (object1);
if (object1->pointer != object2) {
  if (object1->pointer)
    _unref (object1->pointer);
  object1->pointer = gst_object_ref (object2);
}
UNLOCK (object1);
销毁single-reffed关系

以下算法删除了object1和object2之间的single-reffed链接。

LOCK (object1);
_unref (object1->pointer);
object1->pointer = NULL;
UNLOCK (object1);

这将再次产生以下初始状态:

   +---------+        +---------+
*--->| object1 |   *--->| object2 |
     |      *  |        |         |
     |        1|        |        1|
     +---------+        +---------+

unreffed 的关系

     +---------+        +---------+
*--->| object1 |   *--->| object2 |
     |       *--------->|         |
     |        1|<---------*      1|
     +---------+        +---------+
特性
- 两个对象相互引用
- 两个对象只能有1个对另一个对象的引用。
- 用LOCK保护的参考字段
- 每个对象持有的引用不会反映在另一个对象的引用计数中。
- 没有对象拥有另一个的所有权。
- 通常,每个对象都由不同的父对象拥有。
- 创建/销毁需要两个嵌套锁,并且没有引用计数。
用法
  • 当链接比对象的存在不那么重要时,使用这种类型的链接。如果放置了一个对象,则链接也是如此。
    GstRealPad <-> GstRealPad(首先使用srcpad锁)

生命周期

存在两个未链接的对象。
     +---------+        +---------+
*--->| object1 |   *--->| object2 |
     |       * |        |         |
     |        1|        | *      1|
     +---------+        +---------+
建立unreffed 的关系

由于我们需要两个锁,因此获取这些锁的顺序非常重要,否则可能会导致死锁。必须为所有未引用的关系定义此锁定顺序。在这些示例中,我们总是先锁定对象1,然后再锁定对象2。

LOCK (object1);
LOCK (object2);
object2->refpointer = object1;
object1->refpointer = object2;
UNLOCK (object2);
UNLOCK (object1);
使用unreffed 关系

读取需要拿一把锁,然后读取对应的对象。同样,我们需要在释放锁之前引用该对象。

LOCK (object1);
object2 = _ref (object1->refpointer);
UNLOCK (object1);

.. use object2 ..
_unref (object2);
销毁unreffed 的关系

由于锁定顺序,在销毁此关系时我们需要小心。
仅保留对object1的引用时:

LOCK (object1);
LOCK (object2);
object1->refpointer->refpointer = NULL;
object1->refpointer = NULL;
UNLOCK (object2);
UNLOCK (object1);

当只持有object2的引用时,我们需要先获得另一个对象的句柄,这样我们就可以先锁定它。这里有一个空窗期,我们需要释放所有的锁,然后这种关系可能是无效的。为了解决这个问题,我们在获得两个锁之后检查关系,如果关系改变了就重试。

retry:
  LOCK (object2);
  object1 = _ref (object2->refpointer);
  UNLOCK (object2);
  .. things can change here ..
  LOCK (object1);
  LOCK (object2);
  if (object1 == object2->refpointer) {
    /* relation unchanged */
    object1->refpointer->refpointer = NULL;
    object1->refpointer = NULL;
  }
  else {
    /* relation changed.. retry */
    UNLOCK (object2);
    UNLOCK (object1);
    _unref (object1);
    goto retry;
  }
  UNLOCK (object2);
  UNLOCK (object1);
  _unref (object1);

/* When references are held to both objects. Note that it is not possible to
get references to both objects with the locks released since when the
references are taken and the locks are released, a concurrent update might
have changed the link, making the references not point to linked objects. */

LOCK (object1);
LOCK (object2);
if (object1->refpointer == object2) {
  object2->refpointer = NULL;
  object1->refpointer = NULL;
}
else {
  .. objects are not linked ..
}
UNLOCK (object2);
UNLOCK (object1);

double-reffed关系

     +---------+        +---------+
*--->| object1 |   *--->| object2 |
     |       *--------->|         |
     |        2|<---------*      2|
     +---------+        +---------+
特性
- 两个对象相互引用
- 用LOCK保护的参考字段
- 每个对象拥有的引用将反映在另一个对象的引用计数中。
- 没有对象拥有另一个的所有权。
- 通常,每个对象都由不同的父对象拥有。
- 创建/销毁需要两个锁和两个引用计数。
用法

未在GStreamer中使用。

生命周期
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值