VLC依赖库准备
vlc库下载地址:
官网地址:https://download.videolan.org/pub/videolan/vlc/
解压后如下:
工程准备
1.将vlc库sdk文件夹中的include和lib文件夹拷入工程目录中,根据自己习惯放置,如:
2.工程中添加依赖库路径和头文件路径:
INCLUDEPATH += $$PWD/include/vlc \
$$PWD/include/vlc/plugins
LIBS += -L$$PWD/lib -llibvlc -llibvlccore
DESTDIR = $$PWD/bin
3.代码准备
a.操作界面如下:
上述界面中的推流参数表示转码参数,如:
#transcode{vcodec=h264,acodec=mpga,ab=128,channels=2,samplerate=44100,scodec=none}
vlc推流参数表示用vlc客户端生成的完整参数,包括推流方式,如:
#transcode{vcodec=h264,acodec=mpga,ab=128,channels=2,samplerate=44100,scodec=none}:rtp{sdp=rtsp://127.0.0.1:10086/live}
b.h文件代码
#ifndef VLCSTREAMER_H
#define VLCSTREAMER_H
#include <QObject>
#include <QIODevice>
#include <QProcess>
#include <QUrl>
#include <QTimer>
#include <QFuture>
#include <QSemaphore>
struct libvlc_instance_t;
struct libvlc_media_t;
struct libvlc_media_player_t;
class VLCStreamer : public QObject
{
Q_OBJECT
public:
explicit VLCStreamer(QObject *parent = nullptr);
~VLCStreamer();
/**
* @brief startStreaming 开始推流
* @param strFile 本地推流媒体文件
* @param strIP 推流IP
* @param nPort 推流端口
* @param nStreamType 推流类型
* @param strParams 转码参数
* @return true推流成功,false推流失败
*/
bool startStreaming(const QString &strFile, const QString &strIP,const int& nPort,const int& nStreamType,const QString& strParams = "");
/**
* @brief startStreaming 开始推流
* @param strFile 本地推流媒体文件
* @param strParams vlc客户端生成的推流指令 #transcode{vcodec=h264,acodec=mpga,ab=128,channels=2,samplerate=44100,scodec=none}:rtp{sdp=rtsp://127.0.0.1:10086/live}
* @return true推流成功,false推流失败
*/
bool startStreaming(const QString &strFile,const QString& strParams);
/**
* @brief stopStreaming 停止推流
* @return true停止成功,false停止失败
*/
bool stopStreaming();
signals:
void streamingFinished();//无用
private:
/**
* @brief convertOupputUrl 转换为推流指令
* @param nStreamType 推流方式
* @param strIP 推流IP
* @param nPort 推流端口
* @return 推流指令
*/
QString convertOupputUrl(const int& nStreamType,const QString &strIP,const int& nPort);
private:
void onCheckThread();//无用
private:
libvlc_instance_t *m_pVlcInstance = nullptr;
libvlc_media_t *m_pMedia = nullptr; //无用
libvlc_media_player_t *m_pMediaPlayer = nullptr; //无用
bool m_bHasTask = false;
QFuture<void> m_future; //无用
QSemaphore m_semaphore_timer; //无用
int m_nMediaLength = 0; //无用
};
#endif // VLCSTREAMER_H
c.cpp文件代码
#include "vlcStreamer.h"
// VLC头文件
#include "vlc/vlc.h"
#include "GlobalDefine.h"
#include <QDir>
#include <QDebug>
#include <QtConcurrent>
#define VIDEO_MEDIA_NAME "Video" //todo 这个名字重复有影响吗?比如这个类实例化两次
VLCStreamer::VLCStreamer(QObject *parent) : QObject(parent)
{
//创建vlc实例
m_pVlcInstance = libvlc_new (0, nullptr);
// m_pMediaPlayer = libvlc_media_player_new(m_pVlcInstance);
}
VLCStreamer::~VLCStreamer()
{
// 停止推流
libvlc_vlm_stop_media(m_pVlcInstance, VIDEO_MEDIA_NAME);
// libvlc_media_player_stop(m_pMediaPlayer);
//释放播放器
// libvlc_media_player_release(m_pMediaPlayer);
//释放媒体
// if(m_pMedia)
// {
// libvlc_media_release(m_pMedia);
// }
// 释放VLC实例
libvlc_vlm_release(m_pVlcInstance);
m_pVlcInstance = nullptr;
// libvlc_media_player_release(m_pMediaPlayer);
// libvlc_media_release(m_pMedia);
if(m_future.isRunning())
{
m_semaphore_timer.release();
m_future.waitForFinished();
}
}
bool VLCStreamer::startStreaming(const QString &strFile, const QString &strIP, const int &nPort, const int &nStreamType, const QString &strParams)
{
if(m_bHasTask)
{
return false;
}
// 转码参数:strParams
// 网络参数:rtp{sdp=rtsp://xx.xx.xx.xx:yyyy/}
// 表示本机ip时,可省略ip,只写端口,如rtp{sdp=rtsp://:8554/}
QString strNetPara = convertOupputUrl(nStreamType,strIP,nPort);
//如果有转码参数则完整的rtsp如下
// 如sout = "#transcode{vcodec=h264,acodec=mpga,ab=128,channels=2,samplerate=44100,scodec=none}:rtp{sdp=rtsp://127.0.0.1:8554/}"
QString strsout = "";
if(strParams.isEmpty())
{
strsout = QString("#%1").arg(strNetPara);
}
else
{
//strParams 格式为:transcode{vcodec=h264,acodec=mpga,ab=128,channels=2,samplerate=44100,scodec=none}
strsout = QString("#%1:%2").arg(strParams).arg(strNetPara);
}
// 将推流视频路径转换为本地系统风格,win下"a\\b\\c",linux下"a/b/c"
QString path = QDir::toNativeSeparators(strFile);
// 添加名为VIDEO_MEDIA_NAME的广播
int ret = libvlc_vlm_add_broadcast(m_pVlcInstance,
VIDEO_MEDIA_NAME,
path.toStdString().c_str(),
strsout.toStdString().c_str(),
0,
nullptr,
true,
false);//是否循环播放
if (ret != 0)
{
return false;
}
#if 0
m_pMedia = libvlc_media_new_path(m_pVlcInstance,path.toStdString().c_str());
libvlc_media_player_set_media(m_pMediaPlayer,m_pMedia);
if(libvlc_media_player_play(m_pMediaPlayer))
{
return false;
}
#endif
// 播放该广播
ret = libvlc_vlm_play_media(m_pVlcInstance, VIDEO_MEDIA_NAME);
if(ret == 0)
{
m_bHasTask = true;
// if(!m_future.isRunning())
// {
// m_future = QtConcurrent::run(this,&VLCStreamer::onCheckThread);
// }
}
// m_nMediaLength = libvlc_vlm_get_media_instance_length(m_pVlcInstance,VIDEO_MEDIA_NAME,nInstance);//todo 组后一个参数的id怎么获取的?
qDebug()<<libvlc_vlm_show_media( m_pVlcInstance,
VIDEO_MEDIA_NAME );//这里打印了流信息,后续如果要判断推流结束可能需要解析才行,具体信息看最后注释内容
return m_bHasTask;
}
bool VLCStreamer::startStreaming(const QString &strFile, const QString &strParams)
{
if(m_bHasTask)
{
return false;
}
// 将推流视频路径转换为本地系统风格,win下"a\\b\\c",linux下"a/b/c"
QString path = QDir::toNativeSeparators(strFile);
// 添加名为VIDEO_MEDIA_NAME的广播
int ret = libvlc_vlm_add_broadcast(m_pVlcInstance,
VIDEO_MEDIA_NAME,
path.toStdString().c_str(),
strParams.toStdString().c_str(),
0,
nullptr,
true,
false);//是否循环播放
if (ret != 0)
{
return false;
}
// 播放该广播
ret = libvlc_vlm_play_media(m_pVlcInstance, VIDEO_MEDIA_NAME);
if(ret == 0)
{
m_bHasTask = true;
// if(!m_future.isRunning())
// {
// m_future = QtConcurrent::run(this,&VLCStreamer::onCheckThread);
// }
}
// m_nMediaLength = libvlc_vlm_get_media_instance_length(m_pVlcInstance,VIDEO_MEDIA_NAME,0);//todo 组后一个参数的id怎么获取的?
return m_bHasTask;
}
//当前程序缺陷,需要手动停止,哪怕是推流结束
bool VLCStreamer::stopStreaming()
{
if(!m_bHasTask)
{
return false;
}
#if 1
// 停止推流
int ret = libvlc_vlm_stop_media(m_pVlcInstance, VIDEO_MEDIA_NAME);
if(ret == 0)
{
m_bHasTask = false;
m_semaphore_timer.release();
m_future.waitForFinished();
emit streamingFinished();
}
else
{
return false;
}
#else
libvlc_media_player_stop(m_pMediaPlayer);
m_bHasTask = true;
m_semaphore_timer.release();
m_future.waitForFinished();
emit streamingFinished();
#endif
return !m_bHasTask;
}
QString VLCStreamer::convertOupputUrl(const int &nStreamType, const QString &strIP, const int &nPort)
{
QString strOutputUrl = "";
switch (nStreamType)
{
case E_STREAM_TYPE_RTSP:
strOutputUrl = QString("rtp{sdp=rtsp://%1:%2/}").arg(strIP).arg(nPort);
break;
case E_STREAM_TYPE_RTP_TS:
strOutputUrl = QString("rtp{dst=%1,port=%2,mux=ts}").arg(strIP).arg(nPort);
break;
case E_STREAM_TYPE_RTP_AVP:
strOutputUrl = QString("rtp{dst=%1,port=%2}").arg(strIP).arg(nPort);
break;
case E_STREAM_TYPE_UDP:
strOutputUrl = QString("udp{dst=%1:%2}").arg(strIP).arg(nPort); //todo 客户端不能播放,待后续排查
break;
case E_STREAM_TYPE_HTTP:
strOutputUrl = QString("http{mux=ffmpeg{mux=flv},dst=%1:%2/}").arg(strIP).arg(nPort);
break;
default:
break;
}
return strOutputUrl;
}
void VLCStreamer::onCheckThread()
{
int nTime = 0;
while(m_bHasTask)
{
if(m_semaphore_timer.tryAcquire(1,20))
{
break;
}
else
{
// nTime = libvlc_vlm_get_media_instance_time(m_pVlcInstance,VIDEO_MEDIA_NAME,0);
// if(nTime >= m_nMediaLength)
// {
// m_bHasTask = false;
// emit streamingFinished();
// break;
// }
qDebug()<<libvlc_vlm_show_media( m_pVlcInstance,
VIDEO_MEDIA_NAME );
}
}
}
/**
*
*
* {
"name": "Video",
"type": "broadcast",
"enabled": "yes",
"loop": "no",
"inputs": [
"E:\little.mp4"
],
"output": "#rtp{sdp=rtsp://127.0.0.1:10086/}",
"options": null,
"instances": {
"instance": {
"name": "default",
"state": "playing",
"position": "0.007775",
"time": "1657000",
"length": "213120000",
"rate": "1.000000",
"title": "0",
"chapter": "0",
"can-seek": "1",
"playlistindex": "1"
}
}
}
{
"name": "Video",
"type": "broadcast",
"enabled": "yes",
"loop": "no",
"inputs": [
"E:\little.mp4"
],
"output": "#rtp{sdp=rtsp://127.0.0.1:10086/}",
"options": null,
"instances": {
"instance": {
"name": "default",
"state": "playing",
"position": "0.007775",
"time": "1657000",
"length": "213120000",
"rate": "1.000000",
"title": "0",
"chapter": "0",
"can-seek": "1",
"playlistindex": "1"
}
}
}
*
*
*
*
*
*
*
*
*
* */
d.代码注意事项
加入vlc头文件和函数时,编译会报错,如下:
解决办法为:
在vlc.h中加入typedef __int64 ssize_t;
另需要注意,可执行程序路径下需要将vlc解压文件夹中的plugins文件放入可执行目录同级,否则会出错,同时记得把依赖的dll拷贝到该目录。
最终情况上图。
测试结果
5种方式测试情况如下:
rtsp:
推流:#rtp{sdp=rtsp://127.0.0.1:8554/}
拉流:rtsp://127.0.0.1:10086/
rtp:
推流:#rtp{dst=127.0.0.1,port=5004,mux=ts}
拉流:rtp://@127.0.0.1:10086
推流:#rtp{dst=127.0.0.1,port=5004}
拉流:rtp://@127.0.0.1:10086 报错,原因未知
rtp有两种方式,并且在vlc客户端推流时,会填一个流名称,此代码默认为空,不填
udp:
推流:#udp{dst=127.0.0.1:1234}
拉流:udp://@127.0.0.1:10086/
http:
推流:#http{mux=ffmpeg{mux=flv},dst=:8080/}
拉流:http://127.0.0.1:10086/
基本都可以通过,注意拉流地址最后的 '/'有的时候如果不加会出错,vlc客户端播放失败。
rtsp测试截图。
参考资料
参考地址https://blog.csdn.net/zyhse/article/details/113760441
修改完善【20241001】
1.添加状态推流状态逻辑
完善前面代码不能知道停止状态的问题,虽然不完善,好歹有个状态了。不能停止的原因是,vlc里面没有相关函数,只有json数据表示状态。
vlc头文件截图
2.推流后,再次推流会异常,解决此bug
推流结束后,应该移除此推流实例,否则不能再次用同样的实例和名字进行推流。
3.优化后代码如下,需要的自行复制,界面逻辑未做修改,只需修改如下cpp和h文件即可
h文件代码
#ifndef VLCSTREAMER_H
#define VLCSTREAMER_H
#include <QObject>
#include <QIODevice>
#include <QProcess>
#include <QUrl>
#include <QTimer>
#include <QFuture>
#include <QSemaphore>
struct libvlc_instance_t;
struct libvlc_media_t;
struct libvlc_media_player_t;
enum E_STOP_TYPE
{
E_STOP_TYPE_UNKNOWNED = 0, //初始化状态,未知
E_STOP_TYPE_NORMAL = 1, //正常推流结束停止
E_STOP_TYPE_FORCED , //强制停止,stopStreaming()
E_STOP_TYPE_ERROR //媒体异常结束停止
};
class VLCStreamer : public QObject
{
Q_OBJECT
public:
explicit VLCStreamer(QObject *parent = nullptr);
~VLCStreamer();
/**
* @brief startStreaming 开始推流
* @param strFile 本地推流媒体文件
* @param strIP 推流IP
* @param nPort 推流端口
* @param nStreamType 推流类型
* @param strParams 转码参数
* @return true推流成功,false推流失败
*/
bool startStreaming(const QString &strFile, const QString &strIP,const int& nPort,const int& nStreamType,const QString& strParams = "");
/**
* @brief startStreaming 开始推流
* @param strFile 本地推流媒体文件
* @param strParams vlc客户端生成的推流指令 #transcode{vcodec=h264,acodec=mpga,ab=128,channels=2,samplerate=44100,scodec=none}:rtp{sdp=rtsp://127.0.0.1:10086/live}
* @return true推流成功,false推流失败
*/
bool startStreaming(const QString &strFile,const QString& strParams);
/**
* @brief stopStreaming 停止推流
* @return true停止成功,false停止失败
*/
bool stopStreaming();
signals:
void streamingFinished(const int& nStopType);//无用
private:
/**
* @brief convertOupputUrl 转换为推流指令
* @param nStreamType 推流方式
* @param strIP 推流IP
* @param nPort 推流端口
* @return 推流指令
*/
QString convertOupputUrl(const int& nStreamType,const QString &strIP,const int& nPort);
/**
* @brief parseJsonData 解析json数据
* @param jsonArray json 数据
* @param nVideoTotalTime 视频总长度
* @param nCurTime 当前时间
* @param IsNull 判断instances是否为空,为空表示播放结束
* @return true 解析成功,false 解析失败。
*/
bool parseJsonData(const QByteArray& jsonArray,unsigned long long& nVideoTotalTime,unsigned long long& nCurTime,bool& IsNull);
private:
void onCheckThread();//无用
private:
libvlc_instance_t *m_pVlcInstance = nullptr; //vlc实例
int m_nMediaStopType = 0; //推流结束方式
bool m_bHasTask = false; //当前是否正在推流
QFuture<void> m_future; //检测推流状态的线程
QSemaphore m_semaphore_timer; //检查周期定时器信号量
libvlc_media_t *m_pMedia = nullptr; //无用
libvlc_media_player_t *m_pMediaPlayer = nullptr; //无用
};
#endif // VLCSTREAMER_H
cpp文件代码
#include "vlcStreamer.h"
// VLC头文件
#include "vlc/vlc.h"
#include "GlobalDefine.h"
#include <QDir>
#include <QDebug>
#include <QtConcurrent>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#define VIDEO_MEDIA_NAME "Video" //todo 这个名字重复有影响吗?比如这个类实例化两次
#define ERROR_TIME 2,000,000 //结束时间和总时间大于2秒,判断为异常结束
VLCStreamer::VLCStreamer(QObject *parent) : QObject(parent)
{
//创建vlc实例
m_pVlcInstance = libvlc_new (0, nullptr);
// m_pMediaPlayer = libvlc_media_player_new(m_pVlcInstance);
}
VLCStreamer::~VLCStreamer()
{
// 停止推流
libvlc_vlm_stop_media(m_pVlcInstance, VIDEO_MEDIA_NAME);
// libvlc_media_player_stop(m_pMediaPlayer);
//释放播放器
// libvlc_media_player_release(m_pMediaPlayer);
//释放媒体
// if(m_pMedia)
// {
// libvlc_media_release(m_pMedia);
// }
// 释放VLC实例
libvlc_vlm_release(m_pVlcInstance);
m_pVlcInstance = nullptr;
// libvlc_media_player_release(m_pMediaPlayer);
// libvlc_media_release(m_pMedia);
if(m_future.isRunning())
{
m_semaphore_timer.release();
m_future.waitForFinished();
}
}
bool VLCStreamer::startStreaming(const QString &strFile, const QString &strIP, const int &nPort, const int &nStreamType, const QString &strParams)
{
QFileInfo tmpInfo(strFile);
if(!tmpInfo.exists())
{
return false;
}
if(m_bHasTask)
{
return false;
}
// 转码参数:strParams
// 网络参数:rtp{sdp=rtsp://xx.xx.xx.xx:yyyy/}
// 表示本机ip时,可省略ip,只写端口,如rtp{sdp=rtsp://:8554/}
QString strNetPara = convertOupputUrl(nStreamType,strIP,nPort);
//如果有转码参数则完整的rtsp如下
// 如sout = "#transcode{vcodec=h264,acodec=mpga,ab=128,channels=2,samplerate=44100,scodec=none}:rtp{sdp=rtsp://127.0.0.1:8554/}"
QString strsout = "";
if(strParams.isEmpty())
{
strsout = QString("#%1").arg(strNetPara);
}
else
{
//strParams 格式为:transcode{vcodec=h264,acodec=mpga,ab=128,channels=2,samplerate=44100,scodec=none}
strsout = QString("#%1:%2").arg(strParams).arg(strNetPara);
}
// 将推流视频路径转换为本地系统风格,win下"a\\b\\c",linux下"a/b/c"
QString path = QDir::toNativeSeparators(strFile);
// 添加名为VIDEO_MEDIA_NAME的广播
int ret = libvlc_vlm_add_broadcast(m_pVlcInstance,
VIDEO_MEDIA_NAME,
path.toStdString().c_str(),
strsout.toStdString().c_str(),
0,
nullptr,
true,
false);//是否循环播放
if (ret != 0)
{
return false;
}
#if 0
m_pMedia = libvlc_media_new_path(m_pVlcInstance,path.toStdString().c_str());
libvlc_media_player_set_media(m_pMediaPlayer,m_pMedia);
if(libvlc_media_player_play(m_pMediaPlayer))
{
return false;
}
#endif
// 播放该广播
ret = libvlc_vlm_play_media(m_pVlcInstance, VIDEO_MEDIA_NAME);
if(ret == 0)
{
m_bHasTask = true;
if(!m_future.isRunning())
{
m_future = QtConcurrent::run(this,&VLCStreamer::onCheckThread);
}
}
// m_nMediaLength = libvlc_vlm_get_media_instance_length(m_pVlcInstance,VIDEO_MEDIA_NAME,nInstance);//todo 组后一个参数的id怎么获取的?
qDebug()<<libvlc_vlm_show_media( m_pVlcInstance,
VIDEO_MEDIA_NAME );//这里打印了流信息,后续如果要判断推流结束可能需要解析才行,具体信息看最后注释内容
return m_bHasTask;
}
bool VLCStreamer::startStreaming(const QString &strFile, const QString &strParams)
{
QFileInfo tmpInfo(strFile);
if(!tmpInfo.exists())
{
return false;
}
if(m_bHasTask)
{
return false;
}
// 将推流视频路径转换为本地系统风格,win下"a\\b\\c",linux下"a/b/c"
QString path = QDir::toNativeSeparators(strFile);
// 添加名为VIDEO_MEDIA_NAME的广播
int ret = libvlc_vlm_add_broadcast(m_pVlcInstance,
VIDEO_MEDIA_NAME,
path.toStdString().c_str(),
strParams.toStdString().c_str(),
0,
nullptr,
true,
false);//是否循环播放
if (ret != 0)
{
return false;
}
// 播放该广播
ret = libvlc_vlm_play_media(m_pVlcInstance, VIDEO_MEDIA_NAME);
if(ret == 0)
{
m_bHasTask = true;
if(!m_future.isRunning())
{
m_future = QtConcurrent::run(this,&VLCStreamer::onCheckThread);
}
}
// m_nMediaLength = libvlc_vlm_get_media_instance_length(m_pVlcInstance,VIDEO_MEDIA_NAME,0);//todo 组后一个参数的id怎么获取的?
return m_bHasTask;
}
//当前程序缺陷,需要手动停止,哪怕是推流结束 【20241001已经解决,但是不是很合理】
bool VLCStreamer::stopStreaming()
{
if(!m_bHasTask)
{
return false;
}
#if 1
// 停止推流
int ret = libvlc_vlm_stop_media(m_pVlcInstance, VIDEO_MEDIA_NAME);
if(ret == 0)
{
m_nMediaStopType = E_STOP_TYPE_FORCED;
// m_semaphore_timer.release();
// m_future.waitForFinished();
}
else
{
return false;
}
#else
libvlc_media_player_stop(m_pMediaPlayer);
m_bHasTask = true;
m_semaphore_timer.release();
m_future.waitForFinished();
emit streamingFinished();
#endif
return true;
}
QString VLCStreamer::convertOupputUrl(const int &nStreamType, const QString &strIP, const int &nPort)
{
QString strOutputUrl = "";
switch (nStreamType)
{
case E_STREAM_TYPE_RTSP:
strOutputUrl = QString("rtp{sdp=rtsp://%1:%2/}").arg(strIP).arg(nPort);
break;
case E_STREAM_TYPE_RTP_TS:
strOutputUrl = QString("rtp{dst=%1,port=%2,mux=ts}").arg(strIP).arg(nPort);
break;
case E_STREAM_TYPE_RTP_AVP:
strOutputUrl = QString("rtp{dst=%1,port=%2}").arg(strIP).arg(nPort);
break;
case E_STREAM_TYPE_UDP:
strOutputUrl = QString("udp{dst=%1:%2}").arg(strIP).arg(nPort); //todo 客户端不能播放,待后续排查
break;
case E_STREAM_TYPE_HTTP:
strOutputUrl = QString("http{mux=ffmpeg{mux=flv},dst=%1:%2/}").arg(strIP).arg(nPort);
break;
default:
break;
}
return strOutputUrl;
}
bool VLCStreamer::parseJsonData(const QByteArray &jsonArray,unsigned long long &nVideoTotalTime,unsigned long long &nCurTime,bool& IsNull)
{
// qDebug()<<"==================================";
// qDebug()<<jsonArray;
// qDebug()<<"==================================";
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonArray);
if(jsonDoc.isEmpty())
{
return false;
}
if(!jsonDoc.isObject())//根据打印结果,就是json对象
{
return false;
}
QJsonObject jsonObj = jsonDoc.object();
QJsonValue jsonValue = jsonObj.value("instances");
if(jsonValue.isNull())
{
IsNull = true;
return false;
}
/**
*
*
*
*结束后的状态信息
"{
"name": "Video",
"type": "broadcast",
"enabled": "yes\,
"loop\": "no"
"inputs": ["E:\\little.mp4" ],
"output": "#rtp{sdp=rtsp://127.0.0.1:10086/}",
"options": null,
"instances": null
}"
*
*
*
*
* */
if(!jsonValue.isObject())//按照正常逻辑,这里肯定为object,但是播放完成后,instances里面的内容就为空了
{
return false;
}
jsonObj = jsonValue.toObject();//得到第二个instance 对象
jsonValue = jsonObj.value("instance");//得到第二个instance对象里面的对象
if(!jsonValue.isObject())
{
return false;
}
jsonObj = jsonValue.toObject();
if(jsonObj.contains("length") && jsonObj.contains("time"))//不能通过时间或者state来判断,因为播放完成后,vlc会打印结束[main input debug: EOF reached],然乎里面的state还是为playing,甚至时间和播放进度都未达到结束时间,播放进度未到达1,接近1,然后突然结束,instances里面的内容为null
{
bool res = false;
nVideoTotalTime = jsonObj.value("length").toVariant().toLongLong(&res);
if(!res)
{
return false;
}
nCurTime = jsonObj.value("time").toVariant().toLongLong(&res);
if(!res)
{
return false;
}
}
else
{
return false;
}
return true;
}
void VLCStreamer::onCheckThread()
{
unsigned long long nCurTime = 0,nTotalTime = 0;//播放当前时刻和视频总时长,单位应该是微秒
bool bInit = false;
while(true)
{
if(m_semaphore_timer.tryAcquire(1,20))
{
m_nMediaStopType = E_STOP_TYPE_FORCED;
m_bHasTask = false;
break;
}
else
{
QByteArray jsonData = libvlc_vlm_show_media( m_pVlcInstance,
VIDEO_MEDIA_NAME );
jsonData = jsonData.trimmed();
bool bIsNull = false;
if(!parseJsonData(jsonData,nTotalTime,nCurTime,bIsNull))
{
if(bInit && bIsNull)
{
if(nTotalTime - nCurTime > ERROR_TIME)
{
m_nMediaStopType = E_STOP_TYPE_ERROR;
}
else
{
if(E_STOP_TYPE_UNKNOWNED == m_nMediaStopType)
{
m_nMediaStopType = E_STOP_TYPE_NORMAL;
}
}
m_bHasTask = false;
break;//这里表示解析失败了,当推流结束时,json中instances里面的内容会为空,此时表示推流结束了
}
continue;
}
if(nTotalTime != 0)
{
bInit = true;//表示正常播放了,因为目前无法知道退出状态,所以当这里为true时,表示正常推流过
}
if((nCurTime >= nTotalTime) &&(nCurTime!=0 && nTotalTime!=0))
{
m_bHasTask = false;
break;
}
}
}
libvlc_vlm_del_media(m_pVlcInstance,VIDEO_MEDIA_NAME);//结束此媒体播放任务
emit streamingFinished(m_nMediaStopType);
switch(m_nMediaStopType)
{
case E_STOP_TYPE_NORMAL:
qDebug()<<"video push finished normal";
break;
case E_STOP_TYPE_FORCED:
qDebug()<<"video push finished forced";
break;
case E_STOP_TYPE_ERROR:
qDebug()<<"video push finished error";
break;
default:
qDebug()<<"video push finished unknowned";
break;
}
m_nMediaStopType = E_STOP_TYPE_UNKNOWNED;
}
/**
*
*
* {
"name": "Video",
"type": "broadcast",
"enabled": "yes",
"loop": "no",
"inputs": [
"E:\little.mp4"
],
"output": "#rtp{sdp=rtsp://127.0.0.1:10086/}",
"options": null,
"instances": {
"instance": {
"name": "default",
"state": "playing",
"position": "0.007775",
"time": "1657000",
"length": "213120000",
"rate": "1.000000",
"title": "0",
"chapter": "0",
"can-seek": "1",
"playlistindex": "1"
}
}
}
{
"name": "Video",
"type": "broadcast",
"enabled": "yes",
"loop": "no",
"inputs": [
"E:\little.mp4"
],
"output": "#rtp{sdp=rtsp://127.0.0.1:10086/}",
"options": null,
"instances": {
"instance": {
"name": "default",
"state": "playing",
"position": "0.007775",
"time": "1657000",
"length": "213120000",
"rate": "1.000000",
"title": "0",
"chapter": "0",
"can-seek": "1",
"playlistindex": "1"
}
}
}
*
*
*
*
*
*
*
*
*
* */