gstreamer移植qnx:编写gstreamer插件audio sink和video sink

Posted 玄道公子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gstreamer移植qnx:编写gstreamer插件audio sink和video sink相关的知识,希望对你有一定的参考价值。

一、概述

​ 因为gstreamer没有提供qnx系统的支持, 因此这里要实现音频和视频的播放,就必须自己实现最终的音视频输出的元件,即sink元件,一开始,我的想法是,可否移植开源的音视频输出库,比如sdl,alsa等等, 但是发现有些麻烦, 反而把事情弄的更复杂了。 最终还是踏踏实实的两个gstreamer的sink元件,用来输出音频和视频。

​ 要编写gstreamer的插件有很多方式, 比如直接“继承”实现GstElementClass和GstElement,使用gst-template工具创建一个插件模板,然后去实现。 我这里使用的另一种方式, 既然是实现sink类型的元件。 那么就直接找gstreamer已有的sink元件作为模板,将他们复制过来,删除原有的接口实现代码,换上自己的实现。

注意, 输出的plugin动态库名称以 libpluginname.so 的形式, 而这个pluginname就是 编写插件时,定义插件的宏GST_PLUGIN_DEFINE 的参数中的name, 如果不匹配,就会出现插件无法是使用,被gstreamer将插件加入黑名单

二、实现QNX audio sink插件

其实gstreamer有sink插件的“基类”:GstAudiosinkClass, 我们要做的就是继承和override一些接口。GstAudioSinkClass的全部接口如下:

struct _GstAudioSinkClass 
  /**基类,其完成的继承路径是 GSTAudioSinkClass ——> GstAudioBaseSinkClass ——>
   GstBaseSinkClass ——> GstElementClass ,再向上就是和gobject相关的东西了,这里不涉及 */  
  GstAudioBaseSinkClass parent_class; 

  /* vtable, 接口定义 */

  /* open the device with given specs, 打开设备*/
  gboolean (*open)      (GstAudioSink *sink);
  /* prepare resources and state to operate with the given specs, 进行prepare操作 */
  gboolean (*prepare)   (GstAudioSink *sink, GstAudioRingBufferSpec *spec);
  /* undo anything that was done in prepare(), 回退到prepare之前 */
  gboolean (*unprepare) (GstAudioSink *sink);
  /* close the device, 关闭设备 */
  gboolean (*close)     (GstAudioSink *sink);
  /* write samples to the device, 向音频设备写数据 */
  gint     (*write)     (GstAudioSink *sink, gpointer data, guint length);
  /* get number of frames queued in the device,获取设备队列里面有多少帧没有输出(即缓存) */
  guint    (*delay)     (GstAudioSink *sink);
  /* reset the audio device, unblock from a write,重置设备 */
  void     (*reset)     (GstAudioSink *sink);

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

​ 我这里就是以oss音频 sink插件作为模板,将其源码复制出来,删除基于oss接口实现的 gstreamer audio sink接口的代码,然后再填充自己的代码, 其中 头文件 gstQnxAudioSink.h, 其内容如下:

/* GStreamer
 * Copyright (C) gu.wen <454727014@qq.com>
 *
 * gstqnxaudiosink.h: 
 *
 */

#ifndef __GST_QNXAUDIO_H__
#define __GST_QNXAUDIO_H__

#include <gst/gst.h>
#include <gst/audio/gstaudiosink.h>
#include <glib/gtypes.h>
#include <sys/asoundlib.h> /** 包含qnx audio接口的头文件 */


G_BEGIN_DECLS
/** 定义方面操作的工具宏 */
#define GST_TYPE_QNXAUDIOSINK (gst_qnxaudio_sink_get_type())

#define GST_QNXAUDIOSINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \\
                               GST_TYPE_QNXAUDIOSINK, \\
                               GstQnxAudioSink))

#define GST_QNXAUDIOSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \\
                                       GST_TYPE_QNXAUDIOSINK, \\
                                       GstQnxAudioSinkClass))

#define GST_IS_QNXAUDIOSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \\
                                  GST_TYPE_QNXAUDIOSINK))

#define GST_IS_QNXAUDIOSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \\
                                          GST_TYPE_QNXAUDIOSINK))

typedef struct _GstQnxAudioSink GstQnxAudioSink;
typedef struct _GstQnxAudioSinkClass GstQnxAudioSinkClass;

struct _GstQnxAudioSink

    GstAudioSink sink; /** 继承audio sink, 这个成员变量必须放在首位 */

    gchar *device;  /** qnx audio 设备节点的路径 */
    gint audioCard; /** 声卡id */
    gint bytes_per_sample; 

    GstCaps *probed_caps;  /** 存放检索到的caps信息 */
    
    /** qnx audio 相关的 数据结构 */
    snd_pcm_info_t info; 
    snd_pcm_t *pcm_handle;
    snd_pcm_channel_info_t channelInfo;
    snd_mixer_t *mixer_handle;
    snd_mixer_group_t mixerGroup;
    snd_pcm_channel_params_t channelParam;
    snd_pcm_channel_setup_t channelSetup;
;

struct _GstQnxAudioSinkClass

      /**类,用于定义接口,其完成的继承路径是 
       * _GstQnxAudioSinkClass ——> GSTAudioSinkClass ——> GstAudioBaseSinkClass 
       * ——> GstBaseSinkClass ——> GstElementClass
       * 再向上就是和gobject相关的东西了,这里不涉及 
       */
    GstAudioSinkClass parent_class;
;

/** 获取GType,具体的参考 gobject/glib相关的信息 */
GType gst_qnxaudio_sink_get_type(void);

G_END_DECLS

#endif /* __GST_QNXAUDIO_H__ */


接下来再试重点, qnx audio sink 的实现源文件, 如下所示:

/* GStreamer
 * Copyright (C) gu.wen <454727014@qq.com>
 *
 * gstqnxaudiosink.c: 
 *
 */

/**
 * SECTION:element-qnxaudio
 *
 * This element lets you output sound using the qnx audio system (QNXAUDIO).
 *
 * Note that you should almost always use generic audio conversion elements
 * like audioconvert and audioresample in front of an audiosink to make sure
 * your pipeline works under all circumstances (those conversion elements will
 * act in passthrough-mode if no conversion is necessary).
 *
 * <refsect2>
 * <title>Example pipelines</title>
 * |[
 * gst-launch-1.0 -v audiotestsrc ! audioconvert ! volume volume=0.1 ! qnxaudio
 * ]| will output a sine wave (continuous beep sound) to your sound card (with
 * a very low volume as precaution).
 * |[
 * gst-launch-1.0 -v filesrc location=music.ogg ! decodebin ! audioconvert ! 
 * audioresample ! qnxaudio ]| 
 * will play an Ogg/Vorbis audio file and output it using the Open Sound System.
 * </refsect2>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

#include "gstQnxAudioSink.h"


#define PLUGIN_VERSION "00.01.00"
#define PACKAGE "gst-plugins-qnx"
#define GST_LICENSE "LGPL"
#define GST_PACKAGE_NAME "GStreamer qnx Plug-ins source release"
#define GST_PACKAGE_ORIGIN "Unknown package origin"


GST_DEBUG_CATEGORY_EXTERN(qnxaudio_debug);
#define GST_CAT_DEFAULT qnxaudio_debug

static void gst_qnxaudio_sink_dispose(GObject *object);
static void gst_qnxaudio_sink_finalise(GObject *object);

static void gst_qnxaudio_sink_get_property(GObject *object, guint prop_id,
                                           GValue *value, GParamSpec *pspec);

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

static GstCaps *gst_qnxaudio_sink_getcaps(GstBaseSink *bsink, GstCaps *filter);

static gboolean gst_qnxaudio_sink_open(GstAudioSink *asink);
static gboolean gst_qnxaudio_sink_close(GstAudioSink *asink);
static gboolean gst_qnxaudio_sink_prepare(GstAudioSink *asink,
                                          GstAudioRingBufferSpec *spec);
static gboolean gst_qnxaudio_sink_unprepare(GstAudioSink *asink);
static gint gst_qnxaudio_sink_write(GstAudioSink *asink, gpointer data,
                                    guint length);
static guint gst_qnxaudio_sink_delay(GstAudioSink *asink);
static void gst_qnxaudio_sink_reset(GstAudioSink *asink);

/* QnxAudioSink signals and args */
enum

    LAST_SIGNAL
;

#define DEFAULT_DEVICE "/dev/snd/pcmPreferredp"

enum

    PROP_0,
    PROP_DEVICE,
;

#define FORMATS "" GST_AUDIO_NE(S16) "," GST_AUDIO_NE(U16) ", S8, U8 "

/** 定义 固定pad的工厂 */
static GstStaticPadTemplate qnxaudio_sink_factory =
    GST_STATIC_PAD_TEMPLATE("sink",
                            GST_PAD_SINK,
                            GST_PAD_ALWAYS,
                            GST_STATIC_CAPS("audio/x-raw, "
                                            "format = (string) " FORMATS ", "
                                            "layout = (string) interleaved, "
                                            "rate = (int) [ 1, MAX ], "
                                            "channels = (int) 1; "
                                            "audio/x-raw, "
                                            "format = (string) " FORMATS ", "
                                            "layout = (string) interleaved, "
                                            "rate = (int) [ 1, MAX ], "
                                            "channels = (int) 2, "
                                            "channel-mask = (bitmask) 0x3"));

/* static guint gst_qnxaudio_sink_signals[LAST_SIGNAL] =  0 ; */

#define gst_qnxaudio_sink_parent_class parent_class

G_DEFINE_TYPE(GstQnxAudioSink, gst_qnxaudio_sink, GST_TYPE_AUDIO_SINK);

static void gst_qnxaudio_sink_dispose(GObject *object)
   /** override GObjectClass 的dispose接口 */
    GstQnxAudioSink *qnxaudio = GST_QNXAUDIOSINK(object);

    if (qnxaudio->probed_caps)
    /**如果已经索引过qnx audio sink 的能力, 就是放已有的caps信息 */
        gst_caps_unref(qnxaudio->probed_caps);
        qnxaudio->probed_caps = NULL;
    

    /** 再调用父类的dispose接口 */
    G_OBJECT_CLASS(parent_class)->dispose(object);


static GstStructure *
gst_qnxaudio_helper_get_format_structure(unsigned int format_bit)
/** 根据qnx audio的类型生成caps信息的结构体 */
    GstStructure *structure;
    //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
    const gchar *format;

    switch (format_bit)
    
    case SND_PCM_SFMT_U8:
        format = "U8";
        break;
    case SND_PCM_SFMT_S16_LE:
        format = "S16LE";
        break;
    case SND_PCM_SFMT_S16_BE:
        format = "S16BE";
        break;
    case SND_PCM_SFMT_S8:
        format = "S8";
        break;
    case SND_PCM_SFMT_U16_LE:
        format = "U16LE";
        break;
    case SND_PCM_SFMT_U16_BE:
        format = "U16BE";
        break;
    default:
        g_assert_not_reached();
        //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
        return NULL;
    

    printf("[%s.%d]===>:format: %s\\n", __FUNCTION__, __LINE__, format);

    structure = gst_structure_new("audio/x-raw",
                                  "format", G_TYPE_STRING, format,
                                  "layout", G_TYPE_STRING, "interleaved", NULL);

    return structure;


static GstCaps *
gst_qnxaudio_helper_probe_caps(snd_pcm_channel_info_t *channelInfo)
/** 索引qnx audio的能力信息 */
    //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
    const guint probe_formats[] = 
    SND_PCM_SFMT_S16_LE, SND_PCM_SFMT_U16_LE, SND_PCM_SFMT_U8, SND_PCM_SFMT_S8;
#else
    const guint probe_formats[] = 
    SND_PCM_SFMT_S16_BE, SND_PCM_SFMT_U16_BE, SND_PCM_SFMT_U8, SND_PCM_SFMT_S8;
#endif
    GstStructure *structure;
    GstCaps *caps;
    int f;
    /* FIXME test make sure we're not currently playing */
    /* FIXME test both mono and stereo */

    caps = gst_caps_new_empty();

    /* assume that the most significant bit of format_mask is 0 */
    for (f = 0; f < G_N_ELEMENTS(probe_formats); ++f)
    
        printf("[%s.%d]===>:idx:%d, rate range(%d - %d)\\n", 
               __FUNCTION__, __LINE__, f, 
               channelInfo->min_rate, channelInfo->max_rate);
        
        GValue rate_value = 0;
        /* one big range */
        g_value_init(&rate_value, GST_TYPE_INT_RANGE);
        gst_value_set_int_range(&rate_value, channelInfo->min_rate,
                                (channelInfo->max_rate == -1) ? 
                                channelInfo->min_rate : 
                                channelInfo->max_rate);

        structure = gst_qnxaudio_helper_get_format_structure(probe_formats[f]);
        gst_structure_set(structure, "channels", 
                          GST_TYPE_INT_RANGE, 1, 2, NULL);
        
        gst_structure_set_value(structure, "rate", &rate_value);
        g_value_unset(&rate_value);

        gst_caps_append_structure(caps, structure);
    

    if (gst_caps_is_empty(caps))
    
        /* fixme: make user-visible */
        GST_WARNING("Your qnx audio device could not be probed correctly");
    
    else
    
        caps = gst_caps_simplify(caps);
    

    printf("[%s.%d]===>:probed caps: %p\\n", 
           __FUNCTION__, __LINE__, caps);
    
    GST_DEBUG("probed caps: %" GST_PTR_FORMAT, caps);

    return caps;


static void gst_qnxaudio_sink_class_init(GstQnxAudioSinkClass *klass)
/** 类初始化接口 */
    //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
    GObjectClass *gobject_class;
    GstElementClass *gstelement_class;
    GstBaseSinkClass *gstbasesink_class;
    GstAudioSinkClass *gstaudiosink_class;

    gobject_class = (GObjectClass *)klass;
    gstelement_class = (GstElementClass *)klass;
    gstbasesink_class = (GstBaseSinkClass *)klass;
    gstaudiosink_class = (GstAudioSinkClass *)klass;

    parent_class = g_type_class_peek_parent(klass);

    /** override 父类接口 */
    gobject_class->dispose = gst_qnxaudio_sink_dispose;
    gobject_class->finalize = gst_qnxaudio_sink_finalise;
    gobject_class->get_property = gst_qnxaudio_sink_get_property;
    gobject_class->set_property = gst_qnxaudio_sink_set_property;

    /** 安装属性接口 */
    g_object_class_install_property(gobject_class, PROP_DEVICE,
              g_param_spec_string("device", "Device",
                         		"QNXAUDIO device (usually /dev/dspN)", 
                                DEFAULT_DEVICE,
                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    /** override 父类接口 */
    gstbasesink_class->get_caps = 
                                GST_DEBUG_FUNCPTR(gst_qnxaudio_sink_getcaps);
    gstaudiosink_class->open = GST_DEBUG_FUNCPTR(gst_qnxaudio_sink_open);
    gstaudiosink_class->close = GST_DEBUG_FUNCPTR(gst_qnxaudio_sink_close);
    gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR(gst_qnxaudio_sink_prepare);
    gstaudiosink_class->unprepare = 
                                GST_DEBUG_FUNCPTR(gst_qnxaudio_sink_unprepare);
                                
    gstaudiosink_class->write = GST_DEBUG_FUNCPTR(gst_qnxaudio_sink_write);
    gstaudiosink_class->delay = GST_DEBUG_FUNCPTR(gst_qnxaudio_sink_delay);
    gstaudiosink_class->reset = GST_DEBUG_FUNCPTR(gst_qnxaudio_sink_reset);

    /** 设置qnx  audio sink 元件的meta信息 */
    gst_element_class_set_static_metadata(gstelement_class, 
                                         "Audio Sink (QNXAUDIO)",
                                         "Sink/Audio",
                                         "Output to a sound card via QNXAUDIO",
                                         "guwen <454727014@qq.com>");

    /** 添加固定pad工厂 */
    gst_element_class_add_static_pad_template(gstelement_class,
                                              &qnxaudio_sink_factory);
    //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);                                              


static void gst_qnxaudio_sink_init(GstQnxAudioSink *qnxaudio)
/** qnx audio sink数据结构初始化 */
    GST_DEBUG_OBJECT(qnxaudio, "initializing qnxaudio");
    //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
    qnxaudio->pcm_handle = NULL;
    qnxaudio->mixer_handle = NULL;
    qnxaudio->audioCard = -1;
    qnxaudio->probed_caps = NULL;
    qnxaudio->device = strdup(DEFAULT_DEVICE);
    memset(&qnxaudio->channelInfo, 0, sizeof(qnxaudio->channelInfo));
    memset(&qnxaudio->channelParam, 0, sizeof(qnxaudio->channelParam));
    memset(&qnxaudio->channelSetup, 0, sizeof(qnxaudio->channelSetup));
    memset(&qnxaudio->mixerGroup, 0, sizeof(qnxaudio->mixerGroup));
    //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);


static void gst_qnxaudio_sink_finalise(GObject *object)
/** override finalise接口 */
    //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
    GstQnxAudioSink *qnxaudio = GST_QNXAUDIOSINK(object);

    g_free(qnxaudio->device);

    G_OBJECT_CLASS(parent_class)->finalize((GObject *)(object));


static void 
gst_qnxaudio_sink_set_property(GObject *object, guint prop_id,
                               const GValue *value, GParamSpec *pspec)
/** 设置属性 */
    //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
    GstQnxAudioSink *sink;

    sink = GST_QNXAUDIOSINK(object);

    switch (prop_id)
    
    case PROP_DEVICE:
        //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
        g_free(sink->device);
        sink->device = g_value_dup_string(value);
        if (sink->probed_caps)
        
            gst_caps_unref(sink->probed_caps);
            sink->probed_caps = NULL;
        
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
        break;
    


static void gst_qnxaudio_sink_get_property(GObject *object, guint prop_id,
                                         GValue *value, GParamSpec *pspec)

    GstQnxAudioSink *sink;
    //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);

    sink = GST_QNXAUDIOSINK(object);

    switch (prop_id)
    
    case PROP_DEVICE:
        g_value_set_string(value, sink->device);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
        break;
    


static GstCaps *gst_qnxaudio_sink_getcaps(GstBaseSink *bsink, 
                                          GstCaps *filter)

    GstQnxAudioSink *qnxaudio;    
    GstCaps *caps;
    qnxaudio = GST_QNXAUDIOSINK(bsink);
   // printf("[%s.%d]===>:pcm_handle: %p\\n", 
   // __FUNCTION__, __LINE__, qnxaudio->pcm_handle);
    
    if (qnxaudio->pcm_handle == NULL)
    
        printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
        caps = gst_pad_get_pad_template_caps(GST_BASE_SINK_PAD(bsink));
    
    else if (qnxaudio->probed_caps)
    
        printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
        caps = gst_caps_ref(qnxaudio->probed_caps);
    
    else
    
        //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
        caps = gst_qnxaudio_helper_probe_caps(&qnxaudio->channelInfo);
        if (caps && !gst_caps_is_empty(caps))
        
            qnxaudio->probed_caps = gst_caps_ref(caps);
        
    

    if (filter && caps)
    
        //printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
        GstCaps *intersection;

        intersection =
            gst_caps_intersect_full(filter, caps, GST_CAPS_INTERSECT_FIRST);
        gst_caps_unref(caps);
        return intersection;
    
    else
    
        printf("[%s.%d]===>:\\n", __FUNCTION__, __LINE__);
        return caps;
    


static gint ilog2(gint x)

    /* well... hacker's delight explains... */
    x = x | (x >> 1);
    x = x | (x >> 2);
    x = x | (

以上是关于gstreamer移植qnx:编写gstreamer插件audio sink和video sink的主要内容,如果未能解决你的问题,请参考以下文章

gstreamer移植qnx:交叉编译qnx版本的gstreamer插件库

gstreamer移植qnx:交叉编译glib

gstreamer移植qnx:概览篇

初探 gstream

GStreamer基础教程02 - 基本概念

安装 Gstreamer 后没有这样的元素 filesrc