2

我正在尝试为 Gstreamer 缓冲区实现自定义队列。问题是当我尝试出队时,似乎我失去了队列的头部。每当我尝试两次出队时,都会出现分段错误。我还注意到 head 总是等于 head->next。现在我不确定入队或出队是否有问题。请帮帮我。谢谢你。

typedef struct _GstBUFFERQUEUE GstBufferQueue;

struct _GstBUFFERQUEUE {
  GstBuffer *buf;
  guint buf_size;
  struct _GstBUFFERQUEUE *next;
};

void enqueue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer *buf)
{
  if (*queue_size == 0)
  {
    *head = malloc(sizeof(GstBufferQueue));
    (*head)->buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE(buf));
    (*head)->buf = gst_buffer_copy(buf); 
    *tail = *head;
  }
  else
  {
    if ((*tail)->next = malloc(sizeof(GstBufferQueue))) {
        (*tail)->next->buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE(buf));
        (*tail)->next->buf = gst_buffer_copy(buf);
        (*tail) = (*tail)->next;
    }
    else {
        GST_WARNING("Error allocating memory for new buffer in queue");
    } 
  } 
  (*tail)->next = NULL; 
  (*queue_size)++;

}

void dequeue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer **buf)
{
  GstBufferQueue **tmpPtr = head;
  GstBufferQueue **nextPtr;
  *nextPtr = (*head)->next; 
  *buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE((*tmpPtr)->buf));
  *buf = gst_buffer_copy((*tmpPtr)->buf);
  gst_buffer_unref((*tmpPtr)->buf);
  free((*tmpPtr));
  *head = *nextPtr;

  if ((*head) == NULL)
     (*tail) = NULL;

   (*queue_size)--;   
}
4

3 回答 3

5

当通过添加足够的伪基础设施来模拟 GST 系统转换为可编译代码时,GCC 会出现一个警告,这几乎肯定是您的问题的根源:

gstq.c: In function ‘dequeue_gstbuffer’:
gstq.c:73:12: warning: ‘nextPtr’ is used uninitialized in this function [-Wuninitialized]

这些行是:

72  GstBufferQueue **nextPtr;
73  *nextPtr = (*head)->next;

在这些行中,您需要:

GstBufferQueue *nextPtr = (*head)->next;

您还需要使用:

(*head)->next = nextPtr;

注意你的编译器警告。如果你的编译器没有警告,让它这样做。如果您不能使其警告,请获得更好的编译器。


SSCCE

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#define GST_BUFFER_SIZE(x)  sizeof(x)
#define GST_WARNING(x)      fprintf(stderr, "%s\n", x)

typedef struct GstBuffer { int value; } GstBuffer;
typedef unsigned int guint;

static GstBuffer *gst_buffer_try_new_and_alloc(int size)
{
    GstBuffer *buf = malloc(sizeof(GstBuffer));
    assert(buf != 0);
    buf->value = size;
    return buf;
}

static GstBuffer *gst_buffer_copy(const GstBuffer *buf)
{
    GstBuffer *new_buf = malloc(sizeof(GstBuffer));
    assert(new_buf != 0);
    new_buf->value = buf->value;
    return new_buf;
}

static void gst_buffer_unref(GstBuffer *buf)
{
    buf->value = -1;
}

typedef struct _GstBUFFERQUEUE GstBufferQueue;

struct _GstBUFFERQUEUE {
  GstBuffer *buf;
  guint buf_size;
  struct _GstBUFFERQUEUE *next;
};

extern void enqueue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer *buf);
extern void dequeue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer **buf);

void enqueue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer *buf)
{
  if (*queue_size == 0)
  {
    *head = malloc(sizeof(GstBufferQueue));
    (*head)->buf = gst_buffer_try_new_and_alloc(GST_BUFFER_SIZE(buf));
    (*head)->buf = gst_buffer_copy(buf); 
    *tail = *head;
  }
  else
  {
    if (((*tail)->next = malloc(sizeof(GstBufferQueue))) != 0)
    {
        (*tail)->next->buf = gst_buffer_try_new_and_alloc(GST_BUFFER_SIZE(buf));
        (*tail)->next->buf = gst_buffer_copy(buf);
        (*tail) = (*tail)->next;
    }
    else
    {
        GST_WARNING("Error allocating memory for new buffer in queue");
    } 
  } 
  (*tail)->next = NULL; 
  (*queue_size)++;
}

void dequeue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer **buf)
{
  GstBufferQueue **tmpPtr = head;
  GstBufferQueue  *nextPtr;
  nextPtr = (*head)->next; 
  *buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE((*tmpPtr)->buf));
  *buf = gst_buffer_copy((*tmpPtr)->buf);
  gst_buffer_unref((*tmpPtr)->buf);
  free((*tmpPtr));
  *head = nextPtr;

  if ((*head) == NULL)
     (*tail) = NULL;

   (*queue_size)--;   
}

int main(void)
{
    GstBufferQueue *q_head = 0;
    GstBufferQueue *q_tail = 0;
    guint           q_size = 0;

    for (int i = 0; i < 10; i++)
    {
        GstBuffer *buf = gst_buffer_try_new_and_alloc(i + 100);
        enqueue_gstbuffer(&q_head, &q_tail, &q_size, buf);
        printf("EQ: %d\n", buf->value);
        free(buf);
        if (i % 2 == 1)
        {
            GstBuffer *buf;
            dequeue_gstbuffer(&q_head, &q_tail, &q_size, &buf);
            printf("DQ: %d\n", buf->value);
            free(buf);
        }
    }

    while (q_size > 0)
    {
        GstBuffer *buf;
        dequeue_gstbuffer(&q_head, &q_tail, &q_size, &buf);
        printf("DQ: %d\n", buf->value);
        free(buf);
    }

    printf("All done\n");
    return(0);
}

输出

EQ: 100
EQ: 101
DQ: 100
EQ: 102
EQ: 103
DQ: 101
EQ: 104
EQ: 105
DQ: 102
EQ: 106
EQ: 107
DQ: 103
EQ: 108
EQ: 109
DQ: 104
DQ: 105
DQ: 106
DQ: 107
DQ: 108
DQ: 109
All done

请注意,上面的 SSCCE 代码比筛子更容易泄漏。我没有修复泄漏的计划,因为它们在模拟 GST 缓冲区管理的代码中。请检查您的代码是否受到内存泄漏的影响。


我认为你应该以不同的方式包装你的“队列”。你所说的 aGstBufferQueue应该真的是 a GstBufferQueueItem,你的实际GstBufferQueue应该包含 head 和 tail 指针,以及大小。您将指向(修订后的) and 函数的指针传递GstBufferQueueenqueue_gstbuffer()anddequeue_gstbuffer()函数,而不是传递 3 个单独的参数。

typedef struct GstBufferQueueItem GstBufferQueueItem;

struct GstBufferQueueItem
{
  GstBuffer *buf;
  guint buf_size;
  GstBufferQueueItem *next;
};

typedef struct GstBufferQueue GstBufferQueue;

struct GstBufferQueue
{
    GstBufferQueueItem *head;
    GstBufferQueueItem *tail;
    guint               size;
};

// Uncompiled - but to give you an idea
void dequeue_gstbuffer(GstBufferQueue *q, GstBuffer **buf)
{
    GstBufferQueueItem *item = q->head;
    GstBufferQueueItem *next = item->next; 
    *buf = gst_buffer_try_new_and_alloc(GST_BUFFER_SIZE(item->buf));
    *buf = gst_buffer_copy(item->buf);
    gst_buffer_unref(item->buf);
    free(item);
    q->head = next;

    if (q->head == NULL)
        q->tail = NULL;

    q->size--;   
}

请注意,这些名称避免使用前导下划线。这样的名字是危险的。带有下划线和大写字母的名称保留用于任何目的的实现。带有下划线和小写字母的名称被保留为不同的单词,但使用其中任何一个都是狡猾的(尽管标准很少提及下划线和数字,但不要与它们玩游戏 - 将前导下划线视为“保留用于系统”,除非你写的是“系统”)。

ISO/IEC 9899:2011 §7.1.3 保留标识符

  • 以下划线和大写字母或另一个下划线开头的所有标识符始终保留用于任何用途。
  • 所有以下划线开头的标识符始终保留用作普通和标记名称空间中具有文件范围的标识符。
于 2012-12-31T17:38:44.167 回答
3

代替

GstBufferQueue **nextPtr;
*nextPtr = (*head)->next;
...
*head = *nextPtr;

经过

GstBufferQueue *nextPtr;
nextPtr = (*head)->next;
...
*head = nextPtr;
于 2012-12-31T17:41:47.433 回答
0

对我来说突出的一件事是当您第一次分配队列时(当 *queue_size == 0 时)您没有将新创建的节点的“下一个”指针设置为 NULL。

不能保证在分配和分配给 (*head) 之后它会为 NULL,因此当您执行出列时,您的 (*head)->next 可能指向垃圾地址。

于 2012-12-31T16:59:47.150 回答