From d0db97d0e616f6454897202a6648eaff59e0e284 Mon Sep 17 00:00:00 2001 From: cbwu <504-wuchengbo@htsdfp.com> Date: Tue, 10 Sep 2024 09:51:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=8E=A8=E6=B5=81=E4=BC=98=E5=8C=96?= =?UTF-8?q?=EF=BC=8C=E5=85=89=E7=94=B5=E5=90=8A=E8=88=B1=E6=9A=82=E6=9C=AA?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/Video/cffmpeg_decode.cpp | 30 ++++++++++++------ Src/Video/cffmpeg_decode.h | 7 ++-- Src/Video/ffmpegpushstream.cpp | 58 +++++++++++++++++++++++++++------- Src/Video/ffmpegpushstream.h | 7 ++-- Src/Video/ffmpegvideodlg.cpp | 43 +++++++++++-------------- Src/Video/ffmpegvideodlg.h | 2 +- 6 files changed, 96 insertions(+), 51 deletions(-) diff --git a/Src/Video/cffmpeg_decode.cpp b/Src/Video/cffmpeg_decode.cpp index 27e9380..54c657d 100644 --- a/Src/Video/cffmpeg_decode.cpp +++ b/Src/Video/cffmpeg_decode.cpp @@ -143,11 +143,13 @@ void Cffmpeg_decode::run() { IsstopPlay = true; return; } - if (bRecordTime) { - startTime = av_gettime(); - qDebug() << "*******StartTime:" << QString::number(startTime); - bRecordTime = false; - } + // if (bRecordTime) { + // startTime = av_gettime(); + // qDebug() << "*******StartTime:" << QString::number(startTime); + // bRecordTime = false; + // } + firstDts = AV_NOPTS_VALUE; // 初始化第一帧的DTS + startTime = av_gettime(); // 读取数据包 while (av_read_frame(inputFormatCtx, inputPacket) >= 0) { if (IsstopPlay) { @@ -155,15 +157,21 @@ void Cffmpeg_decode::run() { break; } + // 第一次获取时间设置起始DTS + if (firstDts == AV_NOPTS_VALUE) { + firstDts = inputPacket->dts; + startTime = av_gettime(); // 记录第一个包到来的系统时间 + } + if (inputPacket->stream_index == videoStreamIndex) { // 推流 if (bPushStreamFlag) { // av_packet_clone(inputPacket); AVPacket *outputPacket = av_packet_clone(inputPacket); - emit sendStreamData_Signal(outputPacket, frm_cnt); + emit sendStreamData_Signal(outputPacket, frm_cnt, startTime, firstDts); } - - // 保存裸流 + // qDebug() << "******拉流" << QString::number(frm_cnt++); + // 保存裸流 if (m_formatContextSave) { // 由于保存的m_formatContextSave只创建了一个视频流,而读取到的图像的流索引不一定为0,可能会出现错误【Invalid // packet stream index: 1】 @@ -200,8 +208,8 @@ void Cffmpeg_decode::run() { } } av_packet_unref(inputPacket); - frm_cnt++; } + frm_cnt++; if (IsstopPlay) { if (m_formatContextSave) { saveDone(); @@ -241,6 +249,10 @@ void Cffmpeg_decode::stop() { avcodec_close(decoderCtx); if (!inputFormatCtx) avformat_close_input(&inputFormatCtx); + + // 停止推流 + bPushStreamFlag = false; + emit sendStopPushStream_Signal(); } void Cffmpeg_decode::setSaveFileDirPath(QString fileDirPath) { diff --git a/Src/Video/cffmpeg_decode.h b/Src/Video/cffmpeg_decode.h index f8c4a97..1b8d922 100644 --- a/Src/Video/cffmpeg_decode.h +++ b/Src/Video/cffmpeg_decode.h @@ -35,7 +35,9 @@ signals: void sendQImage(QImage); void sendConnectFail(int); void sendInitPushStream_Signal(AVFormatContext *inputFormatCtx); - void sendStreamData_Signal(AVPacket *pkt, int frm_cnt); + void sendStreamData_Signal(AVPacket *pkt, int frm_cnt, int64_t startTime, + int64_t firstDts); + void sendStopPushStream_Signal(); public: bool IsstopPlay = false; @@ -67,7 +69,8 @@ private: QString _url; QString saveFileDirPath; bool bRecordTime = true; - long long startTime; + int64_t startTime; + int64_t firstDts; /******** 保存裸流使用 ******************/ AVFormatContext *m_formatContextSave = nullptr; // 封装上下文 QString m_strCodecName; // 编解码器名称 diff --git a/Src/Video/ffmpegpushstream.cpp b/Src/Video/ffmpegpushstream.cpp index 87f2a25..34b8f78 100644 --- a/Src/Video/ffmpegpushstream.cpp +++ b/Src/Video/ffmpegpushstream.cpp @@ -40,7 +40,7 @@ int FFmpegPushStream::openNetworkStream(AVFormatContext *inputFormatCtx) { return -1; } // outputStream->codecpar = inputStream->codecpar; - // outputStream->codecpar->codec_tag = 0; + outputStream->codecpar->codec_tag = 0; // outputStream->time_base.num = 1; break; } @@ -62,16 +62,26 @@ int FFmpegPushStream::openNetworkStream(AVFormatContext *inputFormatCtx) { } mInitStatus = true; this->inputFormatCtx = inputFormatCtx; + startTime = av_gettime_relative(); return 1; } -int FFmpegPushStream::pushStream(AVPacket *pkt, int frm_cnt) { +/** + * @brief 推流 + * @param pkt: 未解码帧 + * @param frm_cnt: 帧计数 + * @param startTime: 开始推流的时间 + * @param firstDts: 第一帧的dts + * @return + */ +int FFmpegPushStream::pushStream(AVPacket *pkt, int frm_cnt, int64_t startTime, + int64_t firstDts) { if (!mInitStatus) return -1; if (pkt->dts <= 0) return -1; - + // qDebug() << "******推流" << QString::number(frm_cnt++); int inputStreamIndex = pkt->stream_index; int outputStreamIndex = 0; // 没有pts的视频数据,如未解码的H.264裸流,需要重新计算其pts。 @@ -88,6 +98,22 @@ int FFmpegPushStream::pushStream(AVPacket *pkt, int frm_cnt) { pkt->duration = (double)calc_duration / (double)(av_q2d(time_base) * AV_TIME_BASE); } + + // 视频帧推送速度 + firstDts = av_rescale_q(firstDts, + inputFormatCtx->streams[inputStreamIndex]->time_base, + AVRational{1, AV_TIME_BASE}); + auto pts_time = av_rescale_q( + pkt->dts, inputFormatCtx->streams[inputStreamIndex]->time_base, + AVRational{1, AV_TIME_BASE}); + int64_t streamTime = pts_time - firstDts; // 计算帧的相对时间 + auto now_time = av_gettime() - startTime; // 获取差值 + int64_t delay = streamTime - now_time; + if (delay > 0) { + // qDebug() << "****************sleep time:" << QString::number(delay); + av_usleep(delay); + } + // 计算延时后,重新指定时间戳 AVRational istream_base = inputFormatCtx->streams[inputStreamIndex]->time_base; @@ -108,15 +134,6 @@ int FFmpegPushStream::pushStream(AVPacket *pkt, int frm_cnt) { return 1; } - // 视频帧推送速度 - auto pts_time = av_rescale_q(pkt->dts, ostream_base, AVRational{1, 1000}); - auto now_time = av_gettime() / 1000; - if (pts_time > now_time) { - qDebug() << "****************sleep time:" - << QString::number(pts_time - now_time); - av_usleep(static_cast(pts_time - now_time)); - } - // 向推流服务器推送流数据 pkt->stream_index = 0; int ret = av_interleaved_write_frame(outputFormatCtx, pkt); @@ -126,3 +143,20 @@ int FFmpegPushStream::pushStream(AVPacket *pkt, int frm_cnt) { // 数据包写入成功,现在可以释放pkt av_packet_unref(pkt); } + +/** + * @brief 停止推流 + */ +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); + } +} diff --git a/Src/Video/ffmpegpushstream.h b/Src/Video/ffmpegpushstream.h index 862ecd4..94270c4 100644 --- a/Src/Video/ffmpegpushstream.h +++ b/Src/Video/ffmpegpushstream.h @@ -19,13 +19,14 @@ signals: private: AVFormatContext *inputFormatCtx = nullptr; // AVFormatContext *outputFormatCtx = NULL; // - AVCodecContext *encoderCtx = nullptr; // - const AVCodec *encoder = nullptr; // 编码器 int videoIndex = -1; QString pushStreamIP; // 推流地址 bool mInitStatus = false; + int64_t startTime; public slots: - int pushStream(AVPacket *pkt, int frm_cnt); + int pushStream(AVPacket *pkt, int frm_cnt, int64_t startTime, + int64_t firstDts); + void stopPush(); int openNetworkStream(AVFormatContext *inputFormatCtx); }; diff --git a/Src/Video/ffmpegvideodlg.cpp b/Src/Video/ffmpegvideodlg.cpp index 8a03370..8be5b8e 100644 --- a/Src/Video/ffmpegvideodlg.cpp +++ b/Src/Video/ffmpegvideodlg.cpp @@ -32,7 +32,7 @@ void ffmpegvideoDlg::play(QString url) { ffmpeg->setSaveFileDirPath(videoSaveDirPath); ffmpeg->IsstopPlay = false; if (!pushStreamIP.isEmpty()) // 推流 - startPushStream(m_PlayStatus); + startPushStream(); ffmpeg->moveToThread(&workerThread); connect(&workerThread, &QThread::finished, ffmpeg, @@ -56,10 +56,8 @@ void ffmpegvideoDlg::stop() { if (m_PlayStatus) { ffmpeg->stop(); m_PlayStatus = false; - ffmpeg->IsstopPlay = true; // 线程结束标志 - if (!pushStreamIP.isEmpty()) // 停止推流 - startPushStream(m_PlayStatus); - workerThread.quit(); // 线程退出时自动delete线程里的类 + ffmpeg->IsstopPlay = true; // 线程结束标志 + workerThread.quit(); // 线程退出时自动delete线程里的类 workerThread.wait(); img.fill(Qt::black); } @@ -113,27 +111,24 @@ bool ffmpegvideoDlg::Isplay(bool IsstopPlay) { return ffmpeg->IsstopPlay = IsstopPlay; } -void ffmpegvideoDlg::startPushStream(bool bPush) { - if (bPush) { - if (ffmpegPushStream == nullptr) { - ffmpegPushStream = new FFmpegPushStream; - ffmpegPushStream->setRemoteIP(pushStreamIP); // 设置推流地址 - } +void ffmpegvideoDlg::startPushStream() { + if (ffmpegPushStream == nullptr) { + ffmpegPushStream = new FFmpegPushStream; + ffmpegPushStream->setRemoteIP(pushStreamIP); // 设置推流地址 + } - if (pushStreamThread == nullptr) { - pushStreamThread = new QThread; - pushStreamThread->start(); - ffmpegPushStream->moveToThread(pushStreamThread); - } - connect(ffmpeg, &Cffmpeg_decode::sendInitPushStream_Signal, - ffmpegPushStream, &FFmpegPushStream::openNetworkStream); - connect(ffmpeg, &Cffmpeg_decode::sendStreamData_Signal, ffmpegPushStream, - &FFmpegPushStream::pushStream); - ffmpeg->bPushStreamFlag = true; - } else { - if (ffmpeg) - ffmpeg->bPushStreamFlag = false; + if (pushStreamThread == nullptr) { + pushStreamThread = new QThread; + pushStreamThread->start(); + ffmpegPushStream->moveToThread(pushStreamThread); } + 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; } void ffmpegvideoDlg::setPushStreamIP(QString pushStreamURL) { diff --git a/Src/Video/ffmpegvideodlg.h b/Src/Video/ffmpegvideodlg.h index 4a38cab..9dbfc74 100644 --- a/Src/Video/ffmpegvideodlg.h +++ b/Src/Video/ffmpegvideodlg.h @@ -28,7 +28,7 @@ public: void play(QString); void stop(); bool Isplay(bool IsstopPlay); - void startPushStream(bool bPush); + void startPushStream(); void setPushStreamIP(QString pushStreamURL); private: