diff --git a/Src/GDDC/gddcdlg.cpp b/Src/GDDC/gddcdlg.cpp index 2934e9f..f77fde0 100644 --- a/Src/GDDC/gddcdlg.cpp +++ b/Src/GDDC/gddcdlg.cpp @@ -79,6 +79,8 @@ void GDDCdlg::initWindow() { m_GDDCStateDlg = new GDDCStateInfo(this); ui->pushButShowStatePage->setIcon(QIcon(":/res/right.png")); ui->pushButShowCmdPage->setIcon(QIcon(":/res/down.png")); + + ui->WgtffmpegVideo->setVedioSaveFileDirPath("./Video"); } // 初始化参数 diff --git a/Src/ModelCamera/modelcameradlg.cpp b/Src/ModelCamera/modelcameradlg.cpp index eadada2..45a264e 100644 --- a/Src/ModelCamera/modelcameradlg.cpp +++ b/Src/ModelCamera/modelcameradlg.cpp @@ -4,9 +4,8 @@ ModelCameraDlg::ModelCameraDlg(QWidget *parent) : QDialog(parent), ui(new Ui::ModelCameraDlg) { ui->setupUi(this); - + ui->cameraVideoWidget->setVedioSaveFileDirPath("./3DCameraVideo"); InitialComboBox(); - cameraCMDThread = new QThread(); encodeModelCamera = new EncodeModelCamera(); encodeModelCamera->moveToThread(cameraCMDThread); diff --git a/Src/Video/cffmpeg_decode.cpp b/Src/Video/cffmpeg_decode.cpp index 4ffb279..8ee3d70 100644 --- a/Src/Video/cffmpeg_decode.cpp +++ b/Src/Video/cffmpeg_decode.cpp @@ -2,9 +2,7 @@ // Cffmpeg_decode::Cffmpeg_decode() { Cffmpeg_decode::Cffmpeg_decode(QObject *parent) : QObject(parent) { inputFormatCtx = avformat_alloc_context(); - outputFormatCtx = avformat_alloc_context(); inputPacket = av_packet_alloc(); - outputPacket = av_packet_alloc(); yuvFrame = av_frame_alloc(); rgbFrame = av_frame_alloc(); avformat_network_init(); @@ -13,8 +11,6 @@ Cffmpeg_decode::Cffmpeg_decode(QObject *parent) : QObject(parent) { Cffmpeg_decode::~Cffmpeg_decode() { if (!inputPacket) av_packet_free(&inputPacket); - if (!outputPacket) - av_packet_free(&outputPacket); if (!yuvFrame) av_frame_free(&yuvFrame); if (!rgbFrame) @@ -29,8 +25,6 @@ Cffmpeg_decode::~Cffmpeg_decode() { avcodec_close(decoderCtx); if (!inputFormatCtx) avformat_close_input(&inputFormatCtx); - if (!outputFormatCtx) - avformat_close_input(&outputFormatCtx); } void Cffmpeg_decode::setUrl(QString url) { _url = url; } @@ -46,7 +40,7 @@ bool Cffmpeg_decode::open_input_file() { // 以udp方式打开,如果以tcp方式打开将udp替换为tcp av_dict_set(&avdic, "rtsp_transport", "udp", 0); // 设置超时断开连接时间,单位微秒//listen_timeout - //av_dict_set(&avdic, "listen_timeout", "200000", 0); + // 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); @@ -55,7 +49,8 @@ bool Cffmpeg_decode::open_input_file() { inputFormatCtx->flags |= AVFMT_FLAG_NONBLOCK; // 打开输入流 - if (avformat_open_input(&inputFormatCtx, _url.toUtf8().data(), NULL, &avdic) <0) { + if (avformat_open_input(&inputFormatCtx, _url.toUtf8().data(), NULL, &avdic) < + 0) { printf("Cannot open input file.\n"); return 0; } @@ -66,7 +61,8 @@ bool Cffmpeg_decode::open_input_file() { } // 从输入流中找到第一个视频流 for (int i = 0; i < inputFormatCtx->nb_streams; i++) { - if (inputFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + if (inputFormatCtx->streams[i]->codecpar->codec_type == + AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; continue; } @@ -101,112 +97,6 @@ bool Cffmpeg_decode::open_input_file() { return 0; } - //===============================编码================================ - // 1、输出文件设置 - if (avformat_alloc_output_context2(&outputFormatCtx, NULL, NULL, "output_video.mp4") < 0) { - qDebug() << "Could not create output context.\n"; - return 0; - } - - // 从输入流中找到第一个视频流 - for (int i = 0; i < inputFormatCtx->nb_streams; i++) { - if (inputFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - videoStreamIndexOut = i; - continue; - } - } - if (videoStreamIndexOut == -1) { - printf("Cannot find video stream in file.\n"); - return 0; - } - // 获取视频流的解码器参数 - videoCodecPara2 = inputFormatCtx->streams[videoStreamIndexOut]->codecpar; - - // 3、创建输出视频流 - outStream = avformat_new_stream(outputFormatCtx, NULL); - if (!outStream) { - qDebug() << "Failed allocating output stream.\n"; - return 0; - } - //4、查找编码器 - encoder = avcodec_find_encoder(videoCodecPara2->codec_id); - if(!encoder){ - return 0; - } - - //5、分配解码器上下文 - encoderCtx = avcodec_alloc_context3(encoder); - if (!encoderCtx) { - return 0; - } - - //初始化视频AVCodecContext - encoderCtx->height = videoCodecPara2->height; - encoderCtx->width = videoCodecPara2->width; - videoCodecPara2->framerate = av_guess_frame_rate(inputFormatCtx,inputFormatCtx->streams[videoStreamIndexOut],NULL); - - //视频流的时间基是1 / 帧率 - encoderCtx->time_base = av_inv_q(inputFormatCtx->streams[videoStreamIndexOut]->codecpar->framerate); - encoderCtx->sample_aspect_ratio = inputFormatCtx->streams[videoStreamIndexOut]->codecpar->sample_aspect_ratio; - if (encoder->pix_fmts) - { - encoderCtx->pix_fmt = encoder->pix_fmts[0]; - } - encoderCtx->codec_type = AVMEDIA_TYPE_VIDEO; - encoderCtx->codec_id = AV_CODEC_ID_H264; - encoderCtx->bit_rate = 2000000; - encoderCtx->time_base.num = 1; - encoderCtx->time_base.den = 20; - - //全局头 - if (outputFormatCtx->oformat->flags & AVFMT_GLOBALHEADER) - { - encoderCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; - } - - // //设置选项 - // AVDictionary *param = 0; - // //H.264 - // if(videoCodecPara->codec_id == AV_CODEC_ID_H264) { - // av_dict_set(¶m, "preset", "slow", 0); - // av_dict_set(¶m, "tune", "zerolatency", 0); - // av_dict_set(¶m, "profile", "main", 0); - // } - // //H.265 - // if(videoCodecPara->codec_id == AV_CODEC_ID_H265){ - // av_dict_set(¶m, "preset", "ultrafast", 0); - // av_dict_set(¶m, "tune", "zero-latency", 0); - // } - - //6、打开编码器 - if (avcodec_open2(encoderCtx, encoder, NULL) < 0) { - qDebug() << "Error occurred when opening encoder.\n"; - //return 0; - } - - avcodec_parameters_from_context(outputFormatCtx->streams[0]->codecpar, encoderCtx); - //avcodec_parameters_to_context(encoderCtx,outStream->codecpar); - - // 打开输出文件 - if (!(outputFormatCtx->oformat->flags & AVFMT_NOFILE)) { - if (avio_open(&outputFormatCtx->pb, - outputFormatCtx->url /* "output_video.mp4"*/, - AVIO_FLAG_WRITE) < 0) { - printf("Could not open output file.\n"); - return 0; - } - } - - //写入输出文件的头信息 - if (avformat_write_header(outputFormatCtx, NULL) < 0) { - qDebug() << "Error occurred when opening output file.\n"; - //return 0; - } - //格式化输出输出文件信息 - av_dump_format(outputFormatCtx,0,outputFormatCtx->url,1); - - - // 初始化图像转换器 swsCtx = sws_getContext(decoderCtx->width, // decoderCtx->height, // @@ -219,6 +109,7 @@ bool Cffmpeg_decode::open_input_file() { decoderCtx->width, // decoderCtx->height, // 1); + out_buffer = (unsigned char *)av_malloc(numBytes * sizeof(unsigned char)); int res = av_image_fill_arrays(rgbFrame->data, // @@ -231,7 +122,9 @@ bool Cffmpeg_decode::open_input_file() { qDebug() << "Fill arrays failed.\n"; return 0; } - + if (!openSave()) { + return false; + }; return true; } @@ -252,6 +145,15 @@ void Cffmpeg_decode::run() { } if (inputPacket->stream_index == videoStreamIndex) { + // 保存裸流 + if (m_formatContextSave) { + // 由于保存的m_formatContextSave只创建了一个视频流,而读取到的图像的流索引不一定为0,可能会出现错误【Invalid + // packet stream index: 1】 + // 所以这里需要将stream_index指定为和m_formatContextSave中视频流索引相同,因为就一个流,所以直接设置为0 + inputPacket->stream_index = 0; + av_write_frame(m_formatContextSave, + inputPacket); // 将数据包写入输出媒体文件 + } // 解码数据包 if (avcodec_send_packet(decoderCtx, inputPacket) >= 0) { int ret; @@ -276,25 +178,25 @@ void Cffmpeg_decode::run() { QImage::Format_RGB32); emit sendQImage(img); QThread::msleep(28); - - - - //将解码后的帧写入输出文件 - if (av_write_frame(outputFormatCtx, inputPacket) < 0) { - //qDebug() << "Error muxing packet.\n"; - break; - } - - } } av_packet_unref(inputPacket); } - //花屏 - // if (av_read_frame(inputFormatCtx, inputPacket) < 0) { - // break; // 达到文件末尾 - // } + if (IsstopPlay) { + if (m_formatContextSave) { + saveDone(); + } + break; + } + // 花屏 + // if (av_read_frame(inputFormatCtx, inputPacket) < 0) { + // break; // 达到文件末尾 + // } + } + if (m_formatContextSave) { + saveDone(); } + qDebug() << "All video play done"; } // 退出 @@ -305,8 +207,6 @@ void Cffmpeg_decode::stop() { IsstopPlay = true; if (!inputPacket) av_packet_free(&inputPacket); - if (!outputPacket) - av_packet_free(&outputPacket); if (!yuvFrame) av_frame_free(&yuvFrame); if (!rgbFrame) @@ -321,6 +221,83 @@ void Cffmpeg_decode::stop() { avcodec_close(decoderCtx); if (!inputFormatCtx) avformat_close_input(&inputFormatCtx); - if (!outputFormatCtx) - avformat_close_input(&outputFormatCtx); +} + +void Cffmpeg_decode::setSaveFileDirPath(QString fileDirPath) { + saveFileDirPath = fileDirPath; +} + +bool Cffmpeg_decode::openSave() { + QDir dir; + if (!dir.exists(saveFileDirPath)) { + dir.mkdir(saveFileDirPath); + } + QString strName = + QString("/%1.h264") + .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH-mm-ss")); + strName = saveFileDirPath + strName; + // const AVOutputFormat *ofmt = av_guess_format("mp4", NULL, NULL); + int ret = avformat_alloc_output_context2( + &m_formatContextSave, nullptr, nullptr, strName.toStdString().data()); + if (ret < 0) { + // free(); + qWarning() << "DecodeVideo Error"; + return false; + } + // m_videoStreamOut->codecpar->codec_tag = 0; + // if (m_formatContextSave->oformat->flags & AVFMT_GLOBALHEADER) { + // m_formatContextSave->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + // // m_videoStreamOut->codecpar->extradata = (uint8_t *)av_malloc(1024); + // // m_videoStreamOut->codecpar->extradata_size = 0; + // } + // 创建并初始化AVIOContext以访问url所指示的资源。 + ret = avio_open(&m_formatContextSave->pb, strName.toStdString().data(), + AVIO_FLAG_WRITE); + if (ret < 0) { + // free(); + qWarning() << "DecodeVideo Error"; + return false; + } + // 向媒体文件添加新流 + m_videoStreamOut = avformat_new_stream(m_formatContextSave, nullptr); + if (!m_videoStreamOut) { + qWarning() << "DecodeVideo Error"; + return false; + } + // 拷贝一些参数,给codecpar赋值(这里使用编码器上下文进行赋值) + ret = avcodec_parameters_from_context(m_videoStreamOut->codecpar, decoderCtx); + if (ret < 0) { + // free(); + qWarning() << "DecodeVideo Error"; + return false; + } + + // 写入文件头 + ret = avformat_write_header(m_formatContextSave, nullptr); + if (ret < 0) { + // free(); + qWarning() << "DecodeVideo Error"; + return false; + } + + m_writeHeader = true; + qDebug() << "开始录制视频!"; + return true; +} + +void Cffmpeg_decode::saveDone() { + if (m_formatContextSave && m_writeHeader) { + av_write_trailer(m_formatContextSave); // 写入文件尾 + m_writeHeader = false; + } + // 关闭文件 + if (m_formatContextSave && !(m_formatContextSave->flags & AVFMT_NOFILE)) { + avio_close(m_formatContextSave->pb); + // av_freep(m_videoStreamOut); + if (m_formatContextSave) { + avformat_free_context(m_formatContextSave); + m_formatContextSave = nullptr; + } + // m_videoStreamOut = nullptr; + } } diff --git a/Src/Video/cffmpeg_decode.h b/Src/Video/cffmpeg_decode.h index 93c54d9..0543a22 100644 --- a/Src/Video/cffmpeg_decode.h +++ b/Src/Video/cffmpeg_decode.h @@ -1,7 +1,9 @@ #ifndef CFFMPEG_DECODE_H #define CFFMPEG_DECODE_H +#include #include +#include #include #include #include @@ -38,6 +40,11 @@ public: bool open_input_file(); bool IsstopPlay = false; void stop(); + /** + * @brief 设置拉流保存文件夹路径 + * @param fileDirPath 文件夹路径 + */ + void setSaveFileDirPath(QString fileDirPath); public slots: void run(); @@ -46,36 +53,37 @@ signals: void sendConnectFail(int); private: - AVFormatContext *inputFormatCtx = NULL; // - AVFormatContext *outputFormatCtx = NULL; // + AVFormatContext *inputFormatCtx = NULL; // AVCodecContext *decoderCtx = NULL; // - AVCodecContext *encoderCtx = NULL;// + AVCodecContext *encoderCtx = NULL; // const AVCodec *decoder = NULL; // 解码器 - const AVCodec *encoder = NULL; // - AVPacket *inputPacket = NULL; // - AVPacket *outputPacket = NULL; // + AVPacket *inputPacket = NULL; // AVFrame *yuvFrame = NULL; AVFrame *rgbFrame = NULL; - struct SwsContext *swsCtx = NULL; + struct SwsContext *swsCtx = NULL; // 图像转换上下文 AVCodecParameters *videoCodecPara = nullptr; - AVCodecParameters *videoCodecPara2 = nullptr; - - - AVStream *outStream; unsigned char *out_buffer = nullptr; int videoStreamIndex = -1; - int videoStreamIndexOut = -1; int numBytes = -1; QString _url; + QString saveFileDirPath; + /******** 保存裸流使用 ******************/ + AVFormatContext *m_formatContextSave = nullptr; // 封装上下文 + QString m_strCodecName; // 编解码器名称 + AVStream *m_videoStreamOut = nullptr; // 输出视频流 + bool m_writeHeader = false; // 是否写入文件头 +private: + bool openSave(); + void saveDone(); }; #endif // CFFMPEG_DECODE_H diff --git a/Src/Video/ffmpegvideodlg.cpp b/Src/Video/ffmpegvideodlg.cpp index 079cc7b..67e5dff 100644 --- a/Src/Video/ffmpegvideodlg.cpp +++ b/Src/Video/ffmpegvideodlg.cpp @@ -5,7 +5,7 @@ ffmpegvideoDlg::ffmpegvideoDlg(QWidget *parent) : QWidget(parent), ui(new Ui::ffmpegvideoDlg) { ui->setupUi(this); - iniWindow(); + // iniWindow(); } ffmpegvideoDlg::~ffmpegvideoDlg() { @@ -13,6 +13,7 @@ ffmpegvideoDlg::~ffmpegvideoDlg() { delete ui; } +// 暂未使用 void ffmpegvideoDlg::iniWindow() { QString VideoFilePath = QDir::currentPath() + "./Video"; QDir VideoDir(VideoFilePath); @@ -23,10 +24,15 @@ void ffmpegvideoDlg::iniWindow() { } void ffmpegvideoDlg::setUrl(QString url) { ffmpeg->setUrl(url); } +void ffmpegvideoDlg::setVedioSaveFileDirPath(QString saveDirPath) { + videoSaveDirPath = saveDirPath; +} + void ffmpegvideoDlg::play() { if (!m_PlayStatus) { m_PlayStatus = true; ffmpeg = new Cffmpeg_decode; + ffmpeg->setSaveFileDirPath(videoSaveDirPath); ffmpeg->IsstopPlay = false; ffmpeg->moveToThread(&workerThread); connect(&workerThread, &QThread::finished, ffmpeg, diff --git a/Src/Video/ffmpegvideodlg.h b/Src/Video/ffmpegvideodlg.h index 428f8ce..e91386b 100644 --- a/Src/Video/ffmpegvideodlg.h +++ b/Src/Video/ffmpegvideodlg.h @@ -44,6 +44,7 @@ private: public: void iniWindow(); void setUrl(QString url); + void setVedioSaveFileDirPath(QString saveDirPath); void play(); void stop(); @@ -69,6 +70,7 @@ private: Cffmpeg_decode *ffmpeg; QThread workerThread; QImage img; + QString videoSaveDirPath; }; #endif // FFMPEGVIDEODLG_H