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 <[email protected]>
 *
 * 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 <[email protected]>
 *
 * 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 <[email protected]>");

    /** 添加固定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 | (x >> 4);
    x = x | (x >> 8);
    x = x | (x >> 16);
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = (x + (x >> 4)) & 0x0f0f0f0f;
    x = x + (x >> 8);
    x = x + (x >> 16);
    return (x & 0x0000003f) - 1;
}

static gint 
gst_qnxaudio_sink_get_format(GstAudioRingBufferFormatType fmt, 
                             GstAudioFormat rfmt)
{
    gint result = -1;

    switch (fmt)
    {
    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MU_LAW:
        result = SND_PCM_SFMT_MU_LAW;
        break;
    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_A_LAW:
        result = SND_PCM_SFMT_A_LAW;
        break;
    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_IMA_ADPCM:
        result = SND_PCM_SFMT_IMA_ADPCM;
        break;
    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG:
        result = SND_PCM_SFMT_MPEG;
        break;
    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW:
    {
        switch (rfmt)
        {
        case GST_AUDIO_FORMAT_U8:
            result = SND_PCM_SFMT_U8;
            break;
        case GST_AUDIO_FORMAT_S16LE:
            result = SND_PCM_SFMT_S16_LE;
            break;
        case GST_AUDIO_FORMAT_S16BE:
            result = SND_PCM_SFMT_S16_BE;
            break;
        case GST_AUDIO_FORMAT_S8:
            result = SND_PCM_SFMT_S8;
            break;
        case GST_AUDIO_FORMAT_U16LE:
            result = SND_PCM_SFMT_U16_LE;
            break;
        case GST_AUDIO_FORMAT_U16BE:
            result = SND_PCM_SFMT_U16_BE;
            break;
        default:
            result = -1;
            break;
        }
        break;
    }
    default:
        result = -1;
        break;
    }

    return result;
}

static gboolean gst_qnxaudio_sink_open(GstAudioSink *asink)
{
    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    GstQnxAudioSink *qnxaudio;

    qnxaudio = GST_QNXAUDIOSINK(asink);

    int rtn = snd_pcm_open_name(&qnxaudio->pcm_handle, 
                                qnxaudio->device, SND_PCM_OPEN_PLAYBACK);

    if (rtn == EOK)
    {
        rtn = snd_pcm_info(qnxaudio->pcm_handle, &qnxaudio->info);

        if (rtn == 0)
        {

            qnxaudio->channelInfo.channel = SND_PCM_CHANNEL_PLAYBACK;
            rtn = snd_pcm_plugin_info(qnxaudio->pcm_handle, 
                                      &qnxaudio->channelInfo);
            
            if (rtn == 0)
            {
                qnxaudio->audioCard = qnxaudio->info.card;
                //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
                return TRUE;
            }
            else
            {
                GST_ELEMENT_ERROR(qnxaudio, RESOURCE, OPEN_WRITE, (NULL),
                                 ("Could not open audio device for playback." 
                                  "snd_pcm_plugin_info failed: %s\n", 
                                  snd_strerror(rtn)));
            }

            /** todo something */
        }
        else
        {
            GST_ELEMENT_ERROR(qnxaudio, RESOURCE, OPEN_WRITE,
                             (("Could not open audio device for playback. "
                             "get qnx audio pcm info failedd")),
                             (NULL));
        }

        rtn = snd_pcm_close(qnxaudio->pcm_handle);
    }
    else
    {

        GST_ERROR_OBJECT(qnxaudio, 
                         "open qnx audio output device: %s failed\n", 
                         qnxaudio->device);

        /** Errors */
        if (rtn == -EINVAL)
        {
            GST_ELEMENT_ERROR(qnxaudio, RESOURCE, OPEN_WRITE,
                              (("Could not open audio device for playback. "
                                "The mode is invalid")),
                              (NULL));
        }
        else if (rtn == -ENOENT)
        {
            GST_ELEMENT_ERROR(qnxaudio, RESOURCE, NOT_FOUND,
                              (("Could not open audio device for playback. "
                               "The named device doesn't exist.")),
                              (NULL));
        }
        else if (rtn == -ENOMEM)
        {
            GST_ELEMENT_ERROR(qnxaudio, RESOURCE, NO_SPACE_LEFT,
                              (("Could not open audio device for playback. "
                              "Not enough memory is available to allocate the"
                              "control structures")),
                              (NULL));
        }
        else if (rtn == -SND_ERROR_INCOMPATIBLE_VERSION)
        {
            GST_ELEMENT_ERROR(qnxaudio, RESOURCE, OPEN_WRITE,
                              (("Could not open audio device for playback. "
                              "The audio driver version is incompatible "
                              "with the client library that the application"
                              "is using.")),
                              (NULL));
        }
    }

    qnxaudio->pcm_handle = NULL;
    qnxaudio->mixer_handle = NULL;
    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return FALSE;
}

static gboolean gst_qnxaudio_sink_close(GstAudioSink *asink)
{
    GstQnxAudioSink *qnxaudio;
    int rtn;
    qnxaudio = GST_QNXAUDIOSINK(asink);
    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    if (qnxaudio->pcm_handle)
    {
        rtn = snd_pcm_plugin_flush(qnxaudio->pcm_handle, 
                                   SND_PCM_CHANNEL_PLAYBACK);       
    }

    if (qnxaudio->mixer_handle)
    {
        rtn = snd_mixer_close(qnxaudio->mixer_handle);
        if (rtn != 0)
        {
            GST_ERROR_OBJECT(qnxaudio, "close mixer failed: %d\n", rtn);
            return FALSE;
        }  
        
        qnxaudio->mixer_handle = NULL;      
    }

    if (qnxaudio->pcm_handle)
    {
        rtn = snd_pcm_close(qnxaudio->pcm_handle);

        if (rtn != 0)
        {
            GST_ERROR_OBJECT(qnxaudio, 
                             "close qnx audio device failed: %d\n", 
                             rtn);
            return FALSE;
        }
        qnxaudio->pcm_handle = NULL;        
    }

    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return TRUE;
}

static gboolean 
gst_qnxaudio_sink_prepare(GstAudioSink *asink, GstAudioRingBufferSpec *spec)
{
    GstQnxAudioSink *qnxaudio;
    int tmp;
    guint mSampleBits, mSampleRate, mSampleChannels;
    gint fragSize;
    snd_pcm_channel_params_t* pChParam;

    qnxaudio = GST_QNXAUDIOSINK(asink);
    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);

    tmp = gst_qnxaudio_sink_get_format(spec->type,
                                       GST_AUDIO_INFO_FORMAT(&spec->info));
    if (tmp == -1)
    {
        GST_ELEMENT_ERROR(qnxaudio, RESOURCE, SETTINGS, (NULL),
                          ("Unable to get format (%d, %d)", spec->type,
                           GST_AUDIO_INFO_FORMAT(&spec->info)));
        return FALSE;
    }

    mSampleBits = GST_AUDIO_INFO_WIDTH(&spec->info);
    mSampleRate = GST_AUDIO_INFO_RATE(&spec->info);
    mSampleChannels = GST_AUDIO_INFO_CHANNELS(&spec->info);

    if (mSampleBits != 16 && mSampleBits != 8)
    {
        GST_ELEMENT_ERROR(qnxaudio, RESOURCE, SETTINGS, (NULL),
                          ("unexpected width %d", mSampleBits));
        return FALSE;
    }

    tmp = ilog2(spec->segsize);
    tmp = ((spec->segtotal & 0x7fff) << 16) | tmp;
    GST_DEBUG_OBJECT(qnxaudio, "set segsize: %d, segtotal: %d, value: %08x",
                     spec->segsize, spec->segtotal, tmp);

    fragSize = 0;
	
    pChParam = &qnxaudio->channelParam;
    pChParam->mode = SND_PCM_MODE_BLOCK;
    pChParam->channel = SND_PCM_CHANNEL_PLAYBACK;
    pChParam->start_mode = SND_PCM_START_FULL;
    pChParam->stop_mode = SND_PCM_STOP_STOP;

    pChParam->buf.block.frag_size = 
                                qnxaudio->channelInfo.max_fragment_size;
    if (fragSize != -1)
    {
        pChParam->buf.block.frag_size = fragSize;
    }

    pChParam->buf.block.frags_max = -1; //num_frags;
    pChParam->buf.block.frags_min = 1;

    pChParam->format.interleave = 1;
    pChParam->format.rate = mSampleRate;
    pChParam->format.voices = mSampleChannels;

    if (mSampleBits == 8)
    {
        pChParam->format.format = SND_PCM_SFMT_U8;
    }
    else if (mSampleBits == 16)
    {
        pChParam->format.format = SND_PCM_SFMT_S16_LE;
    }
    else
    {
        pChParam->format.format = SND_PCM_SFMT_S16_LE;
    }

    strcpy(pChParam->sw_mixer_subchn_name, "Wave playback channel");
	tmp = snd_pcm_plugin_params(qnxaudio->pcm_handle, 
                                &qnxaudio->channelParam);
    
    if (tmp < 0)
    {
        GST_ELEMENT_ERROR(qnxaudio, RESOURCE, SETTINGS, (NULL),
                          ("snd_pcm_plugin_params failed: %s\n",
                           snd_strerror(tmp)));
        return FALSE;
    }
	tmp = snd_pcm_plugin_prepare(qnxaudio->pcm_handle, 
                                 SND_PCM_CHANNEL_PLAYBACK);
    
    if (tmp < 0)
    {
        GST_ELEMENT_ERROR(qnxaudio, RESOURCE, SETTINGS, (NULL),
                          ("snd_pcm_plugin_prepare failed: %s\n",
                           snd_strerror(tmp)));

        return FALSE;
    }

    qnxaudio->channelSetup.channel = SND_PCM_CHANNEL_PLAYBACK;
    qnxaudio->channelSetup.mixer_gid = &qnxaudio->mixerGroup.gid;
    
	tmp = snd_pcm_plugin_setup(qnxaudio->pcm_handle, 
                               &qnxaudio->channelSetup);
    if (tmp < 0)
    {
        GST_ELEMENT_ERROR(qnxaudio, RESOURCE, SETTINGS, (NULL),
                          ("snd_pcm_plugin_setup failed: %s\n",
                           snd_strerror(tmp)));

        return FALSE;
    }
    GST_INFO_OBJECT(qnxaudio, "Format %s \n", 
                    snd_pcm_get_format_name(
                        qnxaudio->channelSetup.format.format));
    
    GST_INFO_OBJECT(qnxaudio, "Frag Size %d \n", 
                    qnxaudio->channelSetup.buf.block.frag_size);
    
    GST_INFO_OBJECT(qnxaudio, "Total Frags %d \n", 
                    qnxaudio->channelSetup.buf.block.frags);
    
    GST_INFO_OBJECT(qnxaudio, "Rate %d \n", 
                    qnxaudio->channelSetup.format.rate);
    
    GST_INFO_OBJECT(qnxaudio, "Voices %d \n", 
                    qnxaudio->channelSetup.format.voices);

    if (qnxaudio->mixerGroup.gid.name[0] == 0)
    {
        GST_ELEMENT_ERROR(qnxaudio, RESOURCE, SETTINGS, (NULL),
                          ("Mixer Pcm Group [%s] Not Set \n",
                           qnxaudio->mixerGroup.gid.name));

        return FALSE;
    }

    tmp = snd_mixer_open(&qnxaudio->mixer_handle, 
                         qnxaudio->audioCard, 
                         qnxaudio->channelSetup.mixer_device);
    if (tmp < 0)
    {
        GST_ELEMENT_ERROR(qnxaudio, RESOURCE, SETTINGS, (NULL),
                          ("snd_mixer_open failed: %s\n",
                           snd_strerror(tmp)));

        return FALSE;
    }

    spec->segsize = pChParam->buf.block.frag_size;
    //spec->segtotal = info.fragstotal;

    qnxaudio->bytes_per_sample = GST_AUDIO_INFO_BPF(&spec->info);

    GST_DEBUG_OBJECT(qnxaudio, "got segsize: %d, segtotal: %d, value: %08x",
                     spec->segsize, spec->segtotal, tmp);

    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return TRUE;
}

static gboolean gst_qnxaudio_sink_unprepare(GstAudioSink *asink)
{

    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    if (!gst_qnxaudio_sink_close(asink))
    {
        GST_ERROR_OBJECT(asink, "Could not close the audio device");
        return FALSE;
    }

    if (!gst_qnxaudio_sink_open(asink))
    {
        GST_ERROR_OBJECT(asink, "Could not reopen the audio device");
        return FALSE;
    }
    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);

    return TRUE;
}

static gint gst_qnxaudio_sink_write(GstAudioSink *asink, 
                                    gpointer data, guint length)
{
    ////printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return snd_pcm_plugin_write(GST_QNXAUDIOSINK(asink)->pcm_handle, 
                                data, length);
}

static guint gst_qnxaudio_sink_delay(GstAudioSink *asink)
{
    GstQnxAudioSink *qnxaudio;
    ////printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    snd_pcm_channel_status_t status;
    memset(&status, 0, sizeof(status));
    status.channel = SND_PCM_CHANNEL_PLAYBACK;
    qnxaudio = GST_QNXAUDIOSINK(asink);
    if (snd_pcm_plugin_status(qnxaudio->pcm_handle, &status) < 0)
    {
        GST_ERROR_OBJECT(asink, 
                         "underrun: playback channel status error\n");
        return 0;
    }

    return status.count / qnxaudio->bytes_per_sample;
}

static void gst_qnxaudio_sink_reset(GstAudioSink *asink)
{
    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    /* There's nothing we can do here really, 
     * 對於qnx audio這裏沒有實現這個接口 
     **/
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

GST_DEBUG_CATEGORY (qnxaudio_debug);
#define GST_CAT_DEFAULT qnxaudio_debug

static gboolean
plugin_init (GstPlugin * plugin)
{
    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    /** 註冊 qnxaudiosink這個元件 */
    if (!gst_element_register(plugin, "qnxaudiosink", GST_RANK_SECONDARY,
                              GST_TYPE_QNXAUDIOSINK))
    {
        return FALSE;
    }

    GST_DEBUG_CATEGORY_INIT(qnxaudio_debug, "qnxaudio",
                            0, "QNX audio elements");

#ifdef ENABLE_NLS
    GST_DEBUG("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
              LOCALEDIR);
    bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
#endif /* ENABLE_NLS */

    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return TRUE;
}

GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    qnxaudio,
    "Qnx Audio support for GStreamer",
    plugin_init, PLUGIN_VERSION, GST_LICENSE, 
    GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

三、 實現QNX video sink 插件

和audio sink一樣,gstreamer也提供了video sink的“基類”GstVideoSinkClass, 其內容就相對比較簡單:

/**
 * GstVideoSinkClass:
 * @parent_class: the parent class structure
 * @show_frame: render a video frame. Maps to #GstBaseSinkClass.render() and
 *     #GstBaseSinkClass.preroll() vfuncs. Rendering during preroll will be
 *     suppressed if the #GstVideoSink:show-preroll-frame property is set to
 *     %FALSE.
 *
 * The video sink class structure. Derived classes should override the
 * @show_frame virtual function.
 */
struct _GstVideoSinkClass {
  GstBaseSinkClass parent_class;

  GstFlowReturn  (*show_frame) (GstVideoSink *video_sink, GstBuffer *buf);

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

一樣的參考現有的視頻輸出元件的模板,定義頭文件如下:

/* GStreamer
 * Copyright (C) gu.wen <[email protected]>
 *
 * gstqnxvideosink.h: 
 *
 */

#ifndef __GST_QNX_VIDEO_SINK_H__
#define __GST_QNX_VIDEO_SINK_H__

#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/video/gstvideosink.h>

//for qnx screen api
#include <screen/screen.h>

//#include <aalib.h>

#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */

#define GST_TYPE_QNXVIDEOSINK \
    (gst_qnxvideo_sink_get_type())
#define GST_QNXVIDEOSINK(obj) \
    (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_QNXVIDEOSINK, GstQnxVideoSink))
#define GST_QNXVIDEOSINK_CLASS(klass) \
    (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_QNXVIDEOSINK, GstQnxVideoSinkClass))
#define GST_IS_QNXVIDEOSINK(obj) \
    (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_QNXVIDEOSINK))
#define GST_IS_QNXVIDEOSINK_CLASS(klass) \
    (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_QNXVIDEOSINK))

    typedef struct _GstQnxVideoSink GstQnxVideoSink;
    typedef struct _GstQnxVideoSinkClass GstQnxVideoSinkClass;

struct _GstQnxVideoSink
{/** qnx video sink 繼承 自  GstVideoSink */
    GstVideoSink parent;

    GstVideoInfo info;

    gint frames_displayed;
    guint64 frame_time;

    screen_context_t screen_ctx;
    screen_window_t screen_win;
    gboolean isOpened;
    screen_buffer_t dispBuf[2];
    gint dispRect[4]; /** dsiaply rect size */
    gint startX;
    gint startY;
    gint width;
    gint height;
};

struct _GstQnxVideoSinkClass
{/** qnx video sink class 繼承自 GstVideoSinkClass */
    GstVideoSinkClass parent_class;
};

GType gst_qnxvideo_sink_get_type(void);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __GST_QNXVIDEOSINKE_H__ */

接下來就是重點,具體的video sink元件的實現,其代碼稍微有點多,具體如下所示。

/* GStreamer
 * Copyright (C) gu.wen <[email protected]>
 *
 * gstqnxvideosink.c: 
 *
 */

/**
 * SECTION:element-qnxvideosink
 * @see_also: #GstQnxVideoSink
 *
 * Displays video on qnx.
 *
 * <refsect2>
 * <title>Example launch line</title>
 * |[
 * gst-launch-1.0 filesrc location=test.avi ! decodebin ! videoconvert 
 * ! qnxvideosink ]| This pipeline renders a video to ascii art into
 *  a separate window. |[
 * gst-launch-1.0 filesrc location=test.avi ! decodebin ! videoconvert 
 * ! qnxvideosink driver=curses ]| 
 * This pipeline renders a video to ascii art into the current terminal.
 * </refsect2>
 */

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

#include <string.h>
#include <sys/time.h>

#include <gst/video/gstvideometa.h>
#include "gstQnxVideoSink.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"

#define GST_DEFAULT_FRAME_BUF_CNT 4
#define GST_DISP_BUF_CNT 4
#define GST_DISP_USAGE SCREEN_USAGE_NATIVE|SCREEN_USAGE_WRITE
/* qnxvideosink signals and args */
enum
{
    LAST_SIGNAL
};

enum
{
    PROP_0,
    PROP_WIDTH,
    PROP_HEIGHT,
#if 0
    PROP_DRIVER,
    PROP_DITHER,
    PROP_BRIGHTNESS,
    PROP_CONTRAST,
    PROP_GAMMA,
    PROP_INVERSION,
    PROP_RANDOMVAL,
#endif
    PROP_FRAMES_DISPLAYED,
    PROP_FRAME_TIME
};

static GstStaticPadTemplate sink_template = 
			GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK,GST_PAD_ALWAYS,
									GST_STATIC_CAPS(
									GST_VIDEO_CAPS_MAKE("NV12")));

static GstCaps *gst_qnxvideo_sink_fixate(GstBaseSink *bsink, GstCaps *caps);
static gboolean gst_qnxvideo_sink_setcaps(GstBaseSink *bsink, GstCaps *caps);
static void gst_qnxvideo_sink_get_times(GstBaseSink *bsink, GstBuffer *buffer,
                                       GstClockTime *start, GstClockTime *end);
                                       
static gboolean gst_qnxvideo_sink_propose_allocation(GstBaseSink *bsink,
                                                    GstQuery *query);
static GstFlowReturn gst_qnxvideo_sink_show_frame(GstVideoSink *videosink,
                                                 GstBuffer *buffer);

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

static GstStateChangeReturn gst_qnxvideo_sink_change_state(GstElement *element,
                                                    GstStateChange transition);

#define gst_qnxvideo_sink_parent_class parent_class
G_DEFINE_TYPE(GstQnxVideoSink, gst_qnxvideo_sink, GST_TYPE_VIDEO_SINK);

#if 0
#define GST_TYPE_QNXVIDEODRIVERS (gst_qnxvideo_sink_drivers_get_type())
static GType gst_qnxvideo_sink_drivers_get_type(void)
{
    static GType driver_type = 0;

    if (!driver_type)
    {
        GEnumValue *drivers;
        const struct aa_driver *driver;
        gint n_drivers;
        gint i;

        for (n_drivers = 0; aa_drivers[n_drivers]; n_drivers++)
        {
            /* count number of drivers */
        }

        drivers = g_new0(GEnumValue, n_drivers + 1);

        for (i = 0; i < n_drivers; i++)
        {
            driver = aa_drivers[i];
            drivers[i].value = i;
            drivers[i].value_name = g_strdup(driver->name);
            drivers[i].value_nick = g_utf8_strdown(driver->shortname, -1);
        }
        drivers[i].value = 0;
        drivers[i].value_name = NULL;
        drivers[i].value_nick = NULL;

        driver_type = g_enum_register_static("GstQnxVideoSinkDrivers", 
        									 drivers);
    }
    return driver_type;
}

#define GST_TYPE_AADITHER (gst_qnxvideo_sink_dither_get_type())
static GType gst_qnxvideo_sink_dither_get_type(void)
{
    static GType dither_type = 0;

    if (!dither_type)
    {
        GEnumValue *ditherers;
        gint n_ditherers;
        gint i;

        for (n_ditherers = 0; aa_dithernames[n_ditherers]; n_ditherers++)
        {
            /* count number of ditherers */
        }

        ditherers = g_new0(GEnumValue, n_ditherers + 1);

        for (i = 0; i < n_ditherers; i++)
        {
            ditherers[i].value = i;
            ditherers[i].value_name = g_strdup(aa_dithernames[i]);
            ditherers[i].value_nick =
                g_strdelimit(g_strdup(aa_dithernames[i]), " _", '-');
        }
        ditherers[i].value = 0;
        ditherers[i].value_name = NULL;
        ditherers[i].value_nick = NULL;

        dither_type = g_enum_register_static("GstQnxVideoSinkDitherers", 
        									ditherers);
    }
    return dither_type;
}
#endif

static void gst_qnxvideo_sink_class_init(GstQnxVideoSinkClass *klass)
{
    GObjectClass *gobject_class;
    GstElementClass *gstelement_class;
    GstBaseSinkClass *gstbasesink_class;
    GstVideoSinkClass *gstvideosink_class;
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    gobject_class = (GObjectClass *)klass;
    gstelement_class = (GstElementClass *)klass;
    gstbasesink_class = (GstBaseSinkClass *)klass;
    gstvideosink_class = (GstVideoSinkClass *)klass;

    gobject_class->set_property = gst_qnxvideo_sink_set_property;
    gobject_class->get_property = gst_qnxvideo_sink_get_property;

    /* FIXME: add long property descriptions */
    g_object_class_install_property(
    						G_OBJECT_CLASS(klass), PROP_WIDTH,
                          	g_param_spec_int("width", "width", "width", 
                          				  G_MININT, G_MAXINT, 0,
                                          G_PARAM_READWRITE | 
                                          G_PARAM_STATIC_STRINGS));
                                          
    g_object_class_install_property(
    						G_OBJECT_CLASS(klass), PROP_HEIGHT,
                        	g_param_spec_int("height", "height", "height", 
                        					 G_MININT, G_MAXINT, 0,
                                             G_PARAM_READWRITE | 
                                             G_PARAM_STATIC_STRINGS));
#if 0  
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DRIVER,
      g_param_spec_enum ("driver", "driver", "driver", GST_TYPE_QNXVIDEODRIVERS, 
          0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DITHER,
      g_param_spec_enum ("dither", "dither", "dither", GST_TYPE_AADITHER, 0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BRIGHTNESS,
      g_param_spec_int ("brightness", "brightness", "brightness", G_MININT,
          G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONTRAST,
      g_param_spec_int ("contrast", "contrast", "contrast", G_MININT, G_MAXINT,
          0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_GAMMA,
      g_param_spec_float ("gamma", "gamma", "gamma", 0.0, 5.0, 1.0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_INVERSION,
      g_param_spec_boolean ("inversion", "inversion", "inversion", TRUE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RANDOMVAL,
      g_param_spec_int ("randomval", "randomval", "randomval", G_MININT,
          G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property(G_OBJECT_CLASS(klass),
                                    PROP_FRAMES_DISPLAYED, 
                                    g_param_spec_int("frames-displayed", 
                                    				 "frames displayed", 
                                    				 "frames displayed", 
                                    				 G_MININT, G_MAXINT, 
                                    				 0, G_PARAM_READABLE |
                                    				 G_PARAM_STATIC_STRINGS));
                                    				 
    g_object_class_install_property(G_OBJECT_CLASS(klass), 
    								PROP_FRAME_TIME,
                                    g_param_spec_int("frame-time", 
                                    				 "frame time", 
                                    				 "frame time", 
                                    				 G_MININT,
                                                     G_MAXINT, 0, 
                                                     G_PARAM_READABLE |
                                                     G_PARAM_STATIC_STRINGS));
#endif
    gst_element_class_add_static_pad_template(gstelement_class, &sink_template);

    gst_element_class_set_static_metadata(gstelement_class,
                                          "Qnx video sink", 
                                          "Sink/Video", 
                                          "An Qnx videosink",
                                          "guwen <[email protected]>");

    gstelement_class->change_state = 
    							GST_DEBUG_FUNCPTR(gst_qnxvideo_sink_change_state);

    gstbasesink_class->fixate = GST_DEBUG_FUNCPTR(gst_qnxvideo_sink_fixate);
    gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR(gst_qnxvideo_sink_setcaps);
    gstbasesink_class->get_times = GST_DEBUG_FUNCPTR(gst_qnxvideo_sink_get_times);
    gstbasesink_class->propose_allocation =
        GST_DEBUG_FUNCPTR(gst_qnxvideo_sink_propose_allocation);

    gstvideosink_class->show_frame = 
    							GST_DEBUG_FUNCPTR(gst_qnxvideo_sink_show_frame);
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
}

static GstCaps* gst_qnxvideo_sink_fixate(GstBaseSink *bsink, GstCaps *caps)
{
    GstStructure *structure;
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    caps = gst_caps_make_writable(caps);

    structure = gst_caps_get_structure(caps, 0);

    gst_structure_fixate_field_nearest_int(structure, "width", 1920);
    gst_structure_fixate_field_nearest_int(structure, "height", 720);
    gst_structure_fixate_field_nearest_fraction(structure, "framerate", 30, 1);

    caps = GST_BASE_SINK_CLASS(parent_class)->fixate(bsink, caps);
    printf("[%s.%d]===>:caps: %p\n", __FUNCTION__, __LINE__, caps);
    return caps;
}

static gboolean gst_qnxvideo_sink_setcaps(GstBaseSink *basesink, GstCaps *caps)
{
    GstQnxVideoSink *qnxvideosink;
    GstVideoInfo info;
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    qnxvideosink = GST_QNXVIDEOSINK(basesink);

    if (!gst_video_info_from_caps(&info, caps))
        goto invalid_caps;

    qnxvideosink->info = info;
    qnxvideosink->width = info.width;
    qnxvideosink->height = info.height;
    printf("[%s.%d]===>:witdh: %d, height:%d\n", 
           __FUNCTION__, __LINE__, info.width, info.height);
           
    if (qnxvideosink->screen_win)
    {
        memset(&(qnxvideosink->dispRect), 0, sizeof(qnxvideosink->dispRect));
        qnxvideosink->dispRect[2] = qnxvideosink->width;
        qnxvideosink->dispRect[3] = qnxvideosink->height;
        screen_create_window_buffers(qnxvideosink->screen_win, GST_DISP_BUF_CNT);
        
        //screen_get_window_property_iv(qnxvideosink->screen_win,
        //                              SCREEN_PROPERTY_BUFFER_SIZE,
        //                              qnxvideosink->dispRect + 2);
        screen_set_window_property_iv(qnxvideosink->screen_win, 
                                      SCREEN_PROPERTY_SIZE, 
                                      qnxvideosink->dispRect + 2);

        screen_set_window_property_iv(qnxvideosink->screen_win, 
                                      SCREEN_PROPERTY_BUFFER_SIZE, 
                                      qnxvideosink->dispRect + 2);
    }


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

    /* ERRORS */
invalid_caps:
{
    GST_ERROR_OBJECT(qnxvideosink, "invalid caps");
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return FALSE;
}
}

static void
gst_qnxvideo_sink_init(GstQnxVideoSink *qnxvideosink)
{
#if 0    
    memcpy(&qnxvideosink->ascii_surf, &aa_defparams,
           sizeof(struct aa_hardware_params));

    qnxvideosink->ascii_parms.bright = 0;
    qnxvideosink->ascii_parms.contrast = 16;
    qnxvideosink->ascii_parms.gamma = 1.0;
    qnxvideosink->ascii_parms.dither = 0;
    qnxvideosink->ascii_parms.inversion = 0;
    qnxvideosink->ascii_parms.randomval = 0;
    qnxvideosink->aa_driver = 0;
#else
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    qnxvideosink->screen_win = NULL;
    qnxvideosink->screen_ctx = NULL;
    qnxvideosink->isOpened = FALSE;
    memset(&(qnxvideosink->dispRect), 0, sizeof(qnxvideosink->dispRect));
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
#endif
}

static void gst_qnxvideo_sink_scale(GstQnxVideoSink *qnxvideosink, 
									guchar *src, guchar *dest,
                       				gint sw, gint sh, gint ss, 
                       				gint dw, gint dh)
{
    gint ypos, yinc, y;
    gint xpos, xinc, x;

    g_return_if_fail((dw != 0) && (dh != 0));

    ypos = 0x10000;
    yinc = (sh << 16) / dh;
    xinc = (sw << 16) / dw;

    for (y = dh; y; y--)
    {
        while (ypos > 0x10000)
        {
            ypos -= 0x10000;
            src += ss;
        }
        xpos = 0x10000;
        {
            guchar *destp = dest;
            guchar *srcp = src;

            for (x = dw; x; x--)
            {
                while (xpos >= 0x10000L)
                {
                    srcp++;
                    xpos -= 0x10000L;
                }
                *destp++ = *srcp;
                xpos += xinc;
            }
        }
        dest += dw;
        ypos += yinc;
    }
}

static void
gst_qnxvideo_sink_get_times(GstBaseSink *sink, GstBuffer *buffer,
                           GstClockTime *start, GstClockTime *end)
{
    //printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    *start = GST_BUFFER_TIMESTAMP(buffer);
    if (GST_BUFFER_DURATION_IS_VALID(buffer))
        *end = *start + GST_BUFFER_DURATION(buffer);
}

static gboolean gst_qnxvideo_sink_propose_allocation(GstBaseSink *bsink, 
													 GstQuery *query)
{
    GstCaps *caps;
    GstVideoInfo info;
    guint size;
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    gst_query_parse_allocation(query, &caps, NULL);

    if (caps == NULL)
        goto no_caps;

    if (!gst_video_info_from_caps(&info, caps))
        goto invalid_caps;

    size = GST_VIDEO_INFO_SIZE(&info);

    /* we need at least 2 buffer because we hold on to the last one */
    gst_query_add_allocation_pool(query, NULL, size, 
    							  GST_DEFAULT_FRAME_BUF_CNT, 0);

    /* we support various metadata */
    gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL);
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return TRUE;

    /* ERRORS */
no_caps:
{
    GST_ERROR_OBJECT(bsink, "no caps specified");
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return FALSE;
}
invalid_caps:
{
    GST_ERROR_OBJECT(bsink, "invalid caps specified");
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return FALSE;
}
}

static GstFlowReturn gst_qnxvideo_sink_show_frame(GstVideoSink *videosink, 
												  GstBuffer *buffer)
{
    GstQnxVideoSink *qnxvideosink;
    GstVideoFrame frame;
  //  printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    qnxvideosink = GST_QNXVIDEOSINK(videosink);

    GST_DEBUG("show frame");

    if (!gst_video_frame_map(&frame, &qnxvideosink->info, 
    						 buffer, GST_MAP_READ))
        goto invalid_frame;

#if 0
    screen_pixmap_t pixmapInfo;
	screen_create_pixmap(&pixmapInfo, qnxvideosink->screen_ctx);

	int format = SCREEN_FORMAT_NV12;
	screen_set_pixmap_property_iv(pixmapInfo, 
								  SCREEN_PROPERTY_FORMAT, &format);

	gint usage = SCREEN_USAGE_WRITE | SCREEN_USAGE_NATIVE;
	screen_set_pixmap_property_iv(pixmapInfo, SCREEN_PROPERTY_USAGE, &usage);

	int size[2] = { qnxvideosink->width, qnxvideosink->height };
	screen_set_pixmap_property_iv(pixmapInfo, S
								  CREEN_PROPERTY_BUFFER_SIZE, size);

	screen_buffer_t pixmapBuf;
	screen_create_pixmap_buffer(pixmapInfo);
	screen_get_pixmap_property_pv(pixmapInfo, SCREEN_PROPERTY_RENDER_BUFFERS, 
								 (void **)&pixmapBuf);

	unsigned char *pBuf = NULL;
	screen_get_buffer_property_pv(pixmapBuf, SCREEN_PROPERTY_POINTER, 
								 (void **)&pBuf);

	int stride = 0;
	screen_get_buffer_property_iv(pixmapBuf, SCREEN_PROPERTY_STRIDE, &stride);
#if 0
	for (i = 0; i < size[1]; i++, ptr += stride) {
			for (j = 0; j < size[0]; j++) {
				ptr[j*4] = 0xa0;
				ptr[j*4+1] = 0xa0;
				ptr[j*4+2] = 0xa0;
				ptr[j*4+3] = ((j >= i && j <= size[1]-i) || 
							 (j <= i && j >= size[1]-i)) ? 0xff : 0;
			}
	}
#endif

#if 0
    gst_qnxvideo_sink_scale(qnxvideosink, 
    					   GST_VIDEO_FRAME_PLANE_DATA(&frame, 0), /* src */
                           pBuf,                     /* dest */
                           GST_VIDEO_INFO_WIDTH(&qnxvideosink->info),  /* sw */
                           GST_VIDEO_INFO_HEIGHT(&qnxvideosink->info), /* sh */
                           GST_VIDEO_FRAME_PLANE_STRIDE(&frame, 0),    /* ss */
                           qnxvideosink->width,                  /* dw */
                           qnxvideosink->height);                /* dh */

#else
    memcpy(pBuf, GST_VIDEO_FRAME_PLANE_DATA(&frame, 0), 
    	   qnxvideosink->width * qnxvideosink->height * 3 / 2);
#endif
	screen_buffer_t render_buf[2];
    screen_get_window_property_pv(qnxvideosink->screen_win, 
                                 SCREEN_PROPERTY_RENDER_BUFFERS, 
                                 (void **)render_buf);

    int hg[] = {
        SCREEN_BLIT_SOURCE_WIDTH, qnxvideosink->width,
        SCREEN_BLIT_SOURCE_HEIGHT, qnxvideosink->height,
        SCREEN_BLIT_DESTINATION_X, 0,
        SCREEN_BLIT_DESTINATION_Y, 0,
        SCREEN_BLIT_DESTINATION_WIDTH, qnxvideosink->width,
        SCREEN_BLIT_DESTINATION_HEIGHT, qnxvideosink->height,
        SCREEN_BLIT_TRANSPARENCY, SCREEN_TRANSPARENCY_SOURCE_OVER,
        SCREEN_BLIT_END
    };

    screen_blit(qnxvideosink->screen_ctx, render_buf[0], pixmapBuf, hg);

    screen_post_window(qnxvideosink->screen_win, render_buf[0], 1, 
    				   qnxvideosink->dispRect, 0);    

    screen_destroy_pixmap(pixmapInfo);
#else
    screen_buffer_t render_buf[GST_DISP_BUF_CNT];
    screen_get_window_property_pv(qnxvideosink->screen_win, 
    							  SCREEN_PROPERTY_RENDER_BUFFERS, 
    							  (void **)&render_buf);   

    char *pBuf = NULL;
	screen_get_buffer_property_pv(render_buf[0], SCREEN_PROPERTY_POINTER, 
								 (void **)&pBuf); 
								 
    memcpy(pBuf, GST_VIDEO_FRAME_PLANE_DATA(&frame, 0), 
    										qnxvideosink->width * 
    										qnxvideosink->height * 3 / 2);

    screen_post_window(qnxvideosink->screen_win, render_buf[0], 1, 
    				   qnxvideosink->dispRect, 0);
#endif

    gst_video_frame_unmap(&frame);
    qnxvideosink->frames_displayed++;
  //  printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return GST_FLOW_OK;

    /* ERRORS */
invalid_frame:
{
    GST_ERROR_OBJECT(qnxvideosink, "invalid frame");
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return GST_FLOW_ERROR;
}
}

static void
gst_qnxvideo_sink_set_property(GObject *object, guint prop_id, 
							   const GValue *value,
                               GParamSpec *pspec)
{
    GstQnxVideoSink *qnxvideosink;
    printf("[%s.%d]===>: prop_id:%d\n", __FUNCTION__, __LINE__, prop_id);
    qnxvideosink = GST_QNXVIDEOSINK(object);

    switch (prop_id)
    {
    case PROP_WIDTH:
        qnxvideosink->width = g_value_get_int(value);
        break;
    case PROP_HEIGHT:
        qnxvideosink->height = g_value_get_int(value);
        break;
#if 0
    case PROP_DRIVER:
    {
        qnxvideosink->aa_driver = g_value_get_enum(value);
        break;
    }
    case PROP_DITHER:
    {
        qnxvideosink->ascii_parms.dither = g_value_get_enum(value);
        break;
    }
    case PROP_BRIGHTNESS:
    {
        qnxvideosink->ascii_parms.bright = g_value_get_int(value);
        break;
    }
    case PROP_CONTRAST:
    {
        qnxvideosink->ascii_parms.contrast = g_value_get_int(value);
        break;
    }
    case PROP_GAMMA:
    {
        qnxvideosink->ascii_parms.gamma = g_value_get_float(value);
        break;
    }
    case PROP_INVERSION:
    {
        qnxvideosink->ascii_parms.inversion = g_value_get_boolean(value);
        break;
    }
    case PROP_RANDOMVAL:
    {
        qnxvideosink->ascii_parms.randomval = g_value_get_int(value);
        break;
    }
#endif
    default:
        break;
    }

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

static void
gst_qnxvideo_sink_get_property(GObject *object, guint prop_id, GValue *value,
                              GParamSpec *pspec)
{
    GstQnxVideoSink *qnxvideosink;
    printf("[%s.%d]===>:, prop_id: %d\n", __FUNCTION__, __LINE__, prop_id);
    qnxvideosink = GST_QNXVIDEOSINK(object);

    switch (prop_id)
    {
    case PROP_WIDTH:
    {
        g_value_set_int(value, qnxvideosink->width);
        break;
    }
    case PROP_HEIGHT:
    {
        g_value_set_int(value, qnxvideosink->height);
        break;
    }
#if 0
    case PROP_DRIVER:
    {
        g_value_set_enum(value, qnxvideosink->aa_driver);
        break;
    }
    case PROP_DITHER:
    {
        g_value_set_enum(value, qnxvideosink->ascii_parms.dither);
        break;
    }
    case PROP_BRIGHTNESS:
    {
        g_value_set_int(value, qnxvideosink->ascii_parms.bright);
        break;
    }
    case PROP_CONTRAST:
    {
        g_value_set_int(value, qnxvideosink->ascii_parms.contrast);
        break;
    }
    case PROP_GAMMA:
    {
        g_value_set_float(value, qnxvideosink->ascii_parms.gamma);
        break;
    }
    case PROP_INVERSION:
    {
        g_value_set_boolean(value, qnxvideosink->ascii_parms.inversion);
        break;
    }
    case PROP_RANDOMVAL:
    {
        g_value_set_int(value, qnxvideosink->ascii_parms.randomval);
        break;
    }
    case PROP_FRAMES_DISPLAYED:
    {
        g_value_set_int(value, qnxvideosink->frames_displayed);
        break;
    }
    case PROP_FRAME_TIME:
    {
        g_value_set_int(value, qnxvideosink->frame_time / 1000000);
        break;
    }
#endif
    default:
    {
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
        break;
    }
    }
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
}

static gboolean
gst_qnxvideo_sink_open(GstQnxVideoSink *qnxvideosink)
{
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    if (qnxvideosink->isOpened == FALSE)
    {
        screen_create_context(&qnxvideosink->screen_ctx, 
        					  SCREEN_APPLICATION_CONTEXT);
        					  
        screen_create_window(&qnxvideosink->screen_win, 
        					 qnxvideosink->screen_ctx);

        int usage = GST_DISP_USAGE;
        screen_set_window_property_iv(qnxvideosink->screen_win, 
        							  SCREEN_PROPERTY_USAGE, &usage);

        int format = SCREEN_FORMAT_NV12;
        screen_set_window_property_iv(qnxvideosink->screen_win, 
        							  SCREEN_PROPERTY_FORMAT, &format);


        qnxvideosink->isOpened = TRUE;
        printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
        return TRUE;
    }
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return FALSE;
}

static gboolean gst_qnxvideo_sink_close(GstQnxVideoSink *qnxvideosink)
{
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    if (qnxvideosink->isOpened == TRUE)
    {
        if (qnxvideosink->screen_win)
        {
            screen_destroy_window(qnxvideosink->screen_win);
            qnxvideosink->screen_win = NULL;
        }    

        if (qnxvideosink->screen_ctx)
        {
            screen_destroy_context(qnxvideosink->screen_ctx);
            qnxvideosink->screen_ctx = NULL;    
        }
        printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
        return TRUE;
    }
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return FALSE;
}

static GstStateChangeReturn
gst_qnxvideo_sink_change_state(GstElement *element, 
							   GstStateChange transition)
{
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    GstStateChangeReturn ret;

    switch (transition)
    {
    case GST_STATE_CHANGE_NULL_TO_READY:
        break;
    case GST_STATE_CHANGE_READY_TO_PAUSED:
        if (!gst_qnxvideo_sink_open(GST_QNXVIDEOSINK(element)))
            goto open_failed;
        break;
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
        break;
    default:
        break;
    }

    ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);

    switch (transition)
    {
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
        break;
    case GST_STATE_CHANGE_PAUSED_TO_READY:
        break;
    case GST_STATE_CHANGE_READY_TO_NULL:
        gst_qnxvideo_sink_close(GST_QNXVIDEOSINK(element));
        break;
    default:
        break;
    }

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

open_failed:
{
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return GST_STATE_CHANGE_FAILURE;
}
}

static gboolean plugin_init(GstPlugin *plugin)
{
    printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    if (!gst_element_register(plugin, "qnxvideosink", 
    						  GST_RANK_SECONDARY, GST_TYPE_QNXVIDEOSINK))
    {
        printf("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
        return FALSE;
    }

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

GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
                  GST_VERSION_MINOR,
                  qnxvideo,
                  "Qnx video sink",
                  plugin_init, PLUGIN_VERSION, 
                  GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);

注意, 輸出的plugin動態庫名稱以 libpluginname.so 的形式, 而這個pluginname就是 編寫插件時,定義插件的宏GST_PLUGIN_DEFINE 的參數中的name, 如果不匹配,就會出現插件無法是使用,被gstreamer將插件加入黑名單

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章