GStreamer开发简单MP4播放器(二)

基于上一篇GStreamer开发简单MP4播放器(一)的基础上,在官网上找到了GStreamer和QT联合使用的案例,于是copy了部分代码,调通了暂停,seek功能,本来向实现个音量调节,但是发现案例都是基于playbin的,暂时没找到解决方案,先把部分代码贴上来。

1 暂停和停止播放功能

void PlayerWindow::onPauseClicked() {
    gst_element_set_state (pipeline, GST_STATE_PAUSED);
}

void PlayerWindow::onStopClicked() {
    gst_element_set_state (pipeline, GST_STATE_NULL);
}

2 seek功能实现

void PlayerWindow::refreshSlider() {
    gint64 current = GST_CLOCK_TIME_NONE;
    if (state == GST_STATE_PLAYING) {
        if (!GST_CLOCK_TIME_IS_VALID(totalDuration)) {
            if (gst_element_query_duration (pipeline, GST_FORMAT_TIME, &totalDuration)) {
                slider->setRange(0, totalDuration/GST_SECOND);
            }
        }
        if (gst_element_query_position (pipeline, GST_FORMAT_TIME, &current)) {
            g_print("%ld / %ld\n", current/GST_SECOND, totalDuration/GST_SECOND);
            slider->setValue(current/GST_SECOND);
        }
    }
}
void PlayerWindow::onSeek() {
    gint64 pos = slider->sliderPosition();
    g_print("seek: %ld\n", pos);
    gst_element_seek_simple (pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH ,
                  pos * GST_SECOND);
}

3 最终效果

在这里插入图片描述

4 完整代码

main.cpp

#include <glib.h>
#include <gst/gst.h>
#include <gst/video/videooverlay.h>

#include "qtoverlay.h"
//#include "mainwindow.h"
#include <QApplication>
#include <QWidget>
#include <QSlider>
#include <QLabel>
#include <QObject>

#define MP4_SOURCE "./movie1.mp4"

typedef struct _ST_CUSTOMDATA {
    GstElement  *gstpPipeline;
    GstElement  *mediaSource;
    GstElement  *mediaDemuxer;
    GstElement  *audioQueue;
    GstElement  *videoQueue;
    GstElement  *audioDecoder;
    GstElement  *videoDecoder;
    GstElement  *audioConvert;
    GstElement  *videoConvert;
    GstElement  *audioSink;
    GstElement  *videoSink;

    GstElement *mediaVolume;
    QFrame *movieFrame;
    QSlider *timeSlider;
    QSlider *volumeSlider;
    QLabel  *curTimeLabel;
    QLabel  *durTimeLabel;

    GstState pipelineState;                 /* Current state of the pipeline */
    gint64 mediaDuration;              /* Duration of the clip, in nanoseconds */

} ST_CUSTOMDATA ;

ST_CUSTOMDATA customData;

static gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data)
{

  GMainLoop *loop = (GMainLoop *) data;

  switch (GST_MESSAGE_TYPE (msg)) {

    case GST_MESSAGE_EOS:
      g_print ("End of stream\n");
      g_main_loop_quit (loop);
      break;

    case GST_MESSAGE_ERROR: {
      gchar  *debug;
      GError *error;

      gst_message_parse_error (msg, &error, &debug);
      g_free (debug);

      g_printerr ("Error: %s\n", error->message);
      g_error_free (error);

      g_main_loop_quit (loop);
      break;
    }
    default:
      break;
  }
  return TRUE;
}



static void on_pad_added (GstElement *element,GstPad *pad, ST_CUSTOMDATA *data)
{
    gchar *pad_name = NULL;
    g_object_get(pad, "name", &pad_name, NULL);

    if(g_str_has_prefix(pad_name, "video"))
    {
        GstPad *v_sinkpad = NULL;

        v_sinkpad = gst_element_get_static_pad (data->videoQueue, "sink");
        gst_pad_link (pad, v_sinkpad);
        gst_object_unref (v_sinkpad);
    }
    if(g_str_has_prefix(pad_name, "audio"))
    {
        GstPad *a_sinkpad;
        a_sinkpad = gst_element_get_static_pad (data->audioQueue, "sink");
        if(!gst_pad_link (pad, a_sinkpad))
            g_print("%s linked to adecoder.\n", pad_name);
        else
            g_print("ERROR: gst_pad_link (pad, a_sinkpad), pad_name = %s\n",pad_name);
        gst_object_unref (a_sinkpad);
    }

}

int main(int argc, char *argv[])
{

     GMainLoop *loop;

     GstBus *bus;
     guint bus_watch_id;

     // Initialisation
     gst_init (&argc, &argv);
     loop = g_main_loop_new(NULL, FALSE);

     QApplication app(argc, argv);
     app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit ()));

     customData.gstpPipeline = gst_pipeline_new("mp4-player");
     customData.mediaSource = gst_element_factory_make("filesrc", "file-source");
     customData.mediaDemuxer = gst_element_factory_make("qtdemux", "qt-demuxer");
     // create video elements
     customData.videoQueue = gst_element_factory_make ("queue", "video-queue");
     customData.videoDecoder = gst_element_factory_make("avdec_h264", "h264-decoder");
     customData.videoConvert = gst_element_factory_make ("videoconvert", "video-converter");
     customData.videoSink = gst_element_factory_make ("ximagesink", "video-output");

     // create audio elements
     customData.audioQueue = gst_element_factory_make ("queue", "audio-queue");
     customData.audioDecoder = gst_element_factory_make("faad", "au-decoder");
     customData.audioConvert = gst_element_factory_make("audioconvert", "au-converter");
     customData.audioSink = gst_element_factory_make("autoaudiosink", "au-output");
     customData.mediaVolume = gst_element_factory_make("volume", NULL);
     double m_volume = 0.1;
     g_object_set(G_OBJECT(customData.mediaVolume), "volume", m_volume, NULL);
     if (!customData.gstpPipeline || !customData.videoQueue ||
             !customData.mediaSource || !customData.mediaDemuxer ||
             !customData.videoQueue || !customData.videoDecoder ||
             !customData.videoConvert || !customData.videoSink ||
             !customData.audioQueue || !customData.audioDecoder ||
             !customData.audioConvert || !customData.audioSink ||
             !customData.mediaVolume)
     {
        g_printerr ("One element could not be created. Exiting.\n");
        return -1;
     }

     PlayerWindow *window = new PlayerWindow(customData.gstpPipeline);
     //window.setWindowTitle("Qt&GStreamer Simple Player");
     window->resize(900, 600);
     window->show();

     WId xwinid = window->getVideoWId();
     gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (customData.videoSink), xwinid);
     g_object_set(GST_OBJECT(customData.gstpPipeline), "video-sink", customData.videoSink, NULL);

     // set the input filename to the source element
     g_object_set (G_OBJECT(customData.mediaSource), "location", MP4_SOURCE, NULL);

     //add a message handler
     bus = gst_pipeline_get_bus (GST_PIPELINE (customData.gstpPipeline));
     bus_watch_id = gst_bus_add_watch (bus, bus_call, loop);
     gst_object_unref (bus);

     // add all elements into the pipeline
     gst_bin_add_many (GST_BIN (customData.gstpPipeline),
                       customData.mediaSource, customData.mediaDemuxer,
                       customData.videoQueue, customData.videoDecoder,
                       customData.videoConvert, customData.videoSink,
                       customData.audioQueue, customData.audioDecoder,
                       customData.audioConvert, customData.audioSink,
                       customData.mediaVolume,
                       NULL);


     // link the elements together

     gst_element_link(customData.mediaSource, customData.mediaDemuxer);
     gst_element_link_many(customData.videoQueue, customData.videoDecoder,
                            customData.videoConvert, customData.videoSink, NULL);
     gst_element_link_many(customData.audioQueue, customData.audioDecoder,
                           customData.audioConvert,customData.audioSink,
                           customData.mediaVolume);

     g_signal_connect (customData.mediaDemuxer, "pad-added", G_CALLBACK (on_pad_added), &customData);

     gst_bus_add_watch(bus, &PlayerWindow::postGstMessage, window);
     /* note that the demuxer will be linked to the decoder dynamically.
        The reason is that Ogg may contain various streams (for example
        audio and video). The source pad(s) will be created at run time,
        by the demuxer when it detects the amount and nature of streams.
        Therefore we connect a callback function which will be executed
        when the "pad-added" is emitted.*/

     // Set the pipeline to "playing" state
     gst_element_set_state (customData.gstpPipeline, GST_STATE_PLAYING);


     // Iterate
     g_print ("Running...\n");
     g_main_loop_run (loop);


     // Out of the main loop, clean up nicely
     g_print ("Returned, stopping playback\n");
     gst_element_set_state (customData.gstpPipeline, GST_STATE_NULL);

     int ret = app.exec();

     window->hide();
     g_print ("Deleting pipeline\n");
     gst_object_unref (GST_OBJECT (customData.gstpPipeline));
     g_source_remove (bus_watch_id);
     g_main_loop_unref (loop);

     return ret;
}


qtoverlay.h

#ifndef _QTOVERLAY_
#define _QTOVERLAY_

#include <gst/gst.h>

#include <QWidget>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSlider>
#include <QTimer>

class PlayerWindow : public QWidget
{
    Q_OBJECT
public:
  PlayerWindow(GstElement *p);

  WId getVideoWId() const ;
  static gboolean postGstMessage(GstBus * bus, GstMessage * message, gpointer user_data);

private slots:
  void onPlayClicked() ;
  void onPauseClicked() ;
  void onStopClicked() ;
  void onAlbumAvaiable(const QString &album);
  void onState(GstState st);
  void refreshSlider();
  void onSeek();
  void onEos();
  void setVolume();

signals:
  void sigAlbum(const QString &album);
  void sigState(GstState st);
  void sigEos();

private:
  GstElement *pipeline;
  QPushButton *playBt;
  QPushButton *pauseBt;
  QPushButton *stopBt;
  QWidget *videoWindow;
  QSlider *slider;
  QHBoxLayout *buttonLayout;
  QVBoxLayout *playerLayout;
  QTimer *timer;
  QSlider *volumeSlider;

  GstState state;
  gint64 totalDuration;
};

#endif

qtoverlay.cpp

#include <gst/video/videooverlay.h>
#include <QApplication>
#include "qtoverlay.h"

PlayerWindow::PlayerWindow(GstElement *p)
    :pipeline(p)
    ,state(GST_STATE_NULL)
    ,totalDuration(GST_CLOCK_TIME_NONE)
{
    playBt = new QPushButton("Play");
    pauseBt = new QPushButton("Pause");
    stopBt = new QPushButton("Stop");
    videoWindow = new QWidget();
    slider = new QSlider(Qt::Horizontal);
    timer = new QTimer();
    volumeSlider = new QSlider(Qt::Horizontal);

    connect(playBt, SIGNAL(clicked()), this, SLOT(onPlayClicked()));
    connect(pauseBt, SIGNAL(clicked()), this, SLOT(onPauseClicked()));
    connect(stopBt, SIGNAL(clicked()), this, SLOT(onStopClicked()));
    connect(slider, SIGNAL(sliderReleased()), this, SLOT(onSeek()));
    connect(volumeSlider, SIGNAL(sliderReleased()), this, SLOT(setVolume()));

    buttonLayout = new QHBoxLayout;
    buttonLayout->addWidget(playBt);
    buttonLayout->addWidget(pauseBt);
    buttonLayout->addWidget(stopBt);
    buttonLayout->addWidget(slider);
    buttonLayout->addWidget(volumeSlider);

    playerLayout = new QVBoxLayout;
    playerLayout->addWidget(videoWindow);
    playerLayout->addLayout(buttonLayout);

    this->setLayout(playerLayout);

    connect(timer, SIGNAL(timeout()), this, SLOT(refreshSlider()));
    connect(this, SIGNAL(sigAlbum(QString)), this, SLOT(onAlbumAvaiable(QString)));
    connect(this, SIGNAL(sigState(GstState)), this, SLOT(onState(GstState)));
    connect(this, SIGNAL(sigEos()), this, SLOT(onEos()));
}

WId PlayerWindow::getVideoWId() const {
    return videoWindow->winId();
}
void PlayerWindow::setVolume()
{
    double numVolume = volumeSlider->sliderPosition()/10;
    if (numVolume < 0)
    {
        numVolume = 0;
    }
    else if (numVolume > 4.0)
    {
        numVolume = 4.0;
    }
    g_print("volume: %lf\n", numVolume);
    g_object_set(pipeline, "volume", numVolume, NULL);


}

void PlayerWindow::onPlayClicked() {
    GstState st = GST_STATE_NULL;
    gst_element_get_state (pipeline, &st, NULL, GST_CLOCK_TIME_NONE);
    if (st < GST_STATE_PAUSED) {
        // Pipeline stopped, we need set overlay again
        //GstElement *vsink = gst_element_factory_make ("ximagesink", "vsink");
        //g_object_set(GST_OBJECT(pipeline), "video-sink", vsink, NULL);
        //WId xwinid = getVideoWId();
        //gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), xwinid);
    }
    gst_element_set_state (pipeline, GST_STATE_PLAYING);
}

void PlayerWindow::onPauseClicked() {
    gst_element_set_state (pipeline, GST_STATE_PAUSED);
}

void PlayerWindow::onStopClicked() {
    gst_element_set_state (pipeline, GST_STATE_NULL);
}

void PlayerWindow::onAlbumAvaiable(const QString &album) {
    setWindowTitle(album);
}

void PlayerWindow::onState(GstState st) {
    if (state != st) {
        state = st;
        if (state == GST_STATE_PLAYING){
            timer->start(1000);
        }
        if (state < GST_STATE_PAUSED){
            timer->stop();
        }
    }
}

void PlayerWindow::refreshSlider() {
    gint64 current = GST_CLOCK_TIME_NONE;
    if (state == GST_STATE_PLAYING) {
        if (!GST_CLOCK_TIME_IS_VALID(totalDuration)) {
            if (gst_element_query_duration (pipeline, GST_FORMAT_TIME, &totalDuration)) {
                slider->setRange(0, totalDuration/GST_SECOND);
            }
        }
        if (gst_element_query_position (pipeline, GST_FORMAT_TIME, &current)) {
            g_print("%ld / %ld\n", current/GST_SECOND, totalDuration/GST_SECOND);
            slider->setValue(current/GST_SECOND);
        }
    }
}

void PlayerWindow::onSeek() {
    gint64 pos = slider->sliderPosition();
    g_print("seek: %ld\n", pos);
    gst_element_seek_simple (pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH ,
                  pos * GST_SECOND);
}

void PlayerWindow::onEos() {
    gst_element_set_state (pipeline, GST_STATE_NULL);
}

gboolean PlayerWindow::postGstMessage(GstBus * bus, GstMessage * message, gpointer user_data) {
    PlayerWindow *pw = NULL;
    if (user_data) {
        pw = reinterpret_cast<PlayerWindow*>(user_data);
    }
    switch (GST_MESSAGE_TYPE(message)) {
        case GST_MESSAGE_STATE_CHANGED: {
            GstState old_state, new_state, pending_state;
            gst_message_parse_state_changed (message, &old_state, &new_state, &pending_state);
            pw->sigState(new_state);
            break;
        }
        case GST_MESSAGE_TAG: {
            GstTagList *tags = NULL;
            gst_message_parse_tag(message, &tags);
            gchar *album= NULL;
            if (gst_tag_list_get_string(tags, GST_TAG_ALBUM, &album)) {
                pw->sigAlbum(album);
                g_free(album);
            }
            gst_tag_list_unref(tags);
            break;
        }
        case GST_MESSAGE_EOS: {
            pw->sigEos();
            break;
        }
        default:
            break;
    }
    return TRUE;
}


评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值