IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> Qt + Gstreamer 实现视频播放功能 -> 正文阅读

[C++知识库]Qt + Gstreamer 实现视频播放功能

简介

  • Qt 是跨平台C++图形用户界面应用程序开发框架。

  • GStreamer是一个用于构造媒体处理组件图的库。它支持的应用程序范围从简单的Ogg/Vorbis 播放, audio/video 流化到复杂的 audio(混音) 和 video (非线性编辑) 处理。

通过Qt + Gstreamer实现一个简单的本地文件播放为例。

本示例通过设置一个播放列表,随机播放视频列表里的文件,当一个文件播放结束后,继续随机播放下一个文件。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QVector>
#include "gstplayer.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    void init();

    void play();

private slots:
    void slt_onEos();
    void slt_onError(QString errMsg);
private:
    void initPlayList();

private:
    Ui::MainWindow    *ui;
    GstPlayer         *m_player;
    QVector<QString>   m_videos;
    QString            m_curPlay;
    qint32             m_curIdx;
};

#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "gstplayer.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QWidget *wid = centralWidget();

    m_player = new GstPlayer(wid->winId());
    connect(m_player, SIGNAL(sig_eos()), this, SLOT(slt_onEos()));
    connect(m_player, SIGNAL(sig_error(QString)), this, SLOT(slt_onError(QString)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::init()
{
    initPlayList();
}


void MainWindow::slt_onEos()
{
    play();
}

void MainWindow::slt_onError(QString errMsg)
{
    qDebug() << m_curPlay << errMsg;
    m_videos.erase(m_videos.begin() + m_curIdx);
    play();
}

void MainWindow::initPlayList()
{
    m_videos.append("/path/to/video1");
    m_videos.append("/path/to/video2");
    m_videos.append("/path/to/video3");
    m_videos.append("/path/to/video4");
}

void MainWindow::play()
{
    if(m_videos.isEmpty()) return;

    m_curIdx   = random() % m_videos.size();
    m_curPlay   = m_videos[m_curIdx];

    m_player->setStateStop();
    m_player->setPlay(m_curPlay);
}

#ifndef GST_PLAYER_H__
#define GST_PLAYER_H__

#include <QWidget>
#include <QString>
#include <QQueue>
#include <QVector>
#include <gst/gst.h>

class GstPlayer : public QObject
{
    Q_OBJECT
public:
    explicit GstPlayer(WId wid);
    ~GstPlayer();

    static void      gstInit();

signals:
    void sig_ready();
    void sig_pause();
    void sig_play();
    void sig_stop();
    void sig_eos();
    void sig_error(QString errMsg);

public:
    /* @param uri: /path/to/video */
    void setPlay(const QString &uri);
    void setStateReady();
    void setStateStop();
    void setStatePause();
    void setStatePlay();

    void        seek(int secs);
    gint64      getPosition();
    gint64      getDuration();

private:
    static gboolean  busMessageCb(GstBus *bus, GstMessage *msg, gpointer data);
    static gboolean  playIdleCb(gpointer data);
    static gboolean  stopIdleCb(gpointer data);
    static void      errCb(GstBus *bus, GstMessage *msg, GstPlayer *data);
    static void      eosCb(GstBus *bus, GstMessage *msg, GstPlayer *data);
    static void      stateChangeCb(GstBus *bus, GstMessage *msg, GstPlayer *data);
    static void      padAddedCb (GstElement *src, GstPad *newPad,GstPlayer *data);

    gboolean    playPipeline(const  QString &uri);
    void        createPipeline();
    void        freePipeline();
    GstElement *createPlaySink();
    void        releaseElement();

    void      evaluateState(GstState oldstate,GstState targetstate,GstState pendingstate);
    void      playFileInternal( );
    void      changeState(GstState newstate);

private:
    WId                 m_wid;
    GstElement         *m_pipeline;
    GstElement         *m_filesrc;
    GstElement         *m_decode;
    GstElement 	       *m_playSink;
    GstBus             *m_bus;
    QVector<GstPad *>   m_pad_vec;

    GstState            m_curState;
    GstState            m_tarState;

    QString 			m_curFile;

    gboolean            m_busyFlag;
    gboolean            m_seeking;
};

#endif



#include "gstplayer.h"
#include <gst/video/videooverlay.h>
#include <gst/gstinfo.h>
#include <gst/audio/streamvolume.h>


#define GST_PLAYER(point) ((GstPlayer *)(point))
#define RETURN_IF_FAIL(flag) do{ if(true == (flag)) return; }while(0);

GstPlayer::GstPlayer(WId wid)
  : m_wid(wid)
  , m_pipeline(nullptr)
  , m_filesrc(nullptr)
  , m_decode(nullptr)
  , m_playSink(nullptr)
  , m_bus(nullptr)
  , m_curState(GST_STATE_VOID_PENDING)
  , m_tarState(GST_STATE_VOID_PENDING)
  , m_busyFlag(false)
  , m_seeking(false)
{
    createPipeline();
}

GstPlayer::~GstPlayer()
{
    freePipeline();
}

//
void GstPlayer::gstInit()
{
    gst_init (0, 0);
}

void GstPlayer::freePipeline()
{
    if(m_pipeline)
    {
        gst_element_set_state (m_pipeline, GST_STATE_NULL);
        releaseElement();
        gst_object_unref (m_pipeline);
        gst_bus_remove_signal_watch (m_bus);
        gst_object_unref (m_bus);
    }
}

void GstPlayer::createPipeline( )
{
    m_pipeline = gst_pipeline_new (nullptr);
    m_bus = gst_pipeline_get_bus (GST_PIPELINE (m_pipeline));
    gst_bus_add_signal_watch (m_bus);
    g_signal_connect (G_OBJECT (m_bus), "message", G_CALLBACK(busMessageCb), this);
}

gboolean GstPlayer::playIdleCb(gpointer data)
{
    GstPlayer *gst;
    gst = GST_PLAYER(data);

    if (gst->m_curState < GST_STATE_PAUSED )
    {
        gst->playFileInternal( );
    }
    else
    {
        gst->changeState(GST_STATE_NULL);
    }

    return false;
}

gboolean GstPlayer::stopIdleCb(gpointer data)
{
    GstPlayer *gst;
    gst = GST_PLAYER(data);

    gst->changeState(GST_STATE_NULL);
    gst->releaseElement();

    return false;
}


gboolean GstPlayer::busMessageCb(GstBus *bus, GstMessage *msg, gpointer data)
{
    GstPlayer *gst = GST_PLAYER(data);
    switch (GST_MESSAGE_TYPE(msg))
    {
        case GST_MESSAGE_EOS:
        {
            gst->m_busyFlag = false;
            emit gst->sig_eos();
            break;
        }

        case GST_MESSAGE_ERROR:
        {
            GError *error = nullptr;
            gchar *debug;
            gst->m_busyFlag = false;
            gst->m_tarState = GST_STATE_NULL;
            gst->changeState(GST_STATE_NULL);
            gst_message_parse_error(msg, &error, &debug);
            QString errStr = error->message;
            emit gst->sig_error(errStr);
            g_error_free(error);
            g_free(debug);

            break;
        }

        case GST_MESSAGE_STATE_CHANGED:
        {
            GstState oldstate, newstate, pendingstate;
            gst_message_parse_state_changed (msg, &oldstate, &newstate, &pendingstate);
            if (GST_MESSAGE_SRC(msg) == GST_OBJECT(gst->m_pipeline))
            {
                gst->evaluateState(oldstate, newstate, pendingstate);
            }
            break;
        }

        case GST_MESSAGE_ASYNC_DONE:
        {
            if (gst->m_seeking)
            {
                gst->m_seeking = false;
            }
            break;
        }
        default:
            break;
    }
    return true;
}

void GstPlayer::padAddedCb(GstElement *src, GstPad *new_pad,GstPlayer *data)
{
    GstCaps *new_pad_caps        = nullptr;
    GstStructure *new_pad_struct = nullptr;
    GstElementClass *klass       = nullptr;
    GstPadTemplate *templ        = nullptr;
    const gchar *new_pad_type    = nullptr;

    if((new_pad_caps = gst_pad_get_current_caps (new_pad)) == nullptr)
    {
        return;
    }

    if((new_pad_struct = gst_caps_get_structure (new_pad_caps, 0)) == nullptr)
    {
        return;
    }

    if((new_pad_type = gst_structure_get_name (new_pad_struct)) == nullptr)
    {
        return;
    }

    klass = GST_ELEMENT_GET_CLASS(data->m_playSink);

    if (g_str_has_prefix (new_pad_type, "audio/x-raw"))
    {
        templ = gst_element_class_get_pad_template(klass,"audio_sink");
    }
    else if(g_str_has_prefix (new_pad_type, "video/x-raw")) {

        templ = gst_element_class_get_pad_template(klass,"video_sink");
    }
    else if(g_str_has_prefix (new_pad_type, "text"))
    {
        templ = gst_element_class_get_pad_template(klass,"text_sink");
    }

    if(templ)
    {
        GstPad *req_pad  = gst_element_request_pad(data->m_playSink,templ,nullptr,nullptr);

        if(!gst_pad_is_linked(req_pad))
        {
            gst_pad_link(new_pad,req_pad);
            data->m_pad_vec.push_back(req_pad);
        }
    }
    gst_caps_unref (new_pad_caps);
}


void GstPlayer::evaluateState(GstState oldstate,GstState targetstate,GstState pendingstate)
{
    m_curState = targetstate;

    if (m_tarState == m_curState )
    {
        m_busyFlag = false;
    }

    switch (m_curState) {
        case GST_STATE_PLAYING:
        {
             emit sig_play();
             break;
        }
        case GST_STATE_PAUSED:
        {
            emit sig_pause();
            break;
        }
       case GST_STATE_READY:
       {
           m_seeking = false;
           emit sig_ready();
           break;
       }
       case GST_STATE_NULL:
       {
           m_seeking  = false;
           emit sig_stop();
           break;
       }
       default:
           break;
    }
}



/* change gst state */
void GstPlayer::changeState(GstState newstate)
{
    GstStateChangeReturn ret;
    ret = gst_element_set_state (GST_ELEMENT(m_pipeline), newstate);
    switch (ret)
    {
        case GST_STATE_CHANGE_SUCCESS:
            evaluateState(GST_STATE(m_pipeline),
                          GST_STATE_TARGET(m_pipeline),
                          GST_STATE_PENDING(m_pipeline));
            break;
        case GST_STATE_CHANGE_ASYNC:
            break;
        case GST_STATE_CHANGE_FAILURE:
            break;
        case GST_STATE_CHANGE_NO_PREROLL:
            break;
        default:
            break;
    }
}

GstElement *GstPlayer::createPlaySink()
{
    GstElement *play_sink = gst_element_factory_make ("playsink", "playsink");

    if(nullptr == play_sink)
    {
        return nullptr;
    }

    gst_util_set_object_arg (G_OBJECT (play_sink), "flags","text+audio+video+soft-volume");

    GstElement *video_sink = gst_element_factory_make ("xvimagesink", "vsink");

    if(nullptr == video_sink)
    {
        if(nullptr == (video_sink = gst_element_factory_make ("ximagesink", "vsink")))
        {
            g_object_unref(play_sink);
            return nullptr;
        }
    }

    g_object_set(G_OBJECT(play_sink),"video-sink",video_sink, nullptr);
    gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (video_sink), m_wid);

    return play_sink;
}

gboolean GstPlayer::playPipeline(const  QString &uri )
{
    m_filesrc    = gst_element_factory_make ("filesrc", "filesrc");
    m_decode     = gst_element_factory_make ("decodebin", "decode");
    m_playSink   = createPlaySink();

    if(!m_filesrc || !m_decode || !m_playSink)
    {
        if(m_filesrc)  { g_object_unref(m_filesrc) ;  m_filesrc  = nullptr;}
        if(m_decode)   { g_object_unref(m_decode)  ;  m_decode   = nullptr;}
        if(m_playSink) { g_object_unref(m_playSink);  m_playSink = nullptr;}
        return false;
    }

    gst_bin_add_many (GST_BIN (m_pipeline),m_filesrc,m_decode, m_playSink,nullptr);
    g_object_set(G_OBJECT(m_filesrc),"location",uri.toStdString().c_str(), nullptr);

    gboolean linkRet = gst_element_link(m_filesrc,m_decode);
    if (!linkRet)
    {
        gst_bin_remove(GST_BIN(m_pipeline),m_filesrc);
        gst_bin_remove(GST_BIN(m_pipeline),m_decode);
        gst_bin_remove(GST_BIN(m_pipeline),m_playSink);
        m_filesrc  = nullptr;
        m_decode   = nullptr;
        m_playSink = nullptr;
        return false;
    }
    g_signal_connect (m_decode, "pad-added", G_CALLBACK (padAddedCb), this);

    return true;
}

void GstPlayer::playFileInternal( )
{
    if (G_UNLIKELY(GST_STATE(m_pipeline) == GST_STATE_PLAYING))
    {
        //
    }

    bool ret = playPipeline(m_curFile);

    if(ret)
    {
        changeState(GST_STATE_PLAYING);
    }
    else
    {
        changeState(GST_STATE_NULL);
    }
}

void GstPlayer::setPlay(const QString &uri)
{

    RETURN_IF_FAIL(m_busyFlag)

    if(uri.isEmpty())
    {
        return;
    }

    m_curFile = uri;

    m_tarState = GST_STATE_PLAYING;

    g_idle_add((GSourceFunc) playIdleCb, this);

    return;
}


void GstPlayer::setStateReady()
{
    RETURN_IF_FAIL(m_busyFlag)

    m_tarState = GST_STATE_READY;

    changeState(GST_STATE_READY);
}

void GstPlayer::setStatePause()
{
    RETURN_IF_FAIL(m_busyFlag)

    m_tarState = GST_STATE_PAUSED;
    m_busyFlag = true;
    changeState(GST_STATE_PAUSED);
}

void GstPlayer::setStateStop()
{
    RETURN_IF_FAIL(m_busyFlag)

    m_tarState = GST_STATE_NULL;
    g_idle_add((GSourceFunc) stopIdleCb, this);
}

void GstPlayer::setStatePlay()
{
    RETURN_IF_FAIL(m_busyFlag)
    m_tarState = GST_STATE_PLAYING;
    changeState(GST_STATE_PLAYING);
}


gint64  GstPlayer::getDuration()
{
    gint64    absolute_duration = 0;
    gint64    duration = 0;
    GstFormat gst_time = GST_FORMAT_TIME;

    gst_element_query_duration(m_pipeline, gst_time, &absolute_duration);

    if (absolute_duration < 0)
    {
        absolute_duration = 0;
    }

    if (gst_time == GST_FORMAT_TIME)
    {
        duration =  absolute_duration / GST_SECOND;
    }
    return duration;
}

gint64 GstPlayer::getPosition( )
{
    GstFormat format = GST_FORMAT_TIME;
    gint64    pos;
    gst_element_query_position(m_pipeline, format, &pos);
    return  pos / GST_SECOND;
}

void GstPlayer::seek(int secs)
{
    gint64 seekPos = secs * GST_SECOND;
    if (!gst_element_seek_simple( m_pipeline, GST_FORMAT_TIME,(GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),seekPos))
    {
        return;
    }
    m_seeking = true;
}


void  GstPlayer::releaseElement()
{
    if(m_playSink && !m_pad_vec.empty())
    {
        for(int i =0; i < m_pad_vec.size(); ++i)
        {
            gst_element_release_request_pad(m_playSink,m_pad_vec[i]);
            gst_object_unref(m_pad_vec[i]);
        }
        m_pad_vec.clear();
    }

    if(m_playSink)
    {
        gst_element_set_state (GST_ELEMENT(m_playSink), GST_STATE_NULL);
        gst_bin_remove(GST_BIN(m_pipeline),m_playSink);
        m_playSink  = nullptr;
    }

    if(m_filesrc)
    {
        gst_element_set_state (GST_ELEMENT(m_filesrc), GST_STATE_NULL);
        gst_bin_remove(GST_BIN(m_pipeline),m_filesrc);
        m_filesrc   = nullptr;
    }

    if(m_decode)
    {
        gst_element_set_state (GST_ELEMENT(m_decode), GST_STATE_NULL);
        gst_bin_remove(GST_BIN(m_pipeline),m_decode);
        m_decode    = nullptr;
    }
}


  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-25 16:12:38  更:2021-07-25 16:12:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/4 5:32:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码