存档

文章标签 ‘gstreamer’

gstreamer播放wav文件

五月 6th, {2010 3 条评论 10,556 人阅读过  

有网友留言希望我在飞信上加上声音提示,下午的时候整了整,很早以前写过一个直接操作/dev/dsp的小程序用来播放wav,后来发现在当另一个程序在使用这个设备文件的时候,程序就无法打开/dev/dsp这个文件,也就无法完成播放,但因为实现简单我在飞信的第一个版本里面用了这种方法,后来觉得不好就直接把声音提示给去掉了。

在网上查了一下实现wav播放的方法,大多数都是这种直接操作/dev/dsp的,GTK也没有像QT那样的直接播放音频文件的类,于是我只好使用第三方的库了,网上很流行的gstreamer,今天简单读了一下它的手册,发现gstreamer确实是很强大,和GTK一样,它也是基于面向对象的思想来实现的,C语言的面向对象,这样对象和对象之间的继承和派生的关系他们都实现的很完美,有时间一定要仔细研究一下具体的实现方法,gstreamer还有一个更强大的特点是它的管道流机制,gstreamer里面的基本元素是element,element上有pad,每一个element都是一个独立的组件,来完成相应的功能,pad应该可以理解成接口吧,我也不知道该翻译成什么,element之间通过各种pad连接起来形成一个pipe,数据流就在这个pipe里面流动,每经过一个element就会经过一层过滤,比如经过数据压缩的element,数据解码的element,这让我想到了很多年前开发OA的时候里面的工作流,不过这种机制用在这里确实是个好主意,要实现一个复杂的功能只需要把各种独立实现某个小功能的element连接成一个pipe,数据流依次流过这个pipe的时候就完成了对这个数据流的复杂的处理,可扩展性也很高,当需要新的功能的时候只需要开发新的组件,提供相应的接口就可以添加到应用程序中去。

gst-inspect命令列出当前系统中已经安装了的gstreamer组件以及这些组件所具有的特性。

gst-launch命令创建并运行一个pipe,组件之间的管道用”!”作为分隔符,为了跟shell的管道分隔符区分开没有用”|”,下面是几个例子:

播放一个mp3文件,第二个组件mad是用来对mp3进行解码的,我系统上没装这个组件…
gst-launch filesrc location=music.mp3 ! mad ! osssink

其中location是组件filesrc的一个属性,filesrc是要从一个文件读取源数据,location指出了文件的路径
具体还有很多,详见http://linux.about.com/library/cmd/blcmdl1_gst-launch.htm

播放wav的时候需要用到wavparse这个组件,它在gst-plugins-good这个包里面,我的slackware没有预装,我在slackbuild.org上下载上来装上了。

在命令行用gst-launch命令播放的时候跟播放mp3时候类似,但在程序里面实现的时候就不同了,简单的把filesrc,wavparse和alsasink这三个组件连接起来会失败,这困扰了我好久,最后终于弄明白了,wavparse这个组件的sink pad只能在数据流来了以后才可以连接,因为wavparse需要先知道输入给它的数据的类型,所以在wavparse创建的时候link就会失败。解决的办法是使用信号动态地连接pad,gst_bin_add_many这个函数调用的时候会触发add_pad信号,回调函数参数列表里面就有新创建的pad,把这个pad连接到alsasink的sink pad上去就可以了,下面是gstreamer播放wav的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <gst/gst.h>
 
static void
add_pad (GstElement *element , GstPad *pad , gpointer data){
 
	gchar *name;
	GstElement *sink = (GstElement*)data;
 
	name = gst_pad_get_name(pad);
	gst_element_link_pads(element , name , sink , "sink");
	g_free(name);
}
 
static gboolean
bus_watch(GstBus *bus , GstMessage *msg , gpointer data)
{
    GMainLoop *loop = (GMainLoop *) data;
    if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_EOS){
        g_main_loop_quit(loop);
    }
    return TRUE;
}
 
void
play_file(const char *filename){
 
	GMainLoop *loop;
	GstElement *pipeline;
	GstBus *bus;
	GstElement *source , *parser , *sink;
 
	loop = g_main_loop_new(NULL , TRUE);
 
	pipeline = gst_pipeline_new("audio-player");
 
	source = gst_element_factory_make("filesrc" , "source");
	parser = gst_element_factory_make("wavparse" , "parser");
	sink = gst_element_factory_make("alsasink" , "output");
 
	g_object_set(G_OBJECT(source) , "location"
			, filename , NULL);
 
	bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
	gst_bus_add_watch(bus , bus_watch , loop);
	g_object_unref(bus);
 
	gst_bin_add_many(GST_BIN(pipeline)
			, source , parser , sink , NULL);
 
	g_signal_connect(parser
			, "pad-added" , G_CALLBACK(add_pad) , sink);
 
	if(! gst_element_link(source , parser)){
		g_warning("linke source to parser failed");
	}
 
	gst_element_set_state(pipeline , GST_STATE_PLAYING);
	printf("Start playing...\n");
	g_main_loop_run(loop);
	printf("Playing stopped!!!\n");
	gst_element_set_state(pipeline , GST_STATE_NULL);
	g_object_unref(pipeline);
}
 
int
main(int argc , char *argv[]){
 
	gst_init(&argc , &argv);
 
	play_file(argv[1]);
 
	return 0;
}

编译:

1
$gcc -o gst gst.c `pkg-config --cflags --libs gstreamer-0.10`

分类: C/C++ 标签: , ,