利用Qt Phonon框架制作音视频播放器

     Phonon严格来说其实非为Qt的library,Phonon原本就是KDE 4的开放源代码多媒体API,後来与Qt合并与开发,所以简单来说就是Qt使用Phonon这个多媒体框架来提供一般影音多媒体档案的播放,而这些影音多媒体来源可以是档案、网路串流或是指到一个档案的QUrl。Phonon是一个跨平台多媒体框架,能够在Qt应用程式中使用与播放影音多媒体内容。

 

Phonon的架构

整体来说,Phonon的架构只需要记住以下的三东西:

  • media object
    Phonon的基础,用於管理多媒体来源。来源可能是影音档等,而能够提供基本的播放控制,例如开始、暂停或结束。而提供多媒体资料给media object的则为media source,在给media object之前通常是raw data,再由media object进行转换。
  • sinks
    输出多媒体,例如在 widget上播放影片或是输出至音效卡(播放音乐)。通常 sink是一个播放的装置(例如音效卡等)。而sink只接受media object来的资料,由media object控制播放;而由sink来处理这些多媒体

  • paths
    用来连接Phonon的物件,意即media object与sink之间的连接。


    所以整个播放影音的流程就是首先由media object开始播放,接著把媒体串流经由path送至sink,sink会经由音效卡等装置重新播放(play back)影音。

安装

QtSoftware官方网站是说Phonon预设会跟在安装Qt时一并安装,不过我不论是从Qt SDK或从source code重新build,都没有包含Phonon模组,目前我是用另外一种方式来安装Phonon,就是使用套件管理工具来从套件包来安装,只需要安装下 列的套件:

sudo apt-get install libphonon-dev libphonon4 phononbackend-gstreamer

安装完毕之後,就可以使用Phono模组罗。


而与其他Qt应用程式一样,若有使用到Phonon函式库的应用程式在build的时候都需要额外设定使用Phonon模组,必须要在qmake project file也就是.pro文件中加入:

QT += phonon

VideoPlayer Class

Phonon有提供很多类别可供使用,其中最简单的,莫过於VideoPlayer这个类别了。
VideoPlayer widget如它的名字一样,就是用来播放video,而且使用起来相当简易,而且功能也不会缺少,包括播放、暂停与停止。
而一开始早先提到的MediaObject等类别与VideoPlayer在使用上有什么差异呢?如果不需要更复杂的功能,例如建立一个media graph,你只需要能够播放影音档案的话,其实使用VideoPlayer类别即可达到你的要求。
而另外值得一提的就是,VideoPlayer大部分函式都是非同步,所以载入media source并不会马上播放多媒体档案,只有在呼叫函式play( )之後才会播放。
要怎么使用这个类别呢? 其实相当简单,下面就是程式码片段:
VideoPlayer *player = new VideoPlayer(Phonon::VideoCategory, parentWidget);
player->play(url);
在实体化VideoPlayer类别物件时,可以在constructor就载入多媒体种类与要放在哪个widget中(即为 parentWidget),而media source可以利用函式load()来载入或是在play()时载入,而载入方式可直接从档案或是从网路位址。
以下就是一个简单的影音播放功能小程式:
#include 
#include 
#include 
#include 
 
int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
 
    QWidget *widget = new QWidget;
    widget->setWindowTitle("Video Player");
    widget->resize(400,400);
 
    Phonon::VideoPlayer *player = new Phonon::VideoPlayer(Phonon::VideoCategory, widget); 
    player->load(Phonon::MediaSource("../Puppet.mpg"));//貌似只支持mpg格式
 
    player->play();
 
    widget->show();
 
    return app.exec();
}
MediaObject Class
MediaObject类别主要提供一个能够处理媒体播放的介面。
MediaObject可说是处理多媒体档案最基本的一部份,它接受并管理来自於MediaSource的媒体档案。而媒体播放、暂停与停止都是由它来控制;而在此之前,media物件必须要与output node连接,如早先所讲的,这个nodes主要将媒体输出至底层的硬体,例如音效卡或显示卡等,而所需要的output node则是根据多媒体的内容而所不同,目前Phonon有两种output node;
  • AudioOutput-声音播放
  • VideoWidget-影像播放
如果MediaSource包含声音与影像的话,这两种node都必须要连接至media物件。
就这个类别来说,有几个函式是你必须要知道的,分别是
  • setCurrentSource():设定MediaObject的多媒体来源,而来源可以是网路上的影音档(利用QUrl来存取)或是本机档案(利用QString),使用上相当简单:

          QUrl url("http://www.example.com/music.ogg");
          media->setCurrentSource(url);

  • play():开始播放多媒体资料
  • pause():暂停播放
  • stop():停止播放

以下为一个简单的程式片段,说明如何使用:
Phonon::MediaObject *mediaObject = new Phonon::MediaObject(this);
 
Phonon::VideoWidget *videoWidget = new Phonon::VideoWidget(this);
Phonon::createPath(mediaObject, videoWidget);
 
Phonon::AudioOutput *audioOutput =
         new Phonon::AudioOutput(Phonon::VideoCategory, this);
Phonon::createPath(mediaObject, audioOutput);
 
mediaObject->play();
Phonon::createPath()

这是相当重要的一个函式,主要用於建立一个Path,连接两个MediaNodes,就是source与sink。

其实它的主要用途是在更进阶的部分,就是在使用到media graph,不过如果只是利用Phonon来播放多媒体影音档,其实只要记得它是用来连接source与输出装置即可。

 

AudioOutput Class

 

AudioOutput类别主要是用来把多媒体的声音送到声音输出装置。所以它能够经由类似喇叭等输出装置来播放声音,稍早有提过,多媒体资料的来源必须要经过Phonon::createPath()由MediaObject连接。

Phonon::MediaObject *mediaObject = new Phonon::MediaObject(this);
mediaObject->setCurrentSource(Phonon::MediaSource("/mymusic/barbiegirl.wav"));
Phonon::AudioOutput *audioOutput =new Phonon::AudioOutput(Phonon::MusicCategory, this);
Phonon::Path path = Phonon::createPath(mediaObject, audioOutput);
编码:mainwindow.h
我们已经为我们的应用程序创建了框架,现在只要添加功能即可。点击“mainwindow.h”,并在顶部添加如下代码行:
#include #include #include #include

以上代码的作用是,通过头文件导入我们要在代码中使用的Qt函数。现在我们需要添加我们的槽,它们在我们前面编辑过的ui文件中已经定义好了。在“public:”部分中的“~MainWindow();”行正下方,添加如下代码:

private slots:
    void playPause();
    void addFiles();
    void nextFile();
    void aboutToFinish();
    void finished();

可以看到,这些槽对应于我们在用户界面中创建的名称,而且我们还添加了更多的槽来处理内部通信。最后,在“private”部分中添加如下变量,我们将在应用程序的主要逻辑中用到这些变量:

    QList sources;
    Phonon::MediaObject *mediaObject;
    Phonon::AudioOutput *audioOutput;
    Phonon::MediaObject *metaInformationResolver;
编码:mainwindow.cpp

现在,我们需要做的第一件事情是初始化Phonon,并建立内部的信号和槽。这可以通过标准的初始化方法MainWindow::MainWindow来完成。如果你看一看这个方法的内容,就会发现应用程序的GUI是由“ui->setupUi(this);”行来运行的。(注意千万不要在setupUI之前来调用UI上面的组件,这时候根本还没创建)这意味着,我们需要在这之前加入我们的预运行代码。我们将从设置Phonon开始:

     audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this);
     mediaObject = new Phonon::MediaObject(this);
     metaInformationResolver = new Phonon::MediaObject(this);
     Phonon::createPath(mediaObject, audioOutput);

在Phonon术语中,我们要创建的audioOutput对象叫做音频接收槽。它是直接与音频驱动器通信的层的组成部分,并充当MediaObject的虚拟音频设备。MediaObject位于这一层的上层,增加了诸如暂停、播放和倒带之类的功能。顺便提一句,MusicCategory不一定是必需的,但它可以对未来发展起到作用,比如可以根据正在收听的内容自动变化的KDE均衡器。

我们将使用“metaInformationResolver”来指向当前音频文件,而最后一行在接收槽和媒体对象之间建立了连接。Phonon使用了一种叫做“graph”的框架,这意味着对象就像是一幅图上的节点,需要连接起来才能创建流向。这正是以上代码的最后一行的作用。

现在添加如下用于处理playPause()槽的新函数:

 

void MainWindow::playPause()
{
    switch (mediaObject->state()){
        case Phonon::PlayingState:
            mediaObject->pause();
            ui->pushButtonPlay->setChecked(false);
            break;
        case Phonon::PausedState:
            mediaObject->play();
            break;
        case Phonon::StoppedState:
            mediaObject->play();
            break;
        case Phonon::LoadingState:
            ui->pushButtonPlay->setChecked(false);
            break;
    }
}

以上代码应该不难理解。我们使用一条Case语句检查播放的当前状态。Phonon通过MediaObject为我们提供了这种功能。它实际上就是一个整数,但是每个值代表着特定的状态。例如,如果应用程序是首次启动,播放列表中尚未加入任何文件,就会返回LoadingState值。播放和暂停由媒体对象进行处理,在“mediaObject->pause();”命令之后将自动从当前位置继续。

 
  
void MainWindow::addFiles()
{
    QStringList files = QFileDialog::getOpenFileNames(this, tr("Select Music Files"),
        QDesktopServices::storageLocation(QDesktopServices::MusicLocation));
 
    ui->pushButtonPlay->setChecked(false);
    if (files.isEmpty())
        return;
    int index = sources.size();
    foreach (QString string, files) {
            Phonon::MediaSource source(string);
         sources.append(source);
    }
    if (!sources.isEmpty()){
        metaInformationResolver->setCurrentSource(sources.at(index));
        mediaObject->setCurrentSource(metaInformationResolver->currentSource());
 
    }
}

 

 

 

现在,让我们开始实现添加文件的部分。创建文件请求器几乎是自动的,我们可以把结果放到一个字符串列表中。“QdesktopServices::MusicLocation”返回一个通常用于保存音乐文件的特定操作系统位置,而我们使用它作为请求器的起始位置。接下来,我们将把已经选择的每个音乐文件添加到我们早先创建的“metaInformationResolver”中,并使用它告诉mediaObject接下来播放哪个文件。在所有这些工作间隙,我们进行一点清理工作,以确保队列中有文件,而且当处于播放状态时出现播放按钮。添加文件将自动停止播放。

void MainWindow::nextFile()

{    

      int index = sources.indexOf(mediaObject->currentSource()) + 1;    

      if (sources.size() > index)

     {        

           mediaObject->stop();        

           mediaObject->setCurrentSource(sources.at(index));        

           mediaObject->play();    

     }

}

上面在音乐播放时进度条和音量控制条的显示也是在Phonon中有自己的组件来显示,只要将当前的mediaObject赋给它们就行啦。 在头文件中:    

      Phonon::SeekSlider *seekSlider;//实现进度条

SeekSlider类别提供一个可滑动的slider来设定多媒体串流播放的位置。所以它会连接到MediaObject,并控制串流目前的位置。以下是一个使用的范例:

Phonon::MediaObject *moo = new Phonon::MediaObject;;

Phonon::AudioOutput *device = new Phonon::AudioOutput;

Phonon::createPath(moo, device);

moo->setCurrentSource(QString("/home/gvatteka/Music/Lumme-Badloop.ogg"));

Phonon::SeekSlider *slider = new Phonon::SeekSlider;

slider->setMediaObject(moo);

slider->show();

moo->play();


VolumeSlider Class

 

VolumeSlider widget提供可以控制声音装置音量的widget。用法其实与上面的SeekSlider类似,使用范例如下:honon::AudioOutput *audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory);

Phonon::createPath(mediaObject, audioOutput);

Phonon::VolumeSlider *volumeSlider = new Phonon::VolumeSlider;

volumeSlider->setAudioOutput(audioOutput);

范例 
再看过上面的一些类别介绍之後,其实就可以显一个简单的媒体播放器了,请看范例程式码: 
#include

 #include

 #include

 #include

 #include

 #include  

#include

 #include   
int main(int argc, char *argv[])

 {   

  QApplication app(argc, argv);   
    QWidget *widget = new QWidget; 
    widget->setWindowTitle("Media Player");    

 widget->resize(400,400);   
    Phonon::MediaObject *media = new Phonon::MediaObject; 
    media->setCurrentSource(Phonon::MediaSource("../Puppet.mpg"));   
    Phonon::VideoWidget *vwidget = new Phonon::VideoWidget(widget);    

 Phonon::createPath(media, vwidget); 
 vwidget->setAspectRatio(Phonon::VideoWidget::AspectRatioAuto);    

 Phonon::AudioOutput *aOutput = new 
 Phonon::AudioOutput(Phonon::VideoCategory);    

 Phonon::createPath(media, aOutput);   
 QLabel *label = new QLabel("Volume: "); 
 Phonon::VolumeSlider *volumeSlider = new Phonon::VolumeSlider;    

 volumeSlider->setAudioOutput(aOutput); 
 volumeSlider->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);   
 Phonon::SeekSlider *seekSlider = new Phonon::SeekSlider;    

 seekSlider->setMediaObject(media);   
 QHBoxLayout *hLayout = new QHBoxLayout;    

 hLayout->addWidget(label); 
 hLayout->addWidget(volumeSlider);    

 hLayout->addStretch();   
 QVBoxLayout *vLayout = new QVBoxLayout;   

 vLayout->addWidget(vwidget);     

 vLayout->addWidget(seekSlider);   

 vLayout->addLayout(hLayout);   
 widget->setLayout(vLayout);   
 widget->show();     

 media->play();   

 return app.exec(); 


上面的程式码就完成了一个简易的多媒体播放器,当然还有很多部分还需要改进,不过在此仅就Phonon的使用上做介绍。执行之後的画面如下所示: 由於VideoWidget已经嵌入到QWidget了,所以你调整视窗大小的话,影片播放的视窗也会跟著调整。

VideoWidget类别提供能够显示出影片的widget。VideoWidget类别会在QWidget上播放多媒体串流的影像,跟AudioOutput一样,必须使用 Phonon::createPath()来与MediaObject连接。你可以利用一些函式来控制在QWidget中的VideoWidget显示的 大小,你可以利用setAspectRatio()或setScaleMode()来控制,而它们接收的参数可以到网站上察看,使用方式如下(预设是使用 aspect ratio):

videowidget->setAspectRatio(Phonon::VideoWidget::AspectRatioAuto);

videowidget->setScaleMode(Phonon::VideoWidget::ScaleAndCrop);
当然也有提供函式让影片进入或退出全萤幕模式。以下为一个简短的程式码范例:
MediaObject *media = new MediaObject(parent);

VideoWidget *vwidget = new VideoWidget(parent);

Phonon::createPath(media, vwidget);

补充:

   多媒体文件的播放主要过程包括:文件读取、分流、解码、输出。


    这些在不同的系统中实现方式不同,如windows下的DirectX、Linux下的 gstreamer或xine及Mac下的QuickTime。 而Qt中的phonon作为夸平台的多媒体解决方案,就因该为用户屏蔽掉这些差异。而实际上它做得还不够好,因为用户还需要自己来安装相应的后端插件来完成播放任务。


    看完上图,你可以知道,应用Phonon框架实质上分4个部分,你的程序,Phonon库,Phonon后端插件(phonon_backend),真正的后端。 Phonon其实什么都不干,他只是提供了一套API接口这套接口可以给你的程序调用,同时也是给编写后端插件提供一个规范。程序完全不知道最后谁来放我的Mp3,谁来解码我的视频,播放的又是哪个设备。
    同样,Phonon库也不知道,他只管搜索符合自己规范的插件。并告诉这些插件,现在程序发出的指令是什么,从插件返回给程序现在的媒体状态和信息。而后端才是实际进行读入媒体、解码并且播放的部分,他们和后端插件是一一对应的。后端可以是任何形式,只要你写出了相应的后端插件。所以,你要使用Phonon必需要先做3件事:
(1) 编译你的Phonon库:通常情况下,Qt默认没有编译,你只需要configure后加好参数,然后单独进入phonon的文件夹进行编译就可以了 
(2) 编译你的Phonon后端插件:Qt为3大平台分别提供了一个可用的后端插件,放在src\plugins\phonon目录下。进入目录编译即可,Qt会自动选择你当前系统的后端插件的。(出现phonon backend plugin could not be loaded就是这步没有做) 
     如果你是MinGW用户,那抱歉了,Windows下Qt只提供了DirectShow这个后端插件,而这个后端插件因为调用了DX的SDK,所以只能由VC编译。当然你其实有更好的选择,比如这个phonon-vlc-mplayer插件,通过他,你可以将mplayer作为你的后端,彻底抛弃臃肿的VS以及读ID3v2都会出错的DirectShow(其实也不能怪DirectShow,DirectShow同样只是个框架,解码还靠系统里安的解码器、滤镜们,我用的播放器都太绿色了,于是DirectShow就很弱) 
(3) 你要保证你的后端正常运作:对于Qt提供默认提供插件的后端来说,基本不成问题。但要是你想做嵌入式播放器?恩,很大的问题。据做过GStreamer移植的人反映,这事恼火的很。想做Qt嵌入式播放器的还是老老实实的QProcess+Mplayer slave模式吧!(新的Qt4.5说是提供了CE上的DirectShow后端插件,有兴趣的可以去试试看) 
    以上3点做完,你就可以拿demo里那个mediaplayer去测试了,测试成功的话,你就可以非常容易的用Qt实现你自己的媒体回放了。想要更高级的操作,比如编码、混音、储存媒体文件?呵呵,等Phonon慢慢更新吧,现在来说,Phonon还只是个什么都不能干的传声筒

   如上所述,如果想要运行使用Qt中的phonon写的程序,需要满足一下条件:Qt基本库、Qt phonon库、phonon_backend(后端插件)和多媒体播放后台。



你可能感兴趣的:(QT,播放器,qt,ubuntu,视频,框架,video)