Gstreamer 工具使用(二)

前面两篇我们完成了两件很重要的事情,第一是建立了编写插件程式的环境和测试方法,第二是替插件装好了进出水阀(sinkpadsrcpad)的格式和属性,格式不合的资料进不来,也出不去。接下来我们要开始放水,让资料流进这个插件。

gstreamer在处理资料的流动有两种主要的模式,一个是「推」,一个是「拉」。两种模式需要实作的routine不同,在对资料的操作(manipulation)上的重点也不一样,很容易被搞得摸不清方向(其实我到现在还是有很多没搞懂的地方…)。首先先解释一下两者的不同。

「推」模式就是由上游的插件控制资料的大小、流速,向下「推」到下游的插件,所以下游的插件并不会事先知道有多少资料会被送进来,它就必须先准备一个缓冲区来承接资料,然后判断缓冲区里的资料是否足够拆解出一个压缩单位的资料,够的话就把资料切割出一个固定大小送给解码器,剩下的资料要留著和下一笔流进来的资料做连接。

「拉」模式则是需要自己控制资料大小、流速,告诉上游的插件说自己要多少资料,从几分几秒开始读,自己控制速度、大小等等变数,把资料「拉」进来。因为要流进来的资料量(举例来说,media-objectsizechunksizepacket size)自己可以控制,就不需要设计一个缓冲区来放资料。

通常,「拉」模式会用在 demuxer,而「推」模式用在其他插件,所以gst-template提供的例子是「推」模式的写法。_chain()函式就是让上游插件把资料送进来的接口,当资料开始流动的时候(完成启动阶段(activationstage)后,启动的部份留待后述。)会直接唤起初始阶段时向pad注册的chain 函式,这个函式的介面(GstPadChainFunction)是已经被定义好的,其中一个变数是GstBuffer的指标,资料就被塞在这个指标所指向的记忆体空间。我们便可以透过注册进去的函式,取得操作这段资料的handle

Gstreamer 在处理资料流有四个状态:Null,Ready, Pause, Playing按顺序切换。也就是说,刚开始播放一个档案时状态变化是:Null –> Ready –> Pause –>Playing,当播放结束要释放pipeline的顺序就是原路走回去:Playing–> Pause –> Ready –> Null。我们写的这个mp3dec插件是要把mpegaudio decoder libmad包装为 gstreamer插件,所以在开始播放档案之前必须先把插件初始化(比如说,设定membervariable的初始值,初始化 gstreamer的其他元件等等),当然,也要先初始化libmad。初始化的动作一般来说,应该要放在Null转到Ready的阶段,或 Ready 转到Pause 的阶段,绝对不可能是在Pause转到Playing的阶段,因为 Pause Playing 两个状态是切换播放模式用的(如:暂停、快进、Seeking)

到目前为止都很抽象,我们走进源码来看就会好一点。

为了处理刚提到的状态切换,我们要注册一个_change_state()函式。

   1: static GstStateChangeReturn
   2:     gst_mp3dec_change_state(GstElement* element, GstStateChange transition)
   3: {
   4:     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
   5:     Gstmp3dec *dec;
   6:     dec = GST_MP3DEC(element);
   7:
   8:     switch(transition)
   9:     {
  10:         case GST_STATE_CHANGE_NULL_TO_READY:
  11:             mad_frame_init(&dec->frame);
  12:             mad_stream_init(&dec->stream);
  13:             mad_synth_init(&dec->synth);
  14:         break;
  15:         default:
  16:         break;
  17:     }
  18:
  19:     ret = parent_class->change_state(element, transition);
  20:     if(ret == GST_STATE_CHANGE_FAILURE)
  21:         return ret;
  22:
  23:     switch(transition)
  24:     {
  25:         case GST_STATE_CHANGE_READY_TO_NULL:
  26:             gst_mp3dec_reset(dec);
  27:         break;
  28:         default:
  29:         break;
  30:     }
  31:     return ret;
  32: }
  33:
  34: static void gst_mp3dec_clas_init()
  35: {
  36:     ...
  37:     gstelement_class->change_state = gst_mp3dec_change_state;
  38:     ...
  39: }

如刚所说,当状态从 NULL 转到READY(GST_STATE_CHANGE_NULL_TO_READY),插件要做初始化,配置记忆体等。反过来当状态从READY转到NULL(GST_STATE_CHANGE_READY_TO_NULL),就要释放资源。为了避免当主要的执行续(mainthread)还在运作时,就因为收到「停止」的指令,从PLAYING切进NULL,把资源都给释放掉,所以状态转换要分成两个switch-case来处理。

我们可以试著讨论一下 pipeline如此处理状态切换的理由是什么。想像你手上有一个滤水器,一个水桶的污水和一个干净的水壶


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值