perf: 推拉流代码优化

video_old
cbwu 6 months ago
parent 7f2334513e
commit f1e8c8090e

@ -76,7 +76,7 @@ void GDDCdlg::initWindow() {
m_DlgGDDCSet = new GDDCSet(this);
m_DlgGDDCSet->setWindowFlags(Qt::Dialog);
m_DlgGDDCSet->setWindowModality(Qt::WindowModal);//设置为模态对话框
m_DlgGDDCSet->setWindowModality(Qt::WindowModal); // 设置为模态对话框
m_GDDCCmdDlg = new GDDCCmdDlg(this);
m_GDDCStateDlg = new GDDCStateInfo(this);
ui->pushButShowStatePage->setIcon(QIcon(":/res/right.png"));
@ -341,7 +341,7 @@ void GDDCdlg::resizeUI() {
// 定时器处理
void GDDCdlg::GDDCControlTimeOut() {
if (m_GDDCCmdDlg->sendTimes > 0) { // 优先发送次数指令
if (m_GDDCCmdDlg->sendTimes > 0) { // 优先发送次数指令
m_GDDCCmdDlg->sendTimes -= 1;
// UDP控制
@ -357,7 +357,7 @@ void GDDCdlg::GDDCControlTimeOut() {
if (m_GDDCCmdDlg->sendTimes == 0) {
m_GDDCCmdDlg->clearTJDCCmdBuffer();
}
} else { // 发送常发帧或长按指令
} else { // 发送常发帧或长按指令
m_GDDCCmdDlg->UpdateDataTJDC();
// UDP控制
if (connectFlag[3]) {
@ -376,10 +376,12 @@ void GDDCdlg::GDDCControlTimeOut() {
void GDDCdlg::startConnectURL1() {
if (!connectFlag[0]) {
connectFlag[0] = true;
ffmpegvideoDlg *videoDlg = ui->WgtffmpegVideo;
// 开始拉流
ui->WgtffmpegVideo->m_flowType = m_DlgGDDCSet->m_FlowType;
ui->WgtffmpegVideo->play(m_DlgGDDCSet->m_playURL1);
videoDlg->m_flowType = m_DlgGDDCSet->m_FlowType;
videoDlg->setStreamIP(m_DlgGDDCSet->m_playURL1);
videoDlg->setPlayVideo(true);
// ui->WgtffmpegVideo->play(m_DlgGDDCSet->m_playURL1);
}
}
@ -389,7 +391,8 @@ void GDDCdlg::stopConnectURL1() {
connectFlag[0] = false;
// Is_openVideo = false;
// ui->WgtffmpegVideo->m_PlayStatus = Is_openVideo;
ui->WgtffmpegVideo->stop();
ui->WgtffmpegVideo->setPlayVideo(false);
// ui->WgtffmpegVideo->stop();
}
}
@ -472,7 +475,8 @@ void GDDCdlg::startPushURL() {
// //方式1命令行推流
// process = new QProcess();
// connect(process, &QProcess::readyReadStandardOutput, this, [=]() mutable {
// connect(process, &QProcess::readyReadStandardOutput, this, [=]() mutable
// {
// QString Output = process->readAllStandardOutput();
// qDebug() << "Output:" << Output;
// });
@ -482,14 +486,16 @@ void GDDCdlg::startPushURL() {
// });
// // process->start("cmd",QStringList()<<"/c"<<"ffmpeg -i
// // rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid -c copy -f flv
// // rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid -c copy -f
// flv
// // rtmp://182.92.130.23/app/test");
// QStringList m_cmd;
// QString str;
// // str = "ffmpeg -i " + m_DlgGDDCSet->m_playURL1 + " -c copy -f flv " +
// // m_DlgGDDCSet->m_pushURL;
// str = "ffmpeg -rtsp_transport tcp -i " + m_DlgGDDCSet->m_playURL1 + " -c:v libx264 -c:a copy -f flv " +
// str = "ffmpeg -rtsp_transport tcp -i " + m_DlgGDDCSet->m_playURL1 + "
// -c:v libx264 -c:a copy -f flv " +
// m_DlgGDDCSet->m_pushURL;
// m_cmd << "/c" << str;
// process->start("cmd", m_cmd);
@ -497,11 +503,13 @@ void GDDCdlg::startPushURL() {
// //方式2代码推流
// if(!connectFlag[0])
// {
// QMessageBox::information(NULL, tr("提示"), "请先开始连接", QMessageBox::Ok);
// m_DlgGDDCSet->setPushStreamText("推送");
// return;
// QMessageBox::information(NULL, tr("提示"), "请先开始连接",
// QMessageBox::Ok); m_DlgGDDCSet->setPushStreamText("推送"); return;
// }
ui->WgtffmpegVideo->setPushStreamIP(m_DlgGDDCSet->m_pushURL);
ffmpegvideoDlg *videoDlg = ui->WgtffmpegVideo;
videoDlg->setStreamIP(m_DlgGDDCSet->m_playURL1);
videoDlg->setPushStreamIP(m_DlgGDDCSet->m_pushURL);
videoDlg->setPushStream(true);
m_DlgGDDCSet->setPushStreamText("停止推送");
connectFlag[5] = true;
@ -535,9 +543,9 @@ void GDDCdlg::stopPushURL() {
// }
// }
//方式2代码推流
ui->WgtffmpegVideo->setPushStreamIP("");
// 方式2代码推流
// ui->WgtffmpegVideo->setPushStreamIP("");
ui->WgtffmpegVideo->setPushStream(false);
m_DlgGDDCSet->setPushStreamText("推送");
connectFlag[5] = false;

@ -4,6 +4,11 @@
ModelCameraDlg::ModelCameraDlg(QWidget *parent)
: QDialog(parent), ui(new Ui::ModelCameraDlg) {
ui->setupUi(this);
ui->videoIPLineEdit->setText(
QStringLiteral("rtmp://liteavapp.qcloud.com/live/"
"liteavdemoplayerstreamid")); // rtsp://192.168.5.70:8554/LIVE
ui->pushStreamIPEdit->setText(
QStringLiteral("rtmp://182.92.130.23/app/stream999"));
ui->cameraVideoWidget->setVedioSaveFileDirPath("./3DCameraVideo");
InitialComboBox();
cameraCMDThread = new QThread();
@ -164,18 +169,35 @@ void ModelCameraDlg::on_OFFBtn_clicked() {
// 播放视频
void ModelCameraDlg::on_pushButton_5_clicked() {
QString str = ui->pushButton_5->text();
ffmpegvideoDlg *videoDlg = ui->cameraVideoWidget;
if (str == "播放视频") {
QString ip = ui->videoIPLineEdit->text();
ui->cameraVideoWidget->setPushStreamIP(
QStringLiteral("rtmp://182.92.130.23/app/stream999"));
ui->cameraVideoWidget->play(ip);
QString streamIP = ui->videoIPLineEdit->text();
videoDlg->setStreamIP(streamIP);
videoDlg->setPlayVideo(true);
ui->pushButton_5->setText("暂停播放");
} else {
ui->cameraVideoWidget->stop();
videoDlg->setPlayVideo(false);
ui->pushButton_5->setText("播放视频");
}
}
// 推流
void ModelCameraDlg::on_pushStreamBtn_clicked() {
QString str = ui->pushStreamBtn->text();
ffmpegvideoDlg *videoDlg = ui->cameraVideoWidget;
if (str == "开始推流") {
QString streamIP = ui->videoIPLineEdit->text();
QString pushIP = ui->pushStreamIPEdit->text();
videoDlg->setStreamIP(streamIP);
videoDlg->setPushStreamIP(pushIP);
videoDlg->setPushStream(true);
ui->pushStreamBtn->setText("停止推流");
} else {
videoDlg->setPushStream(false);
ui->pushStreamBtn->setText("开始推流");
}
}
// 快门速度
void ModelCameraDlg::on_SSComboBox_activated(int index) {
QString tmp = ui->SSComboBox->itemText(index);

@ -45,6 +45,8 @@ private slots:
void on_receiveUDP(QByteArray cmdDataArray);
void on_pushStreamBtn_clicked();
private:
void InitialComboBox();
// 状态查询指令

@ -25,7 +25,7 @@
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="2,2,2,4,2">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,1,1,1,2,1">
<property name="spacing">
<number>12</number>
</property>
@ -55,14 +55,11 @@
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>拍照指令</string>
<string>推拉流设置</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="2,1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>4</number>
</property>
@ -79,26 +76,17 @@
<number>4</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>4</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="spacing">
<number>10</number>
<number>8</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<item>
<widget class="QLabel" name="label_21">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -121,12 +109,12 @@
</font>
</property>
<property name="text">
<string>拍照间隔</string>
<string>拉流地址</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="ONBtn">
<item>
<widget class="QLineEdit" name="videoIPLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -145,13 +133,30 @@
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_22">
<property name="text">
<string>开机</string>
<string>推流地址:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="stopPhotoBtn">
<item>
<widget class="QLineEdit" name="pushStreamIPEdit"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>8</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QPushButton" name="pushButton_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -171,12 +176,81 @@
</size>
</property>
<property name="text">
<string>停止拍照</string>
<string>播放视频</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="OFFBtn">
<item>
<widget class="QPushButton" name="pushStreamBtn">
<property name="text">
<string>开始推流</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>拍照指令</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="2">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>18</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -195,13 +269,18 @@
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>关机</string>
<string>拍照间隔:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="startPhotoBtn">
<item row="2" column="0">
<widget class="QPushButton" name="ONBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -221,12 +300,12 @@
</size>
</property>
<property name="text">
<string>开始拍照</string>
<string>开</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="photoTimeDbSpinBox">
<item row="1" column="1">
<widget class="QPushButton" name="stopPhotoBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -245,47 +324,13 @@
<height>16777215</height>
</size>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.800000000000000</double>
</property>
<property name="maximum">
<double>25.000000000000000</double>
</property>
<property name="stepType">
<enum>QAbstractSpinBox::StepType::DefaultStepType</enum>
<property name="text">
<string>停止拍照</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>5</number>
</property>
<item row="1" column="0">
<widget class="QLineEdit" name="videoIPLineEdit">
<item row="2" column="1">
<widget class="QPushButton" name="OFFBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -304,10 +349,13 @@
<height>16777215</height>
</size>
</property>
<property name="text">
<string>关机</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="pushButton_5">
<item row="1" column="0">
<widget class="QPushButton" name="startPhotoBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -327,14 +375,14 @@
</size>
</property>
<property name="text">
<string>播放视频</string>
<string>开始拍照</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_21">
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="photoTimeDbSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -351,13 +399,20 @@
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="text">
<string>拉流地址:</string>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.800000000000000</double>
</property>
<property name="maximum">
<double>25.000000000000000</double>
</property>
<property name="stepType">
<enum>QAbstractSpinBox::StepType::DefaultStepType</enum>
</property>
</widget>
</item>
@ -393,7 +448,7 @@
<number>8</number>
</property>
<property name="verticalSpacing">
<number>5</number>
<number>14</number>
</property>
<property name="leftMargin">
<number>5</number>

@ -6,6 +6,7 @@ Cffmpeg_decode::Cffmpeg_decode(QObject *parent) : QObject(parent) {
yuvFrame = av_frame_alloc();
rgbFrame = av_frame_alloc();
avformat_network_init();
m_rtsp_transport = "tcp";
}
Cffmpeg_decode::~Cffmpeg_decode() {
@ -27,24 +28,62 @@ Cffmpeg_decode::~Cffmpeg_decode() {
avformat_close_input(&inputFormatCtx);
}
void Cffmpeg_decode::setUrl(QString url) { _url = url; }
void Cffmpeg_decode::setStreamUrl(QString url) { _url = url; }
void Cffmpeg_decode::setPlayVideo(bool bPlay) {
mutex.lock();
bPlayVideoFlag = bPlay;
if (bPlayVideoFlag) {
// 初始化 previous_pts_time 为无效值(拉流使用)
previous_pts_time = -1.0; // 表示没有上一帧的时间戳
first_frame_pts_time = 0.0; // 第一帧的 PTS 时间
first_frame_system_time = 0.0; // 第一帧解码时的系统时间
}
mutex.unlock();
}
/**
* @brief
* @param bPushStream: /
*/
void Cffmpeg_decode::setPushStream(bool bPushStream) {
mutex.lock();
bPushStreamFlag = bPushStream;
if (bPushStreamFlag) { // 推流初始化
// emit sendInitPushStream_Signal(inputFormatCtx);
} else { // 停止推流
emit sendStopPushStream_Signal();
bOpenPushStreamFlag = false;
firstDts = AV_NOPTS_VALUE; // 初始化第一帧的DTS
}
mutex.unlock();
}
bool Cffmpeg_decode::open_input_file() {
if (_url.isEmpty())
return 0;
m_rtsp_transport = _url.left(4) == "rtmp" ? "tcp" : "udp";
//========================解码============================
AVDictionary *avdic = NULL;
// 设置缓存大小1080p可将值调大
av_dict_set(&avdic, "buffer_size", "2048000", 0);
// 以udp方式打开如果以tcp方式打开将udp替换为tcp
av_dict_set(&avdic, "rtsp_transport", m_rtsp_transport.toUtf8().data(), 0);
int ret = av_dict_set(&avdic, "rtsp_transport",
m_rtsp_transport.toUtf8().data(), 0);
// 如果设置失败则设置UDP传输
if (avdic == NULL || ret < 0) {
if (m_rtsp_transport == "tcp")
av_dict_set(&avdic, "rtsp_transport", "udp", 0);
else
av_dict_set(&avdic, "rtsp_transport", "tcp", 0);
}
// 设置缓存大小1080p可将值调大
av_dict_set(&avdic, "buffer_size", "4096000", 0);
// 设置超时断开连接时间,单位微秒//listen_timeout
// av_dict_set(&avdic, "listen_timeout", "200000", 0);
av_dict_set(&avdic, "stimeout", "200000", 0);
av_dict_set(&avdic, "max_delay", "3", 0); // 设置最大时延
av_dict_set(&avdic, "tune", "zerolatency", 0);
av_dict_set(&avdic, "stimeout", "2000000", 0); // 设置超时2秒
av_dict_set(&avdic, "max_delay", "3", 0); // 设置最大时延
av_dict_set(&avdic, "tune", "zerolatency", 0); // 实时编码
av_dict_set(&avdic, "preset", "ultrafast", 0);
av_dict_set(&avdic, "threads", "auto", 0); // 自动开启线程数
inputFormatCtx->flags |= AVFMT_FLAG_NONBLOCK;
@ -64,7 +103,7 @@ bool Cffmpeg_decode::open_input_file() {
if (inputFormatCtx->streams[i]->codecpar->codec_type ==
AVMEDIA_TYPE_VIDEO) {
videoStreamIndex = i;
continue;
break;
}
}
if (videoStreamIndex == -1) {
@ -91,6 +130,9 @@ bool Cffmpeg_decode::open_input_file() {
printf("Cannot initialize parameters.\n");
return 0;
}
// 设置加速解码
decoderCtx->lowres = decoder->max_lowres;
decoderCtx->flags2 |= AV_CODEC_FLAG2_FAST;
// 打开解码器
if (avcodec_open2(decoderCtx, decoder, NULL) < 0) {
printf("Cannot open codec.\n");
@ -123,15 +165,9 @@ bool Cffmpeg_decode::open_input_file() {
return 0;
}
// 推流初始化
if (bPushStreamFlag) {
emit sendInitPushStream_Signal(inputFormatCtx);
}
// 裸流保存初始化
// m_saveVideoFlag = openSave();
// 裸流保存
if (!openSave()) {
return false;
};
return true;
}
@ -143,11 +179,13 @@ void Cffmpeg_decode::run() {
IsstopPlay = true;
return;
}
// if (bRecordTime) {
// startTime = av_gettime();
// qDebug() << "*******StartTime:" << QString::number(startTime);
// bRecordTime = false;
// }
// 初始化 previous_pts_time 为无效值(拉流使用)
previous_pts_time = -1.0; // 表示没有上一帧的时间戳
first_frame_pts_time = 0.0; // 第一帧的 PTS 时间
first_frame_system_time = 0.0; // 第一帧解码时的系统时间
// 推流使用
firstDts = AV_NOPTS_VALUE; // 初始化第一帧的DTS
startTime = av_gettime();
// 读取数据包
@ -157,8 +195,8 @@ void Cffmpeg_decode::run() {
break;
}
// 第一次获取时间设置起始DTS
if (firstDts == AV_NOPTS_VALUE) {
// 开始推流时,第一次获取时间设置起始DTS
if (firstDts == AV_NOPTS_VALUE && bPushStreamFlag) {
firstDts = inputPacket->dts;
startTime = av_gettime(); // 记录第一个包到来的系统时间
}
@ -166,13 +204,17 @@ void Cffmpeg_decode::run() {
if (inputPacket->stream_index == videoStreamIndex) {
// 推流
if (bPushStreamFlag) {
// av_packet_clone(inputPacket);
if (!bOpenPushStreamFlag) { // 推流初始化
emit sendInitPushStream_Signal(inputFormatCtx);
bOpenPushStreamFlag = true;
// QThread::msleep(10);
}
AVPacket *outputPacket = av_packet_clone(inputPacket);
emit sendStreamData_Signal(outputPacket, frm_cnt, startTime, firstDts);
}
// qDebug() << "******拉流" << QString::number(frm_cnt++);
// 保存裸流
if (m_formatContextSave) {
if (m_saveVideoFlag) {
// 由于保存的m_formatContextSave只创建了一个视频流而读取到的图像的流索引不一定为0可能会出现错误【Invalid
// packet stream index: 1】
// 所以这里需要将stream_index指定为和m_formatContextSave中视频流索引相同因为就一个流所以直接设置为0
@ -181,37 +223,68 @@ void Cffmpeg_decode::run() {
inputPacket); // 将数据包写入输出媒体文件
}
// 解码数据包
if (avcodec_send_packet(decoderCtx, inputPacket) >= 0) {
int ret;
while ((ret = avcodec_receive_frame(decoderCtx, yuvFrame)) >= 0) {
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return;
} else if (ret < 0) {
fprintf(stderr, "Error during decoding\n");
exit(1);
if (bPlayVideoFlag) {
// 解码数据包
if (avcodec_send_packet(decoderCtx, inputPacket) >= 0) {
int ret;
while ((ret = avcodec_receive_frame(decoderCtx, yuvFrame)) >= 0) {
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return;
} else if (ret < 0) {
fprintf(stderr, "Error during decoding\n");
exit(1);
}
if (inputPacket->dts <= 0)
continue;
// 获取当前帧的 PTS显示时间戳并转换为相同时间基
double pts_time = av_rescale_q(
inputPacket->pts,
inputFormatCtx->streams[videoStreamIndex]->time_base,
AVRational{1, AV_TIME_BASE});
// 如果是第一帧,记录系统时间和 PTS 时间
if (previous_pts_time == -1.0) {
first_frame_pts_time = pts_time;
first_frame_system_time = av_gettime(); // 系统时间(秒)
previous_pts_time = pts_time;
}
// 转化为RGB图像并显示
sws_scale(swsCtx, //
yuvFrame->data, //
yuvFrame->linesize, //
0, //
decoderCtx->height, //
rgbFrame->data, //
rgbFrame->linesize);
QImage img(out_buffer, //
decoderCtx->width, //
decoderCtx->height, //
QImage::Format_RGB32);
emit sendQImage(img);
// QThread::msleep(1);
// 计算从第一帧开始的相对 PTS 时间
double elapsed_pts_time = pts_time - first_frame_pts_time;
// 计算从第一帧开始的相对系统时间
double elapsed_system_time = av_gettime() - first_frame_system_time;
// 计算需要等待的时间(us)
double wait_time = elapsed_pts_time - elapsed_system_time;
qDebug() << "pull stream sleep time:"
<< QString::number(wait_time / 1000.0);
if (wait_time > 0) {
av_usleep(wait_time); // 延时以同步 PTS
}
// 更新 previous_pts_time 为当前帧的 PTS
previous_pts_time = pts_time;
}
sws_scale(swsCtx, //
yuvFrame->data, //
yuvFrame->linesize, //
0, //
decoderCtx->height, //
rgbFrame->data, //
rgbFrame->linesize);
QImage img(out_buffer, //
decoderCtx->width, //
decoderCtx->height, //
QImage::Format_RGB32);
emit sendQImage(img);
QThread::msleep(28);
}
}
av_packet_unref(inputPacket);
}
frm_cnt++;
if (IsstopPlay) {
if (m_formatContextSave) {
if (m_saveVideoFlag) {
saveDone();
}
break;
@ -221,17 +294,16 @@ void Cffmpeg_decode::run() {
// break; // 达到文件末尾
// }
}
if (m_formatContextSave) {
if (m_saveVideoFlag) {
saveDone();
}
// QCoreApplication::processEvents();
qDebug() << "All video play done";
}
// 退出
void Cffmpeg_decode::stop() {
// 写入输出文件的尾信息
// av_write_trailer(outputFormatCtx);
IsstopPlay = true;
if (!inputPacket)
av_packet_free(&inputPacket);
@ -239,26 +311,30 @@ void Cffmpeg_decode::stop() {
av_frame_free(&yuvFrame);
if (!rgbFrame)
av_frame_free(&rgbFrame);
if (!encoderCtx)
avcodec_free_context(&encoderCtx);
if (!encoderCtx)
avcodec_close(encoderCtx);
if (!decoderCtx)
avcodec_free_context(&decoderCtx);
if (!encoderCtx)
avcodec_free_context(&encoderCtx);
if (!decoderCtx)
avcodec_close(decoderCtx);
if (!decoderCtx)
avcodec_free_context(&decoderCtx);
if (!inputFormatCtx)
avformat_close_input(&inputFormatCtx);
// 停止推流
bPushStreamFlag = false;
emit sendStopPushStream_Signal();
}
/**
* @brief
* @param fileDirPath:
*/
void Cffmpeg_decode::setSaveFileDirPath(QString fileDirPath) {
saveFileDirPath = fileDirPath;
}
/**
* @brief
* @return
*/
bool Cffmpeg_decode::openSave() {
QDir dir;
if (!dir.exists(saveFileDirPath)) {
@ -287,13 +363,13 @@ bool Cffmpeg_decode::openSave() {
AVIO_FLAG_WRITE);
if (ret < 0) {
// free();
qWarning() << "DecodeVideo Error";
qWarning() << "Open file Error";
return false;
}
// 向媒体文件添加新流
m_videoStreamOut = avformat_new_stream(m_formatContextSave, nullptr);
if (!m_videoStreamOut) {
qWarning() << "DecodeVideo Error";
qWarning() << "Create New Stream Error";
return false;
}
// 拷贝一些参数给codecpar赋值(这里使用编码器上下文进行赋值)
@ -308,15 +384,18 @@ bool Cffmpeg_decode::openSave() {
ret = avformat_write_header(m_formatContextSave, nullptr);
if (ret < 0) {
// free();
qWarning() << "DecodeVideo Error";
qWarning() << "avformat_write_header Error";
return false;
}
m_writeHeader = true;
qDebug() << "开始录制视频";
qDebug() << "保存视频文件初始化成功";
return true;
}
/**
* @brief
*/
void Cffmpeg_decode::saveDone() {
if (m_formatContextSave && m_writeHeader) {
av_write_trailer(m_formatContextSave); // 写入文件尾
@ -330,11 +409,8 @@ void Cffmpeg_decode::saveDone() {
avformat_free_context(m_formatContextSave);
m_formatContextSave = nullptr;
}
// m_videoStreamOut = nullptr;
}
m_saveVideoFlag = false;
}
void Cffmpeg_decode::setFlowType(QString param)
{
m_rtsp_transport = param;
}
void Cffmpeg_decode::setFlowType(QString param) { m_rtsp_transport = param; }

@ -2,10 +2,12 @@
#define CFFMPEG_DECODE_H
#include "ffmpeginclude.h"
#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QImage>
#include <QMutex>
#include <QPaintEvent>
#include <QPainter>
#include <QThread>
@ -19,7 +21,6 @@ public:
Cffmpeg_decode(QObject *parent = nullptr);
~Cffmpeg_decode();
public:
bool open_input_file();
void stop();
/**
@ -27,11 +28,13 @@ public:
* @param fileDirPath
*/
void setSaveFileDirPath(QString fileDirPath);
void setFlowType(QString);//设置拉流打开方式
void setFlowType(QString); // 设置拉流打开方式
void setStreamUrl(QString url);
public slots:
void run();
void setUrl(QString url);
void setPlayVideo(bool bPlay);
void setPushStream(bool bPushStream);
signals:
void sendQImage(QImage);
void sendConnectFail(int);
@ -41,10 +44,15 @@ signals:
void sendStopPushStream_Signal();
public:
QMutex mutex;
bool IsstopPlay = false;
bool bPushStreamFlag = false;
bool bPushStreamFlag = false; // 推流标志
bool bPlayVideoFlag = false; // 播放视频标志
private:
bool bOpenPushStreamFlag = false; // 推流初始化
AVFormatContext *inputFormatCtx = NULL; //
AVCodecContext *decoderCtx = NULL; //
@ -70,18 +78,32 @@ private:
QString _url;
QString saveFileDirPath;
bool bRecordTime = true;
// 推流使用
int64_t startTime;
int64_t firstDts;
// 初始化 previous_pts_time 为无效值(拉流使用)
double previous_pts_time; // 表示没有上一帧的时间戳
double first_frame_pts_time; // 第一帧的 PTS 时间
double first_frame_system_time; // 第一帧解码时的系统时间
/******** 保存裸流使用 ******************/
AVFormatContext *m_formatContextSave = nullptr; // 封装上下文
QString m_strCodecName; // 编解码器名称
AVStream *m_videoStreamOut = nullptr; // 输出视频流
bool m_writeHeader = false; // 是否写入文件头
QString m_rtsp_transport = ""; //拉流打开方式UDP或TCP
QString m_rtsp_transport; // 拉流打开方式UDP或TCP
private:
bool m_saveVideoFlag = false;
/**
* @brief
* @return
*/
bool openSave();
/**
* @brief
*/
void saveDone();
};

@ -12,6 +12,9 @@ int FFmpegPushStream::openNetworkStream(AVFormatContext *inputFormatCtx) {
if (pushStreamIP.isEmpty())
return -1;
if (!inputFormatCtx)
return -1;
int ret;
// 初始化网络输出流
// const char *output_url = "rtsp://182.92.130.23/app/stream999";
@ -56,8 +59,9 @@ int FFmpegPushStream::openNetworkStream(AVFormatContext *inputFormatCtx) {
}
// 写入头文件
if (avformat_write_header(outputFormatCtx, NULL) < 0) {
qDebug() << "Error occurred when opening output file.\n";
ret = avformat_write_header(outputFormatCtx, NULL);
if (ret < 0) {
qDebug() << "Error occurred when write_header into output file.\n";
return -1;
}
mInitStatus = true;
@ -110,7 +114,7 @@ int FFmpegPushStream::pushStream(AVPacket *pkt, int frm_cnt, int64_t startTime,
auto now_time = av_gettime() - startTime; // 获取差值
int64_t delay = streamTime - now_time;
if (delay > 0) {
// qDebug() << "****************sleep time:" << QString::number(delay);
qDebug() << "****************sleep time:" << QString::number(delay / 1000);
av_usleep(delay);
}
@ -142,6 +146,8 @@ int FFmpegPushStream::pushStream(AVPacket *pkt, int frm_cnt, int64_t startTime,
}
// 数据包写入成功现在可以释放pkt
av_packet_unref(pkt);
av_packet_free(&pkt);
return 1;
}
/**
@ -149,14 +155,13 @@ int FFmpegPushStream::pushStream(AVPacket *pkt, int frm_cnt, int64_t startTime,
*/
void FFmpegPushStream::stopPush() {
av_write_trailer(outputFormatCtx);
if (inputFormatCtx != nullptr) {
avformat_close_input(&inputFormatCtx);
}
// 关闭输出
if (outputFormatCtx && !(outputFormatCtx->flags & AVFMT_NOFILE)) {
avio_close(outputFormatCtx->pb);
}
if (outputFormatCtx) {
avformat_free_context(outputFormatCtx);
outputFormatCtx = nullptr;
}
mInitStatus = false;
}

@ -13,6 +13,8 @@ ffmpegvideoDlg::ffmpegvideoDlg(QWidget *parent)
ffmpegvideoDlg::~ffmpegvideoDlg() {
stop();
if (!ffmpeg)
ffmpeg->deleteLater();
delete ui;
if (ffmpegPushStream != nullptr)
ffmpegPushStream->deleteLater();
@ -27,16 +29,18 @@ void ffmpegvideoDlg::setVedioSaveFileDirPath(QString saveDirPath) {
videoSaveDirPath = saveDirPath;
}
/**
* @brief ffmpegvideoDlg::play
* @param url
*/
void ffmpegvideoDlg::play(QString url) {
if (!m_PlayStatus) {
m_PlayStatus = true;
ffmpeg = new Cffmpeg_decode;
ffmpeg->setSaveFileDirPath(videoSaveDirPath);
ffmpeg->setStreamUrl(url); // 设置拉流URL
ffmpeg->IsstopPlay = false;
if (!pushStreamIP.isEmpty()){
startPushStream();
}
ffmpeg->setPlayVideo(this->m_bVideoPlayFlag); // 设置是否播放视频
ffmpeg->setFlowType(m_flowType);
ffmpeg->moveToThread(&workerThread);
@ -45,18 +49,20 @@ void ffmpegvideoDlg::play(QString url) {
&QObject::deleteLater); // 线程发送结束标志
connect(this, &ffmpegvideoDlg::operate, ffmpeg,
&Cffmpeg_decode::run); // 线程开始处理数据
connect(this, &ffmpegvideoDlg::setUrlSign, ffmpeg,
&Cffmpeg_decode::setUrl); // 设置URL
// connect(this, &ffmpegvideoDlg::setUrlSign, ffmpeg,
// &Cffmpeg_decode::setUrl); // 设置URL
connect(ffmpeg, SIGNAL(sendQImage(QImage)), this,
SLOT(receiveQImage(QImage))); // 发送解析的图片
connect(ffmpeg, SIGNAL(sendConnectFail(int)), this,
SLOT(showMessagBox(int))); // 发送错误信息提示
workerThread.start();
emit this->setUrlSign(url); // 设置URL
emit this->operate(); // 启用线程信号
emit this->operate(); // 启用线程信号,开始拉流
}
}
/**
* @brief
*/
void ffmpegvideoDlg::stop() {
if (m_PlayStatus) {
ffmpeg->stop();
@ -116,29 +122,66 @@ bool ffmpegvideoDlg::Isplay(bool IsstopPlay) {
return ffmpeg->IsstopPlay = IsstopPlay;
}
/**
* @brief
* @param streamURL
*/
void ffmpegvideoDlg::setStreamIP(QString streamURL) { m_streamIP = streamURL; }
// 设置推流地址
void ffmpegvideoDlg::setPushStreamIP(QString pushStreamURL) {
pushStreamIP = pushStreamURL;
m_pushStreamIP = pushStreamURL;
}
// 开始推流
void ffmpegvideoDlg::startPushStream() {
if (ffmpegPushStream == nullptr) {
ffmpegPushStream = new FFmpegPushStream;
ffmpegPushStream->setRemoteIP(pushStreamIP); // 设置推流地址
void ffmpegvideoDlg::setPushStream(bool bPush) {
this->m_bPushStreamFlag = bPush;
if (this->m_bPushStreamFlag) {
this->play(m_streamIP);
// QThread::msleep(500);
// 创建推流任务对象
if (ffmpegPushStream == nullptr) {
ffmpegPushStream = new FFmpegPushStream;
ffmpegPushStream->setRemoteIP(m_pushStreamIP); // 设置推流地址
}
// 推流线程开启
if (pushStreamThread == nullptr) {
pushStreamThread = new QThread;
pushStreamThread->start();
ffmpegPushStream->moveToThread(pushStreamThread);
}
if (ffmpeg) {
connect(ffmpeg, &Cffmpeg_decode::sendInitPushStream_Signal,
ffmpegPushStream, &FFmpegPushStream::openNetworkStream,
Qt::UniqueConnection);
connect(ffmpeg, &Cffmpeg_decode::sendStreamData_Signal, ffmpegPushStream,
&FFmpegPushStream::pushStream, Qt::UniqueConnection);
connect(ffmpeg, &Cffmpeg_decode::sendStopPushStream_Signal,
ffmpegPushStream, &FFmpegPushStream::stopPush,
Qt::UniqueConnection);
// ffmpeg->bPushStreamFlag = true;
}
qDebug() << "video threadID:" << QThread::currentThreadId();
ffmpeg->setPushStream(true);
} else { // 停止推流
ffmpeg->setPushStream(false);
if (!this->m_bVideoPlayFlag && !this->m_bPushStreamFlag)
this->stop();
}
}
if (pushStreamThread == nullptr) {
pushStreamThread = new QThread;
pushStreamThread->start();
ffmpegPushStream->moveToThread(pushStreamThread);
/**
* @brief
* @param bPlay:
*/
void ffmpegvideoDlg::setPlayVideo(bool bPlay) {
if (m_PlayStatus && ffmpeg) {
// emit startPlayVideoSignal(bPlay);
ffmpeg->setPlayVideo(bPlay);
}
connect(ffmpeg, &Cffmpeg_decode::sendInitPushStream_Signal, ffmpegPushStream,
&FFmpegPushStream::openNetworkStream);
connect(ffmpeg, &Cffmpeg_decode::sendStreamData_Signal, ffmpegPushStream,
&FFmpegPushStream::pushStream);
connect(ffmpeg, &Cffmpeg_decode::sendStopPushStream_Signal, ffmpegPushStream,
&FFmpegPushStream::stopPush);
ffmpeg->bPushStreamFlag = true;
m_bVideoPlayFlag = bPlay;
if (!m_streamIP.isEmpty())
this->play(m_streamIP);
if (!this->m_bVideoPlayFlag && !this->m_bPushStreamFlag)
this->stop();
}

@ -4,6 +4,7 @@
#include "cffmpeg_decode.h"
#include "ffmpeginclude.h"
#include "ffmpegpushstream.h"
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QImage>
@ -23,16 +24,31 @@ public:
explicit ffmpegvideoDlg(QWidget *parent = nullptr);
~ffmpegvideoDlg();
/**
* @brief
* @param saveDirPath:
*/
void setVedioSaveFileDirPath(QString saveDirPath);
//拉流
// 拉流
void play(QString);
void stop();
bool Isplay(bool IsstopPlay);
/**
* @brief
* @param streamURL:
*/
void setStreamIP(QString streamURL);
//推流
// 推流
void setPushStreamIP(QString pushStreamURL);
void startPushStream();
void setPushStream(bool bPush);
/**
* @brief
* @param bPlay:
*/
void setPlayVideo(bool bPlay);
private:
Ui::ffmpegvideoDlg *ui;
@ -45,6 +61,10 @@ public:
double m_ay;
QString m_flowType;
bool m_bVideoPlayFlag = false; // 视频播放标志
bool m_bPushStreamFlag = false; // 推流标志
protected:
void paintEvent(QPaintEvent *);
void resizeEvent(QResizeEvent *event);
@ -63,7 +83,9 @@ private:
QString videoSaveDirPath;
QThread *pushStreamThread;
FFmpegPushStream *ffmpegPushStream;
QString pushStreamIP;
QString m_pushStreamIP;
QString m_streamIP;
// bool bPushStream
};
#endif // FFMPEGVIDEODLG_H

Loading…
Cancel
Save