我們有提到過GstElement有NULL、READY、PAUSED、PLAY四種狀態,NULL跟READY屬於停止狀態,PAUSED、PLAY屬於開始狀態。在這個狀態轉換的過程中會有對應的資源要分配及釋放,今天就是要來完成這個狀態轉換的資源分配與釋放
昨天只教大家新增屬性,今天則是需要分配串流的資源,我們需要的是一個cudaStream_t,因為在串流時進來的GstBuffer是NvBufSurface,所以我們可以先對每個影格呼叫修改顏色的CUDA kernel,因為kernel是可以非同步運算的,呼叫完再透過cudaStream_t等待所有kernel運算完成
所以我們去gstironmancolormagic.h新增欄位
/* some includes */
#include <cuda_runtime_api.h>
/* some codes */
struct _GstIronmanColorMagic
{
GstBaseTransform base_ironmancolormagic;
guint operate_on_gie;
guint operate_on_class;
guint gpu_id;
/* private */
cudaStream_t stream;
}
因為用了CUDA,所以include路徑跟link路徑都要修改,替Makefile加上CUDA的include跟lib路徑
TARGET := libgstironmancolormagic.so
CC := gcc
CUDA_DIR := /usr/local/cuda
PKGS := gstreamer-1.0 gstreamer-base-1.0
CFLAGS := -Wall -Wextra -fPIC $(shell pkg-config --cflags $(PKGS)) \
-I$(CUDA_DIR)/include
LDFLAGS := -shared $(shell pkg-config --libs $(PKGS)) \
-L$(CUDA_DIR)/lib64 -lcudart \
-Wl,-rpath,$(CUDA_DIR)/lib64
SRCS := gstironmancolormagic.c
OBJS := $(SRCS:.c=.o)
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(TARGET)
.PHONY: all clean
模板有一個函式是gst_ironman_color_magic_start,這個函式會在READY轉PAUSED時被呼叫,我們要在這邊初始化串流需要的資源,本次範例的串流資源就是stream
static gboolean
gst_ironman_color_magic_start (GstBaseTransform * trans)
{
GstIronmanColorMagic* self = GST_IRONMAN_COLOR_MAGIC (trans);
/* Set device before allocating resources on the GPU */
{
cudaError_t cuda_err;
cuda_err = cudaSetDevice((int)self->gpu_id);
if (cuda_err != cudaSuccess) {
GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
("Failed to set the GPU device"),
("cudaSetDevice failed: %s", cudaGetErrorString(cuda_err)));
return FALSE;
}
cuda_err = cudaStreamCreateWithFlags(&self->stream, cudaStreamNonBlocking);
if (cuda_err != cudaSuccess) {
GST_ELEMENT_ERROR(self, RESOURCE, FAILED,
("Failed to create a cuda stream"),
("cudaStreamCreateWithFlags failed: %s", cudaGetErrorString(cuda_err)));
return FALSE;
}
}
return TRUE;
}
使用CUDA分配/釋放任何資源時都必須呼叫先cudaSetDevice切換裝置,即使你的GPU只有一個也一樣,第一次呼叫會讓CUDA在API內部分配必要的資源。接下來就是分配cudaStream_t,用的是cudaStreamCreateWithFlags,用這個原因是要帶入cudaStreamNonBlocking,讓stream不會隱式的和預設stream(NULL)進行同步,就是不會在我們控制的範圍外突然卡住
模板有一個函式是gst_ironman_color_magic_stop,這個函式則是在PAUSED轉READY時被呼叫,我們要在這邊釋放串流需要的資源
gboolean
gst_ironman_color_magic_stop (GstBaseTransform * trans)
{
GstIronmanColorMagic* self = GST_IRONMAN_COLOR_MAGIC (trans);
if (self->stream) {
cudaError_t cuda_err;
/* Should not failed here but we can still treat errors as warning */
cuda_err = cudaSetDevice(self->gpu_id);
if (cuda_err != cudaSuccess) {
GST_ELEMENT_WARNING(self, RESOURCE, SETTINGS,
("Failed to set the GPU device"),
("cudaSetDevice failed: %s", cudaGetErrorString(cuda_err)));
}
cuda_err = cudaStreamDestroy(self->stream);
if (cuda_err != cudaSuccess) {
GST_ELEMENT_WARNING(self, RESOURCE, CLOSE,
("Failed to destroy the cuda stream"),
("cudaStreamDestroy failed: %s", cudaGetErrorString(cuda_err)));
}
/* The element may change to PAUSED state again, it is more safe to set 'stream' to NULL */
self->stream = NULL;
}
return TRUE;
}
理論上這邊CUDA API不應該出錯,我選擇攔截錯誤並以警告的方式來處理。同樣地記得在使用任何GPU資源前要呼叫cudaSetDevice
我們先到gstironmancolormagic.c的上半部新增#include <gst/video/video.h>
#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
#include <gst/video/video.h>
#include "gstironmancolormagic.h"
然後往下一些,修改Pad Templates,我們讓Src Pad和Sink Pad都只能接受video/x-raw(memory:NVMM),format=RGBA(支援NV12是在折磨自己)
/* pad templates */
#define FEATURE_NVMM "memory:NVMM"
static GstStaticPadTemplate gst_ironman_color_magic_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES(FEATURE_NVMM, "{RGBA}"))
);
static GstStaticPadTemplate gst_ironman_color_magic_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES(FEATURE_NVMM, "{RGBA}"))
);
GST_VIDEO_CAPS_MAKE_WITH_FEATURES是一個高階macro,用它來描述Caps會比較簡單
#define GST_VIDEO_CAPS_MAKE_WITH_FEATURES(features,format) \
"video/x-raw(" features "), " \
"format = (string) " format ", " \
"width = " GST_VIDEO_SIZE_RANGE ", " \
"height = " GST_VIDEO_SIZE_RANGE ", " \
"framerate = " GST_VIDEO_FPS_RANGE
完成之後再用gst-inspect-1.0看一次
Pad Templates:
SINK template: 'sink'
Availability: Always
Capabilities:
video/x-raw(memory:NVMM)
format: { (string)RGBA }
width: [ 1, 2147483647 ]
height: [ 1, 2147483647 ]
framerate: [ 0/1, 2147483647/1 ]
SRC template: 'src'
Availability: Always
Capabilities:
video/x-raw(memory:NVMM)
format: { (string)RGBA }
width: [ 1, 2147483647 ]
height: [ 1, 2147483647 ]
framerate: [ 0/1, 2147483647/1 ]
登愣!是不是有點樣子了呢
我們示範的目標非常簡單,所以只要覆寫start和stop兩個virtual function就已經很夠用了。最後再補充set_caps這個virtual function,它會在start之後、Caps協商完畢後被呼叫,如果資源分配必須知道最終的Caps才能決定,例如color format、width、height,那麼你就必須實作set_caps