Qt-Gstreamer에서 예제 코드를 몇 개 제공한다.
그 중에서 player코드를 분석해보자.
해당 플레이어는 pc에 저장된 동영상 파일을 읽어와서 재생, 정지, 일시정지 기능을 제공한다.
코드 구조
- 매우 심플하다.
- main을 제외하고 두 개의 코드 파일만 필요하다. (mediaapp, player)
코드의 큰 흐름으로 먼저 따라가보자
1. 응용프로그램 실행
// main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGst::init(&argc, &argv);
MediaApp media;
media.show();
if (argc == 2) {
// argv[1] : 파일 uri
media.openFile(argv[1]);
}
return app.exec();
}
- MediaApp이라는 클래스를 생성하고 argument로 받은 파일의 경로를 전달해준다. openFile()
2. MediaApp 클래스 생성자
//mediaapp.cpp
MediaApp::MediaApp(QWidget *parent)
: QWidget(parent)
{
//create the player
m_player = new Player(this);
connect(m_player, SIGNAL(positionChanged()), this, SLOT(onPositionChanged()));
connect(m_player, SIGNAL(stateChanged()), this, SLOT(onStateChanged()));
//m_baseDir is used to remember the last directory that was used.
//defaults to the current working directory
m_baseDir = QLatin1String(".");
//this timer (re-)hides the controls after a few seconds when we are in fullscreen mode
m_fullScreenTimer.setSingleShot(true);
connect(&m_fullScreenTimer, SIGNAL(timeout()), this, SLOT(hideControls()));
//create the UI
QVBoxLayout *appLayout = new QVBoxLayout;
appLayout->setContentsMargins(0, 0, 0, 0);
createUI(appLayout);
setLayout(appLayout);
onStateChanged(); //set the controls to their default state
setWindowTitle(tr("QtGStreamer example player"));
resize(400, 400);
}
- MedaApp 클래스 의 생성자이다.
- 첫 줄에서 보면 알 수 있듯이 비디오의 스트리밍을 제어하는 player라는 객체가 따로 있다.
- 즉, MediaApp : GUI 윈도우 창, Player: 비디오 스트리밍 컨트롤 이다.
3. MediaApp::openFile()
main에서 두 번째 argument를 openFile() 메서드로 넘겨줬다. 그 흐름을 따라서 openFile() 메서드를 살펴보자.
void MediaApp::openFile(const QString & fileName)
{
m_baseDir = QFileInfo(fileName).path();
m_player->stop();
m_player->setUri(fileName);
m_player->play();
}
- 비디오 플레이어 윈도우 창인 MediaApp에서 파일의 이름을 인자로 전달 받았다.
- 해당 인자를 스트리밍 컨트롤을 담당하는 player에게 넘겨준다. setUri()
4.Player::setUri()
void Player::setUri(const QString & uri)
{
QString realUri = uri;
//if uri is not a real uri, assume it is a file path
if (realUri.indexOf("://") < 0) {
realUri = QUrl::fromLocalFile(realUri).toEncoded();
}
if (!m_pipeline) {
m_pipeline = QGst::ElementFactory::make("playbin").dynamicCast<QGst::Pipeline>();
if (m_pipeline) {
//let the video widget watch the pipeline for new video sinks
watchPipeline(m_pipeline);
//watch the bus for messages
QGst::BusPtr bus = m_pipeline->bus();
bus->addSignalWatch();
QGlib::connect(bus, "message", this, &Player::onBusMessage);
} else {
qCritical() << "Failed to create the pipeline";
}
}
if (m_pipeline) {
m_pipeline->setProperty("uri", realUri);
}
}
- player의 로컬변수 m_pipeline이 null인지 확인한다.
- 만약 null일 경우, pipeline을 새로 생성해준다.
- playbin을 이용해 파이프라인을 만든다.
*playbin : playbin을 이용하면 오직 하나의 element만을 사용해서 파이프라인을 구성할 수 있다. playbin은 Gstreamer에서 제공하는 특별한 element인데, source로서도 동작을하고, sink로서도 동작을 한다. 즉 파이프라인의 전체를 담당할 수 있다. playbin을 생성하고, 미디어 URI만 넘겨주면 쉽게 플레이를 할 수 있다. (GStreamer Basic tutorial 1 참고)
*QGst의 ElementFactory를 이용해서 플레이빈 엘레먼트를 만든다.
- GUI 윈도우 창이 새로운 비디오 싱크를 위해 pipeline을 대기하고 있는다.
- 파이프라인의 이벤트 메시지를 전달 받을 수 있도록 bus를 대기하고 있는다.
- 그리고 bus에서 메시지가 오면 여기 클래스에서 처리할 수 있도록 connect해놓는다.
*기본 Qt에서는 Q_OBJECT를 클래스 헤더에 선언해놓고 connect()를 사용해서 시그널 슬롯 함수를 연결하면 된다.
그러나 여기에서는 QGlib의 connect를 사용해야 gstreamer의 이벤트를 받을 수 있다.
-파이프라인에 미디어의 uri를 등록해준다.
5.Player::play()
MediaApp::openFile() 메서드에서 uri를 등록해준 후, player->play() 메서드를 호출한다.
따라서 play함수를 살펴보자.
void Player::play()
{
if (m_pipeline) {
m_pipeline->setState(QGst::StatePlaying);
}
}
- 파이프라인의 상태값을 StatePlaying으로 바꿔주기만 하면 된다.
* pipeline 클래스는 element 클래스를 상속받아 setState()함수의 사용이 가능하다.
- Player::play() 함수는 두 가지 경우에 호출된다.
(1) 동영상 path를 지정해줄 때. MediaApp::openFile() -> Player::setUri() -> Player::play()
(2) 동영상 플레이어 애플리케이션의 '재생'버튼을 누를 때. playButton 시그널 clicked() -> Player::play()