diff --git a/Src/GDDC/gddcdlg.cpp b/Src/GDDC/gddcdlg.cpp
index e0b9010..581ac04 100644
--- a/Src/GDDC/gddcdlg.cpp
+++ b/Src/GDDC/gddcdlg.cpp
@@ -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;
diff --git a/Src/ModelCamera/modelcameradlg.cpp b/Src/ModelCamera/modelcameradlg.cpp
index c4ae139..e9212d7 100644
--- a/Src/ModelCamera/modelcameradlg.cpp
+++ b/Src/ModelCamera/modelcameradlg.cpp
@@ -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);
diff --git a/Src/ModelCamera/modelcameradlg.h b/Src/ModelCamera/modelcameradlg.h
index 7aac316..5e42614 100644
--- a/Src/ModelCamera/modelcameradlg.h
+++ b/Src/ModelCamera/modelcameradlg.h
@@ -45,6 +45,8 @@ private slots:
void on_receiveUDP(QByteArray cmdDataArray);
+ void on_pushStreamBtn_clicked();
+
private:
void InitialComboBox();
// 状态查询指令
diff --git a/Src/ModelCamera/modelcameradlg.ui b/Src/ModelCamera/modelcameradlg.ui
index 23e4a33..b7dba3d 100644
--- a/Src/ModelCamera/modelcameradlg.ui
+++ b/Src/ModelCamera/modelcameradlg.ui
@@ -25,7 +25,7 @@
QFrame::Shape::NoFrame
-
+
12
@@ -55,14 +55,11 @@
-
-
+
- 拍照指令
+ 推拉流设置
-
- false
-
-
+
4
@@ -79,26 +76,17 @@
4
-
-
-
- 5
-
-
- 5
-
-
- 5
+
+
+ 4
- 5
-
-
- 10
+ 8
-
-
-
+
-
+
-
+
0
0
@@ -121,12 +109,12 @@
- 拍照间隔:
+ 拉流地址:
- -
-
+
-
+
0
@@ -145,13 +133,30 @@
16777215
+
+
+ -
+
- 开机
+ 推流地址:
- -
-
+
-
+
+
+
+
+ -
+
+
+ 8
+
+
+ 4
+
+
-
+
0
@@ -171,12 +176,81 @@
- 停止拍照
+ 播放视频
- -
-
+
-
+
+
+ 开始推流
+
+
+
+
+
+ -
+
+
+ Qt::Orientation::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+ -
+
+
+ 拍照指令
+
+
+ false
+
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
-
+
+
+ 5
+
+
+ 5
+
+
+ 5
+
+
+ 5
+
+
+ 10
+
+
+ 18
+
+
-
+
0
@@ -195,13 +269,18 @@
16777215
+
+
+ 10
+
+
- 关机
+ 拍照间隔:
- -
-
+
-
+
0
@@ -221,12 +300,12 @@
- 开始拍照
+ 开机
- -
-
+
-
+
0
@@ -245,47 +324,13 @@
16777215
-
- QAbstractSpinBox::ButtonSymbols::NoButtons
-
-
- 1
-
-
- 0.800000000000000
-
-
- 25.000000000000000
-
-
- QAbstractSpinBox::StepType::DefaultStepType
+
+ 停止拍照
-
-
- -
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 10
-
-
- 5
-
-
-
-
+
-
+
0
@@ -304,10 +349,13 @@
16777215
+
+ 关机
+
- -
-
+
-
+
0
@@ -327,14 +375,14 @@
- 播放视频
+ 开始拍照
- -
-
+
-
+
-
+
0
0
@@ -351,13 +399,20 @@
16777215
-
-
- 10
-
+
+ QAbstractSpinBox::ButtonSymbols::NoButtons
-
- 拉流地址:
+
+ 1
+
+
+ 0.800000000000000
+
+
+ 25.000000000000000
+
+
+ QAbstractSpinBox::StepType::DefaultStepType
@@ -393,7 +448,7 @@
8
- 5
+ 14
5
diff --git a/Src/Video/cffmpeg_decode.cpp b/Src/Video/cffmpeg_decode.cpp
index 1b80d58..6db2a99 100644
--- a/Src/Video/cffmpeg_decode.cpp
+++ b/Src/Video/cffmpeg_decode.cpp
@@ -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; }
diff --git a/Src/Video/cffmpeg_decode.h b/Src/Video/cffmpeg_decode.h
index 9512b61..079327a 100644
--- a/Src/Video/cffmpeg_decode.h
+++ b/Src/Video/cffmpeg_decode.h
@@ -2,10 +2,12 @@
#define CFFMPEG_DECODE_H
#include "ffmpeginclude.h"
+#include
#include
#include
#include
#include
+#include
#include
#include
#include
@@ -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();
};
diff --git a/Src/Video/ffmpegpushstream.cpp b/Src/Video/ffmpegpushstream.cpp
index 34b8f78..588fd53 100644
--- a/Src/Video/ffmpegpushstream.cpp
+++ b/Src/Video/ffmpegpushstream.cpp
@@ -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;
}
diff --git a/Src/Video/ffmpegvideodlg.cpp b/Src/Video/ffmpegvideodlg.cpp
index 6e72b05..ef70037 100644
--- a/Src/Video/ffmpegvideodlg.cpp
+++ b/Src/Video/ffmpegvideodlg.cpp
@@ -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();
}
diff --git a/Src/Video/ffmpegvideodlg.h b/Src/Video/ffmpegvideodlg.h
index 2f2ece5..60c22a2 100644
--- a/Src/Video/ffmpegvideodlg.h
+++ b/Src/Video/ffmpegvideodlg.h
@@ -4,6 +4,7 @@
#include "cffmpeg_decode.h"
#include "ffmpeginclude.h"
#include "ffmpegpushstream.h"
+#include
#include
#include
#include
@@ -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