【GStreamer】GstBus和GstMessage的简单分析

模块的简单分析
【GStreamer】GstAllocator的简单分析
【GStreamer】GstMemory的简单分析
【GStreamer】GstBuffer的简单分析
【GStreamer】GstPad的简单分析
【GStreamer】GstElement的简单分析
【GStreamer】GstBus和GstMessage的简单分析
【GStreamer】GstDevice和GstPlugin的简单分析

1.GstBus

GstBus用于在应用程序和管道pipeline之间传递消息,允许应用程序异步地接收来自管道或元素的通知、错误、警告和状态变化等消息。由元素运行时生成的消息,会被发布到与管道关联的bus中,应用程序可以通过轮询或信号机制从bus中获取消息,这些消息可以是同步的或异步的,之后根据消息类型执行相应的操作

1.1 类定义

(1)GstBusFlags

typedef enum {
  // 当前bus正在丢弃所有message
  GST_BUS_FLUSHING      = (GST_OBJECT_FLAG_LAST << 0),
  /* padding */
  GST_BUS_FLAG_LAST     = (GST_OBJECT_FLAG_LAST << 1)
} GstBusFlags;

(2)GstBusSyncReply
总线同步处理函数的返回值

typedef enum
{
  GST_BUS_DROP = 0,	// 丢弃msg
  GST_BUS_PASS = 1,	// 将msg给到异步队列
  GST_BUS_ASYNC = 2	// 将msg给到异步队列,并继续处理当前消息
} GstBusSyncReply;

(3)_GstBus和_GstBusClass

struct _GstBus
{
  GstObject         object;

  /*< private >*/
  GstBusPrivate    *priv;

  gpointer _gst_reserved[GST_PADDING];
};

struct _GstBusClass
{
  GstObjectClass parent_class;

  /* signals */

  /**
   * GstBusClass::message:
   * @bus: the #GstBus
   * @message: the message that has been posted asynchronously
   *
   * A message has been posted on the bus.
   */
  void (*message)       (GstBus *bus, GstMessage *message);

  /**
   * GstBusClass::sync_message:
   * @bus: the #GstBus
   * @message: the message that has been posted synchronously
   *
   * A message has been posted on the bus.
   */
  void (*sync_message)  (GstBus *bus, GstMessage *message);

  /*< private >*/
  gpointer _gst_reserved[GST_PADDING];
};

1.2 常用函数

(1)gst_bus_pop
从bus队列中取出信息

GstMessage *
gst_bus_pop (GstBus * bus)
{
  g_return_val_if_fail (GST_IS_BUS (bus), NULL);

  return gst_bus_timed_pop_filtered (bus, 0, GST_MESSAGE_ANY);
}

gst_bus_timed_pop_filtered

GstMessage *
gst_bus_timed_pop_filtered (GstBus * bus, GstClockTime timeout,
    GstMessageType types)
{
  GstMessage *message;
  gint64 now, then;
  gboolean first_round = TRUE;
  GstClockTime elapsed = 0;

  g_return_val_if_fail (GST_IS_BUS (bus), NULL);
  g_return_val_if_fail (types != 0, NULL);
  g_return_val_if_fail (timeout == 0 || bus->priv->poll != NULL, NULL);

  g_mutex_lock (&bus->priv->queue_lock);
  // 消息轮询
  while (TRUE) {
    gint ret;

    GST_LOG_OBJECT (bus, "have %" G_GSIZE_FORMAT " messages",
        gst_vec_deque_get_length (bus->priv->queue));
    // 从bus队列中取出消息
    while ((message = gst_vec_deque_pop_head (bus->priv->queue))) {
      if (bus->priv->poll) {
        while (!gst_poll_read_control (bus->priv->poll)) {
          if (errno == EWOULDBLOCK) {
            /* Retry, this can happen if pushing to the queue has finished,
             * popping here succeeded but writing control did not finish
             * before we got to this line. */
            /* Give other threads the chance to do something */
            g_thread_yield ();
            continue;
          } else {
            /* This is a real error and means that either the bus is in an
             * inconsistent state, or the GstPoll is invalid. GstPoll already
             * prints a critical warning about this, no need to do that again
             * ourselves */
            break;
          }
        }
      }

      GST_DEBUG_OBJECT (bus, "got message %p, %s from %s, type mask is %u",
          message, GST_MESSAGE_TYPE_NAME (message),
          GST_MESSAGE_SRC_NAME (message), (guint) types);
      // 检查消息的类型是否匹配types,如果匹配,则跳转到beach标签,返回该消息
      if ((GST_MESSAGE_TYPE (message) & types) != 0) {
        /* Extra check to ensure extended types don't get matched unless
         * asked for */
        if ((!GST_MESSAGE_TYPE_IS_EXTENDED (message))
            || (types & GST_MESSAGE_EXTENDED)) {
          /* exit the loop, we have a message */
          goto beach;
        }
      }
      // 消息类型不匹配,释放消息,继续遍历
      GST_DEBUG_OBJECT (bus, "discarding message, does not match mask");
      gst_message_unref (message);
      message = NULL;
    }

    /* no need to wait, exit loop */
    if (timeout == 0)
      break;

    else if (timeout != GST_CLOCK_TIME_NONE) {
      if (first_round) {
        then = g_get_monotonic_time ();
        first_round = FALSE;
      } else {
        now = g_get_monotonic_time ();

        elapsed = (now - then) * GST_USECOND;

        if (elapsed > timeout)
          break;
      }
    }

    /* only here in timeout case */
    g_assert (bus->priv->poll);
    g_mutex_unlock (&bus->priv->queue_lock);
    ret = gst_poll_wait (bus->priv->poll, timeout - elapsed);
    g_mutex_lock (&bus->priv->queue_lock);

    if (ret == 0) {
      GST_DEBUG_OBJECT (bus, "timed out, breaking loop");
      break;
    } else {
      GST_DEBUG_OBJECT (bus, "we got woken up, recheck for message");
    }
  }

beach:

  g_mutex_unlock (&bus->priv->queue_lock);

  return message;
}

(2)gst_bus_peek
查看消息但不会移除,适合需要预览消息而不改变队列状态的场景

GstMessage *
gst_bus_peek (GstBus * bus)
{
  GstMessage *message;

  g_return_val_if_fail (GST_IS_BUS (bus), NULL);

  g_mutex_lock (&bus->priv->queue_lock);
  // 这里是peek而不是pop,所以指示从队列中获取消息而不会清除
  message = gst_vec_deque_peek_head (bus->priv->queue);
  if (message)
    gst_message_ref (message);
  g_mutex_unlock (&bus->priv->queue_lock);

  GST_DEBUG_OBJECT (bus, "peek on bus, got message %p", message);

  return message;
}

(3)gst_bus_post
将一个消息发布到总线上

gboolean
gst_bus_post (GstBus * bus, GstMessage * message)
{
  GstBusSyncReply reply = GST_BUS_PASS;
  gboolean emit_sync_message;
  SyncHandler *sync_handler = NULL;

  g_return_val_if_fail (GST_IS_BUS (bus), FALSE);
  g_return_val_if_fail (GST_IS_MESSAGE (message), FALSE);

  GST_DEBUG_OBJECT (bus, "[msg %p] posting on bus %" GST_PTR_FORMAT, message,
      message);

  /* check we didn't accidentally add a public flag that maps to same value */
  g_assert (!GST_MINI_OBJECT_FLAG_IS_SET (message,
          GST_MESSAGE_FLAG_ASYNC_DELIVERY));

  GST_OBJECT_LOCK (bus);
  /* check if the bus is flushing */
  // 检查总线状态
  if (GST_OBJECT_FLAG_IS_SET (bus, GST_BUS_FLUSHING))
    goto is_flushing;
  // 获取同步处理函数
  if (bus->priv->sync_handler)
    sync_handler = sync_handler_ref (bus->priv->sync_handler);
  emit_sync_message = bus->priv->num_sync_message_emitters > 0;
  GST_OBJECT_UNLOCK (bus);

  /* first call the sync handler if it is installed */
  // 调用同步处理函数
  if (sync_handler)
    reply = sync_handler->handler (bus, message, sync_handler->user_data);

  /* emit sync-message if requested to do so via
     gst_bus_enable_sync_message_emission. terrible but effective */
  // 如果需要发出同步消息且同步处理函数没有丢弃消息,发送sync-message
  if (emit_sync_message && reply != GST_BUS_DROP
      && (!sync_handler
          || sync_handler->handler != gst_bus_sync_signal_handler))
    gst_bus_sync_signal_handler (bus, message, NULL);

  g_clear_pointer (&sync_handler, sync_handler_unref);

  /* If this is a bus without async message delivery always drop the message.
   * If the sync handler returned GST_BUS_DROP it is responsible of unreffing
   * the message, otherwise do it ourself. */
  if (!bus->priv->poll && reply != GST_BUS_DROP) {
    reply = GST_BUS_DROP;
    gst_message_unref (message);
  }

  /* now see what we should do with the message */
  // 处理消息
  switch (reply) {
    case GST_BUS_DROP: // 丢弃消息
      /* drop the message */
      GST_DEBUG_OBJECT (bus, "[msg %p] dropped", message);
      break;
    case GST_BUS_PASS:{ // 将消息放入异步队列
      g_mutex_lock (&bus->priv->queue_lock);
      gsize length = gst_vec_deque_get_length (bus->priv->queue);
      if (G_UNLIKELY (length > 0 && length % WARN_QUEUE_SIZE == 0)) {
        GST_WARNING_OBJECT (bus,
            "queue overflows with %" G_GSIZE_FORMAT " messages. "
            "Application is too slow or is not handling messages. "
            "Please add a message handler, otherwise the queue will grow "
            "infinitely.", length);
      }
      /* pass the message to the async queue, refcount passed in the queue */
      GST_DEBUG_OBJECT (bus, "[msg %p] pushing on async queue", message);
      gst_vec_deque_push_tail (bus->priv->queue, message);
      gst_poll_write_control (bus->priv->poll);
      GST_DEBUG_OBJECT (bus, "[msg %p] pushed on async queue", message);
      g_mutex_unlock (&bus->priv->queue_lock);

      break;
    }
    case GST_BUS_ASYNC: // 进行异步处理,等待消息被处理完成
    {
      /* async delivery, we need a mutex and a cond to block
       * on */
      GCond *cond = GST_MESSAGE_GET_COND (message);
      GMutex *lock = GST_MESSAGE_GET_LOCK (message);

      g_cond_init (cond);
      g_mutex_init (lock);

      GST_MINI_OBJECT_FLAG_SET (message, GST_MESSAGE_FLAG_ASYNC_DELIVERY);

      GST_DEBUG_OBJECT (bus, "[msg %p] waiting for async delivery", message);

      /* now we lock the message mutex, send the message to the async
       * queue. When the message is handled by the app and destroyed,
       * the cond will be signalled and we can continue */
      g_mutex_lock (lock);

      g_mutex_lock (&bus->priv->queue_lock);
      gst_vec_deque_push_tail (bus->priv->queue, message);
      gst_poll_write_control (bus->priv->poll);
      g_mutex_unlock (&bus->priv->queue_lock);

      /* now block till the message is freed */
      g_cond_wait (cond, lock);

      /* we acquired a new ref from gst_message_dispose() so we can clean up */
      g_mutex_unlock (lock);

      GST_DEBUG_OBJECT (bus, "[msg %p] delivered asynchronously", message);

      GST_MINI_OBJECT_FLAG_UNSET (message, GST_MESSAGE_FLAG_ASYNC_DELIVERY);

      g_mutex_clear (lock);
      g_cond_clear (cond);

      gst_message_unref (message);
      break;
    }
    default: // 无效的返回值,直接释放消息
      g_warning ("invalid return from bus sync handler");
      gst_message_unref (message);
      break;
  }
  return TRUE;

  /* ERRORS */
is_flushing:
  {
    GST_DEBUG_OBJECT (bus, "bus is flushing");
    GST_OBJECT_UNLOCK (bus);
    gst_message_unref (message);

    return FALSE;
  }
}

(4)gst_bus_create_watch
创建bus的监视器

GSource *
gst_bus_create_watch (GstBus * bus)
{
  GSource *source;

  g_return_val_if_fail (GST_IS_BUS (bus), NULL);
  g_return_val_if_fail (bus->priv->poll != NULL, NULL);

  GST_OBJECT_LOCK (bus);
  source = gst_bus_create_watch_unlocked (bus);
  GST_OBJECT_UNLOCK (bus);

  return source;
}

gst_bus_create_watch_unlocked

static GSource *
gst_bus_create_watch_unlocked (GstBus * bus)
{
  GstBusSource *source;

  if (bus->priv->gsource) {
    GST_ERROR_OBJECT (bus,
        "Tried to add new GSource while one was already there");
    return NULL;
  }

  bus->priv->gsource = g_source_new (&gst_bus_source_funcs,
      sizeof (GstBusSource));
  source = (GstBusSource *) bus->priv->gsource;

  g_source_set_name ((GSource *) source, "GStreamer message bus watch");
  g_source_set_dispose_function ((GSource *) source, gst_bus_source_dispose);

  source->bus = gst_object_ref (bus);
  g_source_add_poll ((GSource *) source, &bus->priv->pollfd);

  return (GSource *) source;
}

(5)gst_bus_poll
从总线中轮询消息,这个函数会阻塞,直到指定的消息类型出现在总线上,或者超时

GstMessage *
gst_bus_poll (GstBus * bus, GstMessageType events, GstClockTime timeout)
{
  GstBusPollData *poll_data; // 存储轮询的数据
  GstMessage *ret;
  gulong id;

  g_return_val_if_fail (GST_IS_BUS (bus), NULL);

  poll_data = g_new (GstBusPollData, 1);
  poll_data->source_running = TRUE; // 表示轮询正在进行
  poll_data->loop = g_main_loop_new (NULL, FALSE); // 创建新的loop
  poll_data->events = events; // 需要监听的消息类型
  poll_data->message = NULL;

  if (timeout != GST_CLOCK_TIME_NONE)
    poll_data->timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
        timeout / GST_MSECOND, (GSourceFunc) poll_timeout, poll_data,
        (GDestroyNotify) poll_destroy_timeout);
  else
    poll_data->timeout_id = 0;
  // 设置回调函数poll_func(),当总线有新消息时,会调用poll_func()
  id = g_signal_connect_data (bus, "message", G_CALLBACK (poll_func), poll_data,
      (GClosureNotify) poll_destroy, 0); 

  /* these can be nested, so it's ok */
  gst_bus_add_signal_watch (bus);

  GST_DEBUG ("running mainloop %p", poll_data->loop);
  // 启动主循环,阻塞当前线程,直到接收到消息或超时
  g_main_loop_run (poll_data->loop);
  GST_DEBUG ("mainloop stopped %p", poll_data->loop);
  // 移除信号监视器
  gst_bus_remove_signal_watch (bus);

  /* holds a ref */
  ret = poll_data->message;

  if (poll_data->timeout_id)
    g_source_remove (poll_data->timeout_id);

  /* poll_data will be freed now */
  // 断开信号连接
  g_signal_handler_disconnect (bus, id);

  GST_DEBUG_OBJECT (bus, "finished poll with message %p", ret);

  return ret;
}

2.GstMessage

GstMessage用于在管道pipeline和应用程序之间传递信息,它封装了各种类型的消息,例如错误、警告、状态变化、流结束(EOS)等,通过GstBus,这些消息可以从管道传递到应用程序,以便应用程序能够对pipeline的行为做出响应

2.1 类定义

(1)GstMessageType
GstMessageType描述了消息的类型

typedef enum
{
  GST_MESSAGE_UNKNOWN           = 0,		// 未知消息类型
  GST_MESSAGE_EOS               = (1 << 0),	// 流结束
  GST_MESSAGE_ERROR             = (1 << 1),	// 错误消息
  GST_MESSAGE_WARNING           = (1 << 2),	// 警告信息
  GST_MESSAGE_INFO              = (1 << 3),	// 信息消息
  GST_MESSAGE_TAG               = (1 << 4),	// 标签消息
  GST_MESSAGE_BUFFERING         = (1 << 5),	// 缓冲消息
  GST_MESSAGE_STATE_CHANGED     = (1 << 6),	// 状态改变消息
  GST_MESSAGE_STATE_DIRTY       = (1 << 7),	// 状态脏消息
  GST_MESSAGE_STEP_DONE         = (1 << 8),	// 步进完成消息
  GST_MESSAGE_CLOCK_PROVIDE     = (1 << 9),	// 时钟提供消息
  GST_MESSAGE_CLOCK_LOST        = (1 << 10), // 时钟丢失消息 
  GST_MESSAGE_NEW_CLOCK         = (1 << 11), // 新时钟消息
  GST_MESSAGE_STRUCTURE_CHANGE  = (1 << 12), // 结构变化消息
  GST_MESSAGE_STREAM_STATUS     = (1 << 13), // 流状态消息
  GST_MESSAGE_APPLICATION       = (1 << 14), // 应用程序消息
  GST_MESSAGE_ELEMENT           = (1 << 15), // 元素消息
  GST_MESSAGE_SEGMENT_START     = (1 << 16), // 段开始消息
  GST_MESSAGE_SEGMENT_DONE      = (1 << 17), // 段结束消息
  GST_MESSAGE_DURATION_CHANGED  = (1 << 18), // 持续事件变化消息
  GST_MESSAGE_LATENCY           = (1 << 19), // 延迟消息
  GST_MESSAGE_ASYNC_START       = (1 << 20), // 异步开始消息
  GST_MESSAGE_ASYNC_DONE        = (1 << 21), // 异步结束消息
  GST_MESSAGE_REQUEST_STATE     = (1 << 22), // 请求状态消息
  GST_MESSAGE_STEP_START        = (1 << 23), // 步进开始消息
  GST_MESSAGE_QOS               = (1 << 24), // 服务质量消息
  GST_MESSAGE_PROGRESS          = (1 << 25), // 进度消息
  GST_MESSAGE_TOC               = (1 << 26), // 目录消息
  GST_MESSAGE_RESET_TIME        = (1 << 27), // 重置时间消息
  GST_MESSAGE_STREAM_START      = (1 << 28), // 流开始消息
  GST_MESSAGE_NEED_CONTEXT      = (1 << 29), // 需要上下文消息
  GST_MESSAGE_HAVE_CONTEXT      = (1 << 30), // 拥有上下文消息
  GST_MESSAGE_EXTENDED          = (gint) (1u << 31), // 扩展消息类型
  GST_MESSAGE_DEVICE_ADDED      = GST_MESSAGE_EXTENDED + 1, // 设备添加
  GST_MESSAGE_DEVICE_REMOVED    = GST_MESSAGE_EXTENDED + 2, // 设备移除
  GST_MESSAGE_PROPERTY_NOTIFY   = GST_MESSAGE_EXTENDED + 3, // 属性通知
  GST_MESSAGE_STREAM_COLLECTION = GST_MESSAGE_EXTENDED + 4, // 流集合
  GST_MESSAGE_STREAMS_SELECTED  = GST_MESSAGE_EXTENDED + 5, // 流选择
  GST_MESSAGE_REDIRECT          = GST_MESSAGE_EXTENDED + 6, // 重定向
  GST_MESSAGE_DEVICE_CHANGED    = GST_MESSAGE_EXTENDED + 7, // 设备更改
  GST_MESSAGE_INSTANT_RATE_REQUEST = GST_MESSAGE_EXTENDED + 8, // 即时速率请求
  GST_MESSAGE_ANY               = (gint) (0xffffffff)
} GstMessageType;

(2)GstStreamStatusType
流状态类型

typedef enum {
  GST_STREAM_STATUS_TYPE_CREATE   = 0, // 创建流
  GST_STREAM_STATUS_TYPE_ENTER    = 1, // 流进入某个状态
  GST_STREAM_STATUS_TYPE_LEAVE    = 2, // 流离开某个状态
  GST_STREAM_STATUS_TYPE_DESTROY  = 3, // 流被销毁

  GST_STREAM_STATUS_TYPE_START    = 8, // 流开始处理数据
  GST_STREAM_STATUS_TYPE_PAUSE    = 9, // 流暂停处理数据
  GST_STREAM_STATUS_TYPE_STOP     = 10 // 流停止处理数据
} GstStreamStatusType;

(3)GstProgressType
进度消息的具体类型

typedef enum {
  GST_PROGRESS_TYPE_START    = 0, // 操作已开始
  GST_PROGRESS_TYPE_CONTINUE = 1, // 操作正在进行中
  GST_PROGRESS_TYPE_COMPLETE = 2, // 操作已完成
  GST_PROGRESS_TYPE_CANCELED = 3, // 操作已被取消
  GST_PROGRESS_TYPE_ERROR    = 4  // 操作中发生错误
} GstProgressType;

(4)_GstMessage
message表示在总线上传递的消息,用于在pipeline和应用程序之间进行通信,例如通知应用程序某个事件的发生或请求应用程序采取某些操作

struct _GstMessage
{
  GstMiniObject   mini_object;

  /*< public > *//* with COW */
  GstMessageType  type;			// 消息类型
  guint64         timestamp;	// 消息时间戳
  GstObject      *src;			// 消息的来源对象
  guint32         seqnum;		// 消息序列号

  /*< private >*//* with MESSAGE_LOCK */
  // 消息锁
  GMutex          lock;                 /* lock and cond for async delivery */
  // 条件变量
  GCond           cond;
};

2.2 常用函数

(1)gst_message_new_custom
gst_message_new_custom()用于创建一个自定义的message对象,其核心是为消息分配内存并初始化,同时将传入的structure附加到消息中

GstMessage *
gst_message_new_custom (GstMessageType type, GstObject * src,
    GstStructure * structure)
{
  GstMessageImpl *message;
  // 分配msg内存
  message = g_new0 (GstMessageImpl, 1);

  GST_CAT_LOG (GST_CAT_MESSAGE, "source %s: creating new message %p %s",
      (src ? GST_OBJECT_NAME (src) : "NULL"), message,
      gst_message_type_get_name (type));
  // 传入的structure必须是独立对象,不能被其他父对象持有
  if (structure) {
    /* structure must not have a parent */
    if (!gst_structure_set_parent_refcount (structure,
            &message->message.mini_object.refcount))
      goto had_parent;
  }
  // 初始化消息的基本字段,包括消息类型、源对象等
  gst_message_init (message, type, src);

  GST_MESSAGE_STRUCTURE (message) = structure;

  return GST_MESSAGE_CAST (message);

  /* ERRORS */
had_parent:
  {
    g_free (message);
    g_warning ("structure is already owned by another object");
    return NULL;
  }
}

(2)gst_message_init
初始化一条消息

static void
gst_message_init (GstMessageImpl * message, GstMessageType type,
    GstObject * src)
{
  gst_mini_object_init (GST_MINI_OBJECT_CAST (message), 0, _gst_message_type,
      (GstMiniObjectCopyFunction) _gst_message_copy,
      (GstMiniObjectDisposeFunction) _gst_message_dispose,
      (GstMiniObjectFreeFunction) _gst_message_free);

  GST_MESSAGE_TYPE (message) = type;
  if (src)
    gst_object_ref (src);
  GST_MESSAGE_SRC (message) = src;
  GST_MESSAGE_TIMESTAMP (message) = GST_CLOCK_TIME_NONE;
  GST_MESSAGE_SEQNUM (message) = gst_util_seqnum_next ();
}

(3)gst_message_new_eos
新建一条eos消息

GstMessage *
gst_message_new_eos (GstObject * src)
{
  GstMessage *message;

  message = gst_message_new_custom (GST_MESSAGE_EOS, src, NULL);

  return message;
}

(4)gst_message_new_info
创建一个info消息,这种消息通常包括的是状态更新、警告或调试信息

GstMessage *
gst_message_new_info (GstObject * src, GError * error, const gchar * debug)
{
  return gst_message_new_info_with_details (src, error, debug, NULL);
}

(5)gst_message_new_state_changed
新建一条状态变化的消息

GstMessage *
gst_message_new_state_changed (GstObject * src,
    GstState oldstate, GstState newstate, GstState pending)
{
  GstMessage *message;
  GstStructure *structure;

  structure = gst_structure_new_static_str ("GstMessageStateChanged",
      "old-state", GST_TYPE_STATE, (gint) oldstate,
      "new-state", GST_TYPE_STATE, (gint) newstate,
      "pending-state", GST_TYPE_STATE, (gint) pending, NULL);
  message = gst_message_new_custom (GST_MESSAGE_STATE_CHANGED, src, structure);

  return message;
}

(6)gst_message_new_stream_start
新建一条流开始消息

GstMessage *
gst_message_new_stream_start (GstObject * src)
{
  GstMessage *message;
  GstStructure *s;

  s = gst_structure_new_static_str_empty ("GstMessageStreamStart");
  message = gst_message_new_custom (GST_MESSAGE_STREAM_START, src, s);

  return message;
}

(7)gst_message_new_device_added
新建一条添加新设备的消息

GstMessage *
gst_message_new_device_added (GstObject * src, GstDevice * device)
{
  GstMessage *message;
  GstStructure *structure;

  g_return_val_if_fail (device != NULL, NULL);
  g_return_val_if_fail (GST_IS_DEVICE (device), NULL);

  structure = gst_structure_new_static_str ("GstMessageDeviceAdded",
      "device", GST_TYPE_DEVICE, device, NULL);
  message = gst_message_new_custom (GST_MESSAGE_DEVICE_ADDED, src, structure);

  return message;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值