wl
2025-02-20 951695d8663403b5e291f6af395c5721d7a7ec56
Merge branch 'master' of ssh://115.28.86.8:29418/~admin/FaceX_AI_智能守卫
2 文件已重命名
21个文件已添加
1个文件已修改
710 ■■■■■ 已修改文件
Client/王雨阳/code/UI.html 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Client/王雨阳/document/项目需求书2.0版.docx 补丁 | 查看 | 原始文档 | blame | 历史
Client/王雨阳/log/日志_王雨阳_250218.doc 补丁 | 查看 | 原始文档 | blame | 历史
Client/王雨阳/log/王雨阳_250218.doc 补丁 | 查看 | 原始文档 | blame | 历史
Client/董佳琦/log/日志_董佳琦_0218.doc 补丁 | 查看 | 原始文档 | blame | 历史
Client/董澎韬/code/facexmainwindow.cpp 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Client/董澎韬/code/facexmainwindow.h 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Client/董澎韬/code/main.cpp 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Client/董澎韬/code/qffmpeg.cpp 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Client/董澎韬/code/qffmpeg.h 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Client/董澎韬/code/rtspthread.cpp 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Client/董澎韬/code/rtspthread.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Client/董澎韬/log/日志_董澎韬_0218.doc 补丁 | 查看 | 原始文档 | blame | 历史
Client/董澎韬/log/日志_董澎韬_0219.doc 补丁 | 查看 | 原始文档 | blame | 历史
Client/解来鑫/log/日志_姓名_日期.doc 补丁 | 查看 | 原始文档 | blame | 历史
Client/郭文强/document/新需求功能规格说明书 - 郭文强.docx 补丁 | 查看 | 原始文档 | blame | 历史
Client/郭文强/log/郭文强_20250218.doc 补丁 | 查看 | 原始文档 | blame | 历史
Client/郭文强/log/郭文强_20250219.doc 补丁 | 查看 | 原始文档 | blame | 历史
ProjectInformation/1.2项目进度表.et 补丁 | 查看 | 原始文档 | blame | 历史
Server/冉凯/document/日志_冉凯_20250218.doc 补丁 | 查看 | 原始文档 | blame | 历史
Server/冉凯/log/日志_冉凯_20250219.doc 补丁 | 查看 | 原始文档 | blame | 历史
Server/卢敏/log/日志_卢敏_2.18.doc 补丁 | 查看 | 原始文档 | blame | 历史
Server/卢敏/log/日志_卢敏_2.19.doc 补丁 | 查看 | 原始文档 | blame | 历史
Server/张敏丽/document/日志_姓名_日期.doc 补丁 | 查看 | 原始文档 | blame | 历史
Client/ÍõÓêÑô/code/UI.html
New file
@@ -0,0 +1,202 @@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>人脸识别考勤 - äººè„¸å½•入模块</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }
        .container {
            max-width: 600px;
            margin: auto;
            padding: 20px;
            border: 1px solid #ccc;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }
        h1 {
            text-align: center;
            color: #333;
        }
        .form-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input[type="text"], button {
            width: 100%;
            padding: 10px;
            font-size: 16px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            background-color: #007bff;
            color: white;
            cursor: pointer;
        }
        button:hover {
            background-color: #0056b3;
        }
        .camera-preview {
            margin-top: 20px;
            text-align: center;
        }
        .camera-preview video {
            max-width: 100%;
            height: auto;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        .image-preview {
            margin-top: 20px;
            text-align: center;
        }
        .image-preview img {
            max-width: 100%;
            height: auto;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        .status-message {
            margin-top: 15px;
            text-align: center;
            font-size: 14px;
            color: #555;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>人脸识别考勤 - äººè„¸å½•入模块</h1>
        <!-- å·¥å·è¾“å…¥ -->
        <div class="form-group">
            <label for="employee-id">输入工号:</label>
            <input type="text" id="employee-id" placeholder="请输入工号">
        </div>
        <!-- æŸ¥è¯¢ä¿¡æ¯æŒ‰é’® -->
        <button onclick="searchUserInfo()">查询信息</button>
        <!-- ç”¨æˆ·ä¿¡æ¯å±•示 -->
        <div class="form-group" id="user-info" style="display: none;">
            <label>用户信息:</label>
            <p id="user-details"></p>
        </div>
        <!-- æ¨¡æ‹Ÿæ‘„像头抓取图片 -->
        <div class="camera-preview" id="camera-preview" style="display: none;">
            <video id="camera-video" autoplay playsinline></video>
            <div>
                <button onclick="captureImage('front')">抓取正脸</button>
                <button onclick="captureImage('left')">抓取左侧脸</button>
                <button onclick="captureImage('right')">抓取右侧脸</button>
            </div>
        </div>
        <!-- å›¾ç‰‡é¢„览 -->
        <div class="image-preview" id="image-preview" style="display: none;">
            <h3>抓取的图片预览</h3>
            <div id="captured-images">
                <img id="front-image" src="" alt="正脸图片" style="display: none;">
                <img id="left-image" src="" alt="左侧脸图片" style="display: none;">
                <img id="right-image" src="" alt="右侧脸图片" style="display: none;">
            </div>
        </div>
        <!-- æäº¤æŒ‰é’® -->
        <button onclick="submitData()" style="display: none;" id="submit-btn">提交</button>
        <!-- çŠ¶æ€æ¶ˆæ¯ -->
        <div class="status-message" id="status-message"></div>
    </div>
    <script>
        let cameraStream = null;
        // æ¨¡æ‹ŸæŸ¥è¯¢ç”¨æˆ·ä¿¡æ¯
        function searchUserInfo() {
            const employeeId = document.getElementById('employee-id').value;
            if (!employeeId) {
                alert('请输入工号');
                return;
            }
            // æ¨¡æ‹ŸæŸ¥è¯¢ç»“æžœ
            const userInfo = {
                id: employeeId,
                name: '张三',
                department: '技术部'
            };
            // æ˜¾ç¤ºç”¨æˆ·ä¿¡æ¯
            document.getElementById('user-details').innerText = `姓名: ${userInfo.name}, éƒ¨é—¨: ${userInfo.department}`;
            document.getElementById('user-info').style.display = 'block';
            // æ˜¾ç¤ºæ‘„像头
            startCamera();
        }
        // æ‰“开摄像头
        async function startCamera() {
            try {
                cameraStream = await navigator.mediaDevices.getUserMedia({ video: true });
                const videoElement = document.getElementById('camera-video');
                videoElement.srcObject = cameraStream;
                document.getElementById('camera-preview').style.display = 'block';
            } catch (error) {
                alert('无法访问摄像头,请检查权限或设备是否可用。');
                console.error('摄像头访问失败:', error);
            }
        }
        // æŠ“取图片
        function captureImage(faceType) {
            const videoElement = document.getElementById('camera-video');
            const canvas = document.createElement('canvas');
            canvas.width = videoElement.videoWidth;
            canvas.height = videoElement.videoHeight;
            const context = canvas.getContext('2d');
            context.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
            // å°†æŠ“取的图片显示在页面中
            const imageUrl = canvas.toDataURL('image/png');
            const imageElement = document.getElementById(`${faceType}-image`);
            imageElement.src = imageUrl;
            imageElement.style.display = 'block';
            // å¦‚果三张图片都已抓取,显示提交按钮
            const frontImage = document.getElementById('front-image').src;
            const leftImage = document.getElementById('left-image').src;
            const rightImage = document.getElementById('right-image').src;
            if (frontImage && leftImage && rightImage) {
                document.getElementById('submit-btn').style.display = 'block';
            }
        }
        // æ¨¡æ‹Ÿæäº¤æ•°æ®
        function submitData() {
            const employeeId = document.getElementById('employee-id').value;
            if (!employeeId) {
                alert('请先输入工号并查询信息');
                return;
            }
            // æ¨¡æ‹Ÿä¸Šä¼ å›¾ç‰‡å’Œæäº¤æ•°æ®
            const statusMessage = document.getElementById('status-message');
            statusMessage.innerText = '正在上传图片并提交数据...';
            setTimeout(() => {
                statusMessage.innerText = '提交成功!';
            }, 2000); // æ¨¡æ‹Ÿä¸Šä¼ å»¶è¿Ÿ
        }
    </script>
</body>
</html>
Client/ÍõÓêÑô/document/ÏîÄ¿ÐèÇóÊé2.0°æ.docx
Binary files differ
Client/ÍõÓêÑô/log/ÈÕÖ¾_ÍõÓêÑô_250218.doc
Binary files differ
Client/ÍõÓêÑô/log/ÍõÓêÑô_250218.doc
Binary files differ
Client/¶­¼Ñçù/log/ÈÕÖ¾_¶­¼Ñçù_0218.doc
Binary files differ
Client/¶­Åìèº/code/facexmainwindow.cpp
New file
@@ -0,0 +1,204 @@
#include "facexmainwindow.h"
#include "ui_facexmainwindow.h"
#include <QMessageBox>
#include <QStringList>
#include <QFileDialog>
#include <QGuiApplication>
#include <QScreen>
#include <QException>
#include <QThread>
FaceXMainWindow::FaceXMainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::FaceXMainWindow),
    m_player(new QMediaPlayer),
    m_playerlist(new QMediaPlaylist),
    m_videowidget(new QVideoWidget(this)),
    m_ffmpeg(new QFFmpeg(this)),
    m_rtspThread(nullptr)
{
    ui->setupUi(this);
    //设置可移动区域
    dragArea = QRect(0, 0, width(), 40);
    setWindowFlags(Qt::FramelessWindowHint);//隐藏边框
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    // è®¾ç½®æ’­æ”¾æŒ‰é’®å›¾æ ‡ä¸ºæ’­æ”¾å›¾æ ‡
    ui->btn_play->setStyleSheet("border-image: url(:/image/start.png)");
    //设置播放器播放队列
    m_player->setPlaylist(m_playerlist);
    //设置播放器显示窗口
    m_player->setVideoOutput(m_videowidget);
    // è®¾ç½®æ’­æ”¾å™¨æ˜¾ç¤ºçª—口
    m_videowidget->resize(ui->label_video->size());
    m_videowidget->move(ui->label_video->pos());
    m_videowidget->show();
    ui->label_video->show();
    // è¿žæŽ¥ QFFmpeg çš„信号
    connect(m_ffmpeg, &QFFmpeg::GetImage, this, &FaceXMainWindow::SetImage, Qt::QueuedConnection);
    // è¿žæŽ¥æ’­æ”¾å™¨çš„错误信号
    connect(m_player, QOverload<QMediaPlayer::Error>::of(&QMediaPlayer::error), this, [this](QMediaPlayer::Error error) {
        QMessageBox::critical(this, "播放错误", m_player->errorString());
    });
}
FaceXMainWindow::~FaceXMainWindow()
{
    if (m_rtspThread && m_rtspThread->isRunning()) {
        m_rtspThread->quit();
        m_rtspThread->wait();
    }
    delete ui;
}
void FaceXMainWindow::mousePressEvent(QMouseEvent *event)
{
    if (dragArea.contains(event->pos())) {
        offset = event->globalPos() - pos();
        isDragging = true;
    }
}
void FaceXMainWindow::mouseMoveEvent(QMouseEvent *event)
{
    if (isDragging && dragArea.contains(event->pos())) {
        move(event->globalPos() - offset);
    }
}
void FaceXMainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    isDragging = false;
}
//查询按钮
void FaceXMainWindow::on_btnSelect_clicked()
{
    QDate dateStart = ui->dateStart->date();
    QDate dateEnd = ui->dateEnd->date();
    if (dateStart > dateEnd) {
        QMessageBox::critical(nullptr, "错误", "开始时间不能大于结束时间!");
        return;
    }
    //将日期转换为日期时间计算相差的天数,超过60天则提示不用继续
    QDateTime dateTimeStart = ui->dateStart->dateTime();
    QDateTime dateTimeEnd = ui->dateEnd->dateTime();
    if (dateTimeStart.daysTo(dateTimeEnd) >= 60) {
        QMessageBox::critical(nullptr, "错误", "每次最大只能查询60天内!");
        return;
    }
    QStringList fileNames = QFileDialog::getOpenFileNames(this, "选择文件", "D:/", "视频文件 (*.mp4 *.avi *.mov);;所有文件 (*.*)");
    if (!fileNames.isEmpty()) {
        m_playerlist->clear();
        m_ffmpeg->SetUrl(fileNames.first());
        if (m_ffmpeg->Init()) {
            if (m_rtspThread && m_rtspThread->isRunning()) {
                m_rtspThread->quit();
                m_rtspThread->wait();
            }
            m_rtspThread = new RtspThread(m_ffmpeg, this);
            m_rtspThread->start();
        }
        QMessageBox::information(this, "成功", "文件已添加到播放列表");
    } else {
        QMessageBox::information(this, "提示", "未选择任何文件");
    }
}
//隐藏窗口按钮
void FaceXMainWindow::on_toolButton_clicked()
{
    hide();
}
//最小化按钮
void FaceXMainWindow::on_toolButton_3_clicked()
{
    showMinimized();
}
//最大化按钮
void FaceXMainWindow::on_toolButton_2_clicked()
{
    if(windowState() != Qt::WindowMaximized)
    {
        this->showMaximized();
    }
    else
    {
        this->showNormal();
    }
}
//鼠标双击最大化
void FaceXMainWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
    if(windowState() != Qt::WindowMaximized)
    {
        this->showMaximized();
    }
    else
    {
        this->showNormal();
    }
}
void FaceXMainWindow::setPlayButtonIcon(bool isPlaying)
{
    if (isPlaying) {
        ui->btn_play->setStyleSheet("border-image: url(:/image/pause.png)");
    } else {
        ui->btn_play->setStyleSheet("border-image: url(:/image/start.png)");
    }
}
//播放按钮
void FaceXMainWindow::on_btn_play_clicked()
{
    if (m_ffmpeg) {
        if (!m_rtspThread || !m_rtspThread->isRunning()) {
            if (m_ffmpeg->Init()) {
                m_rtspThread = new RtspThread(m_ffmpeg, this);
                m_rtspThread->start();
            }
        }
    } else {
        QMessageBox::critical(this, "错误", "播放器未初始化");
    }
}
//截图按钮
void FaceXMainWindow::on_btn_cut_clicked()
{
    QScreen *screen = QGuiApplication::primaryScreen();
    if (screen) {
        QPixmap screenshot = screen->grabWindow(this->winId());
        screenshot.save("screenshot.png");
        QMessageBox::information(this, "提示", "截图已保存为 screenshot.png");
    }
}
void FaceXMainWindow::SetImage(const QImage &image)
{
    qDebug() << "图像尺寸: " << image.width() << "x" << image.height();
    qDebug() << "图像格式: " << image.format();
    qDebug() << "接收到图像信号";
    if (!image.isNull()) {
        ui->label_video->setScaledContents(true); // è®¾ç½®å›¾åƒè‡ªåŠ¨ç¼©æ”¾
        ui->label_video->setPixmap(QPixmap::fromImage(image));
        ui->label_video->adjustSize();
        ui->label_video->update();
        qDebug() << "图像已更新";
    } else {
        qDebug() << "接收到的图像为空";
    }
}
Client/¶­Åìèº/code/facexmainwindow.h
New file
@@ -0,0 +1,82 @@
#ifndef FACEXMAINWINDOW_H
#define FACEXMAINWINDOW_H
#include <QMainWindow>
#include <QMouseEvent>
#include <QRect>
#include <QMediaPlayer>     //播放器
#include <QMediaPlaylist>   //播放队列
#include <QVideoWidget>     //视频显示窗口
#include "qffmpeg.h"
#include "rtspthread.h"
//必须加以下内容,否则编译不能通过,为了兼容C和C99标准
#ifndef INT64_C
#define INT64_C
#define UINT64_C
#endif
//因为#include <libavcodec/avcodec.h>是C文件,所以需要用extern
extern "C"
{
#include <libavcodec/avcodec.h>     //实现音视频的编解码功能
#include <libavformat/avformat.h>   //实现音视频文件的读取和写入功能,支持多种音视频格式
#include <libavfilter/avfilter.h>
#include <libswscale/swscale.h>
#include <libavutil/frame.h>
}
QT_BEGIN_NAMESPACE
namespace Ui {
class FaceXMainWindow;
}
QT_END_NAMESPACE
class FaceXMainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit FaceXMainWindow(QWidget *parent = 0);
    ~FaceXMainWindow();
    void mousePressEvent(QMouseEvent *event)override;
    void mouseMoveEvent(QMouseEvent *event)override;
    void mouseReleaseEvent(QMouseEvent *event)override;
    void mouseDoubleClickEvent(QMouseEvent *event)override;
private slots:
    void on_btnSelect_clicked();
    void on_toolButton_clicked();
    void on_toolButton_3_clicked();
    void on_toolButton_2_clicked();
    void on_btn_play_clicked();
    void on_btn_cut_clicked();
    void SetImage(const QImage &image);
private:
    Ui::FaceXMainWindow *ui;
    QPoint offset;
    QRect dragArea;
    bool isDragging;
    QMediaPlayer *m_player;
    QMediaPlaylist *m_playerlist;
    QVideoWidget *m_videowidget;
    QFFmpeg *m_ffmpeg;
    RtspThread *m_rtspThread;
    void setPlayButtonIcon(bool isPlaying);
};
#endif // FACEXMAINWINDOW_H
Client/¶­Åìèº/code/main.cpp
New file
@@ -0,0 +1,11 @@
#include "facexmainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    FaceXMainWindow w;
    w.show();
    return a.exec();
}
Client/¶­Åìèº/code/qffmpeg.cpp
New file
@@ -0,0 +1,113 @@
#include "qffmpeg.h"
#include <QDateTime>
#include <QDebug>
QFFmpeg::QFFmpeg(QObject *parent) :
    QObject(parent)
{
    videoStreamIndex=-1;
    av_register_all();//注册库中所有可用的文件格式和解码器
    avformat_network_init();//初始化网络流格式,使用RTSP网络流时必须先执行
    pAVFormatContext = avformat_alloc_context();//申请一个AVFormatContext结构的内存,并进行简单初始化
    pAVFrame=av_frame_alloc();
}
QFFmpeg::~QFFmpeg()
{
    if (pAVFormatContext) {
        avformat_close_input(&pAVFormatContext);
        avformat_free_context(pAVFormatContext);
    }
    if (pAVFrame) {
        av_frame_free(&pAVFrame);
    }
    if (pSwsContext) {
        sws_freeContext(pSwsContext);
    }
    avpicture_free(&pAVPicture);
}
bool QFFmpeg::Init()
{
    //打开视频流
    int result=avformat_open_input(&pAVFormatContext, url.toStdString().c_str(),NULL,NULL);
    if (result<0){
        qDebug()<<"打开视频流失败";
        return false;
    }
    //获取视频流信息
    result=avformat_find_stream_info(pAVFormatContext,NULL);
    if (result<0){
        qDebug()<<"获取视频流信息失败";
        avformat_close_input(&pAVFormatContext);
        return false;
    }
    //获取视频流索引
    videoStreamIndex = -1;
//    qDebug()<<"nb:"<<pAVFormatContext->nb_streams;
//    qDebug()<<"type:"<<pAVFormatContext->streams[0]->codec->codec_type;
//    qDebug()<<"AVMEDIA_TYPE_VIDEO:"<<AVMEDIA_TYPE_VIDEO;
    for (uint i = 0; i < pAVFormatContext->nb_streams; i++) {
        if (pAVFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStreamIndex = i;
            break;
        }
    }
    if (videoStreamIndex==-1){
        qDebug()<<"获取视频流索引失败";
        avformat_close_input(&pAVFormatContext);
        return false;
    }
    //获取视频流的分辨率大小
    pAVCodecContext = pAVFormatContext->streams[videoStreamIndex]->codec;
    videoWidth=pAVCodecContext->width;
    videoHeight=pAVCodecContext->height;
    avpicture_alloc(&pAVPicture,AV_PIX_FMT_RGB24,videoWidth,videoHeight);
    AVCodec *pAVCodec;
    //获取视频流解码器
    pAVCodec = avcodec_find_decoder(pAVCodecContext->codec_id);
    pSwsContext = sws_getContext(videoWidth,videoHeight,AV_PIX_FMT_YUV420P,videoWidth,videoHeight,AV_PIX_FMT_RGB24,SWS_BICUBIC,0,0,0);
    //打开对应解码器
    result=avcodec_open2(pAVCodecContext,pAVCodec,NULL);
    if (result<0){
        qDebug()<<"打开解码器失败";
        avpicture_free(&pAVPicture);
        sws_freeContext(pSwsContext);
        avformat_close_input(&pAVFormatContext);
        return false;
    }
    qDebug()<<"初始化视频流成功";
    return true;
}
void QFFmpeg::Play()
{
    //一帧一帧读取视频
    int frameFinished = 0;
    while (av_read_frame(pAVFormatContext, &pAVPacket) >= 0) {
        if (pAVPacket.stream_index == videoStreamIndex) {
            qDebug() << "开始解码" << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
            avcodec_decode_video2(pAVCodecContext, pAVFrame, &frameFinished, &pAVPacket);
            if (frameFinished) {
                mutex.lock();
                sws_scale(pSwsContext, (const uint8_t* const *)pAVFrame->data, pAVFrame->linesize, 0, videoHeight, pAVPicture.data, pAVPicture.linesize);
                QImage image(pAVPicture.data[0], videoWidth, videoHeight, QImage::Format_RGB888);
                QImage copyImage = image.copy(); // æ·±æ‹·è´
                emit GetImage(copyImage, this->index);
                qDebug() << "解码成功,发送图像信号";
                mutex.unlock();
            }
        }
        av_packet_unref(&pAVPacket);
    }
    avformat_close_input(&pAVFormatContext);
}
Client/¶­Åìèº/code/qffmpeg.h
New file
@@ -0,0 +1,63 @@
#ifndef QFFMPEG_H
#define QFFMPEG_H
//必须加以下内容,否则编译不能通过,为了兼容C和C99标准
#ifndef INT64_C
#define INT64_C
#define UINT64_C
#endif
//引入ffmpeg头文件
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libswscale/swscale.h>
#include <libavutil/frame.h>
}
#include <QObject>
#include <QMutex>
#include <QImage>
class QFFmpeg : public QObject
{
    Q_OBJECT
public:
    explicit QFFmpeg(QObject *parent = 0);
    ~QFFmpeg();
    bool Init();        // åˆå§‹åŒ–
    void Play();        // æ’­æ”¾
    void SetUrl(QString url){this->url=url;}    // è®¾ç½®è§†é¢‘源
    QString Url()const{return url;}
    int VideoWidth()const{return videoWidth;}
    int VideoHeight()const{return videoHeight;}
    void SetIndex(int x){this->index=x;}
private:
    QMutex mutex;
    AVPicture  pAVPicture;
    AVFormatContext *pAVFormatContext;
    AVCodecContext *pAVCodecContext;
    AVFrame *pAVFrame;
    SwsContext * pSwsContext;
    AVPacket pAVPacket;
    QString url;
    int videoWidth;
    int videoHeight;
    int videoStreamIndex;
    int index;
signals:
    void GetImage(const QImage &image,int x);    // å‘送解码后的图像信号
public slots:
};
#endif // QFFMPEG_H
Client/¶­Åìèº/code/rtspthread.cpp
New file
@@ -0,0 +1,13 @@
#include "rtspthread.h"
RtspThread::RtspThread(QFFmpeg *ffmpeg,QObject *parent) :
    QThread(parent), ffmpeg(ffmpeg)
{
}
void RtspThread::run()
{
    if (ffmpeg) {
        ffmpeg->Play();
    }
}
Client/¶­Åìèº/code/rtspthread.h
New file
@@ -0,0 +1,22 @@
#ifndef RTSPTHREAD_H
#define RTSPTHREAD_H
#include <QThread>
#include "qffmpeg.h"
class RtspThread : public QThread
{
    Q_OBJECT
public:
    explicit RtspThread(QFFmpeg *ffmpeg,QObject *parent = 0);
protected:
    void run()override;
private:
    QFFmpeg * ffmpeg;
};
#endif // RTSPTHREAD_H
Client/¶­Åìèº/log/ÈÕÖ¾_¶­Åìèº_0218.doc
Binary files differ
Client/¶­Åìèº/log/ÈÕÖ¾_¶­Åìèº_0219.doc
Binary files differ
Client/½âÀ´öÎ/log/ÈÕÖ¾_ÐÕÃû_ÈÕÆÚ.doc
Binary files differ
Client/¹ùÎÄÇ¿/document/ÐÂÐèÇó¹¦Äܹæ¸ñ˵Ã÷Êé - ¹ùÎÄÇ¿.docx
Binary files differ
Client/¹ùÎÄÇ¿/log/¹ùÎÄÇ¿_20250218.doc
Binary files differ
Client/¹ùÎÄÇ¿/log/¹ùÎÄÇ¿_20250219.doc
Binary files differ
ProjectInformation/1.2ÏîÄ¿½ø¶È±í.et
Binary files differ
Server/Ƚ¿­/document/ÈÕÖ¾_Ƚ¿­_20250218.doc
Binary files differ
Server/Ƚ¿­/log/ÈÕÖ¾_Ƚ¿­_20250219.doc
Binary files differ
Server/¬Ãô/log/ÈÕÖ¾_¬Ãô_2.18.doc
Binary files differ
Server/¬Ãô/log/ÈÕÖ¾_¬Ãô_2.19.doc
Binary files differ
Server/ÕÅÃôÀö/document/ÈÕÖ¾_ÐÕÃû_ÈÕÆÚ.doc
Binary files differ