基于Qt5.15.2进行海康相机的二次开发SDK

关于海康相机的SDK包在官网即可下载。
主要界面如下图所示。可对视频流进行实时的显示提取,对车辆进行抓拍并记录该车辆数据为json文件形式,可保存至本地等等。
在这里插入图片描述
直接贴上主要功能实现的源码:

/*widget.cpp*/
#include "widget.h"
#include "ui_widget.h"
#include<QtDebug> //调试输出到控制台头文件
#include"offlinedata.h"
#include <QTimer>
#include <QTime>
#include<QFile>
#include<QFileDialog> //保存视频能弹出来对话框

#pragma execution_character_set("utf-8"); // qDebug()可以显示汉字
//QFile   *aFile;
std::vector<std::string> vecRealTimeCamera;//实时相机数据
void writeCamera(std::vector<std::string> &vecRealTimeCamera);
QString filestr;

void CALLBACK MSesGCallback(LONG lCommand, NET_DVR_ALARMER* pAlarmer, char* pAlarmInfo, DWORD dwBufLen, void* pUser);

//构造函数
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget) //新建一个Widget界面
{
    ui->setupUi(this); //在一个指定的窗口建立起ui
    //this->resize(800,600);
    userID = -1; //默认值为注销状态
    previewID = -1; //默认关闭预览

    if(initSDK()){
        qDebug() << "SDK init Success!" << endl;
    }

    putRoad();

    cameraTimer = new QTimer;
    connect(cameraTimer,SIGNAL(timeout()),this,SLOT(extractCameraRealTime())); //执行extractCameraRealTime()函数
}

Widget::~Widget()
{
    if(initSDK()){
        qDebug() << "SDK init Success!" << endl;
    }
    delete ui;
}


void Widget::on_offlinedata_clicked()
{
    this->hide();
    offlineData *o = new offlineData(); //创建新的ui界面指针
    o->setWindowTitle("离线展示界面");
    o->show();//显示新的ui界面
}


//初始化
bool Widget::initSDK(){
    bool isok = NET_DVR_Init();
    if(isok == false){
        qDebug() << "NET_DVR_Init error;error number is " << NET_DVR_GetLastError();
        QMessageBox::warning(this, "Warning","NET_DVR_Init error;error number " + QString::number(NET_DVR_GetLastError()));//弹出提示框
        return isok;
    }

    //设置连接时间与重连时间
    NET_DVR_SetConnectTime(2000, 1);
    NET_DVR_SetReconnect(10000, true);

    return isok;
}

//退出初始化 //释放SDK资源
bool Widget::uninitSDK(){
    bool isok = NET_DVR_Cleanup();
    if(isok == false){
        qDebug() << "NET_DVR_Cleanup error;error number is " << NET_DVR_GetLastError();
        QMessageBox::warning(this, "Warning","NET_DVR_Cleanup error;error number " + QString::number(NET_DVR_GetLastError()));//弹出提示框
        return isok;
    }

    return isok;
}

long Widget::play(HWND hWnd, NET_DVR_PREVIEWINFO struPlayInfo){ //NET_DVR_PREVIEWINFO 预览
    //启动预览并设置回调数据流
    LONG lRealPlayHandle;
    
    struPlayInfo = {0};
    struPlayInfo.hPlayWnd = hWnd;         //需要SDK解码时句柄设为有效值,仅取流不解码时可设为空
    struPlayInfo.lChannel     = 1;       //预览通道号
    struPlayInfo.dwStreamType = 0;       //0-主码流,1-子码流,2-码流3,3-码流4,以此类推
    struPlayInfo.dwLinkMode   = 0;       //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
    struPlayInfo.bBlocked     = 1;       //0- 非阻塞取流,1- 阻塞取流

    lRealPlayHandle = NET_DVR_RealPlay_V40(userID, &struPlayInfo, NULL, NULL);
    if (lRealPlayHandle < 0)
    {
        qDebug() << "NET_DVR_RealPlay_V40 error;error number is " << NET_DVR_GetLastError();
        QMessageBox::warning(this, "Warning","NET_DVR_RealPlay_V40 error;error number " + QString::number(NET_DVR_GetLastError()));//弹出提示框
        NET_DVR_Logout(userID);
        userID = -1; //注销
        return -1;
    }

    return lRealPlayHandle; //lRealPlayHandle 记为 previewID
}

//登录设备
void Widget::on_LoginButton_clicked()
{
    //initSDK();
    NET_DVR_DEVICEINFO_V30 struDeviceInfo;
    memset(&struDeviceInfo, 0, sizeof(NET_DVR_DEVICEINFO_V30));//存放设备参数的结构体
    userID = NET_DVR_Login_V30(const_cast<char*>("192.168.1.66"), 8000, const_cast<char*>("admin"), const_cast<char*>("fh123456"), &struDeviceInfo);

    /*//相机参数
    char sDVRIP[20] = "192.168.1.66";
    short wDVRPort = 8000;
    char sUserName[20] = "admin";
    char sPassword[20] = "fh123456";
    //注册摄像机设备
    userID = NET_DVR_Login_V30(sDVRIP, wDVRPort, sUserName, sPassword, &struDeviceInfo);*/  

    if (userID < 0){
        qDebug() << "NET_DVR_DEVICEINFO_V30 error;error number is " << NET_DVR_GetLastError();
        QMessageBox::warning(this, "Warning","NET_DVR_DEVICEINFO_V30 error;error number " + QString::number(NET_DVR_GetLastError()));//弹出提示框
        return;
    }

    qDebug() << "Login Success , userID: " << userID << endl;
    //uninitSDK();
}

//注销设备
void Widget::on_LogoutButton_clicked()
{
    //initSDK();
    NET_DVR_Logout(userID);//注销用户
    /*if(userID != -1){
        if( NET_DVR_Logout(userID) == false){
            qDebug() << "NET_DVR_Logout error;error number is " << NET_DVR_GetLastError();
            QMessageBox::warning(this, "Warning","NET_DVR_Logout error;error number " + QString::number(NET_DVR_GetLastError()));//弹出提示框
            return;
        }
        userID = -1; //注销
    }*/

    //uninitSDK();
    qDebug() << "Logout Success!" << userID << endl;
    ui->graphicsView->close();
    //this->close();
}

//开始预览
void Widget::on_PreviewButton_clicked()
{
    if(userID == -1){
        qDebug() << "Need Login!" << endl;
        QMessageBox::warning(this , "error" , "Need Login!");
        return;
    }

    HWND hWnd = (HWND)ui->graphicsView->winId();    
    NET_DVR_PREVIEWINFO struPlayInfo;
    previewID = play(hWnd, struPlayInfo);
    if(previewID == -1){
        return;
    }

    qDebug() << "Preview Success!" << userID << endl;

    cameraTimer->start(1000);
}

//停止预览
void Widget::on_StopPreviewButton_clicked()
{
    //关闭预览
    NET_DVR_StopRealPlay(previewID);
    /*if(previewID != -1){
        if(NET_DVR_StopRealPlay(previewID) == false){
            qDebug() << "NET_DVR_StopRealPlay error;error number is " << NET_DVR_GetLastError();
            QMessageBox::warning(this, "Warning","NET_DVR_StopRealPlay error;error number " + QString::number(NET_DVR_GetLastError()));//弹出提示框
            return;
        }
        previewID = -1;
    }*/

    qDebug() << "Stop Preview Success!" << userID << endl;
    //ui->graphicsView->close();
}

void Widget::putRoad(){
    QImage Image;
    Image.load(":/images/image/Road.png");
    QPixmap pixmap = QPixmap::fromImage(Image);

    //roadLabel = ui->RoadLabel;
    //roadLabel->setPixmap(QPixmap("C:\\Users\\LLW\\Desktop\\Qt_HK_camera\\Qt_HK_camera\\image\\Road.png"));
    //roadLabel->setScaledContents(true); //设置图片自适应控件大小
    //roadLabel->resize(this->size());
    wide = ui->RoadLabel->width();
    height = ui->RoadLabel->height();
    QPixmap fitpixmap = pixmap.scaled(wide, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); //按比例缩放  饱满填充
    ui->RoadLabel->setPixmap(fitpixmap);
    //roadLabel->setScaledContents(true); //设置图片自适应控件大小
    //roadLabel->resize(wide, height);

}

/*void Widget::receiveLogin(){
    this->close();
    Widget *w = new Widget();
    w->show();
}*/

void Widget::on_savedata_clicked()
{
    QTime time=QTime::currentTime();
    QString strtime;
    strtime=time.toString("hhmmss");
    QString filestr=("C:\\Users\\LLW\\Desktop\\Qt_HK_camera\\Qt_HK_camera\\data\\20220111_"+strtime+".mp4");
    bool save_ReturnValue = NET_DVR_SaveRealData_V30(previewID, 0x1, filestr.toLocal8Bit().data());// 0x1 : PS文件层,主要用于存储,也可用于传输  // 0x2 : 3GPP文件层,用于存储
    if(save_ReturnValue == false){
        qDebug() << "NET_DVR_SaveRealData_V30 error;error number is " << NET_DVR_GetLastError();
        QMessageBox::warning(this, "Warning","NET_DVR_SaveRealData_V30 error;error number " + QString::number(NET_DVR_GetLastError()));//弹出提示框
        return;
    }

    qDebug() << "SaveRealData Success!" << userID << endl;
}

void Widget::on_stopsave_clicked()
{
    NET_DVR_StopSaveRealData(previewID);
    /*if(NET_DVR_StopSaveRealData(previewID) == false){
        qDebug() << "NET_DVR_StopSaveRealData error;error number is " << NET_DVR_GetLastError();
        QMessageBox::warning(this, "Warning","NET_DVR_StopSaveRealData error;error number " + QString::number(NET_DVR_GetLastError()));//弹出提示框
        return;
    }
    NET_DVR_StopSaveRealData(previewID) == true;*/

    qDebug() << "StopSaveRealData Success!" << userID << endl;
}

//获取相机实时视频流时间
void Widget::extractCameraRealTime(){
    //NET_DVR_TIME_V50 CameraRealTime;
    bool iRet;
    DWORD dwReturnLeo;
    NET_DVR_TIME CameraRealTime = { 0 };

    iRet = NET_DVR_GetDVRConfig(userID, NET_DVR_GET_TIMECFG, 1, &CameraRealTime, sizeof(NET_DVR_TIME), &dwReturnLeo);
    if (!iRet){
        printf("NET_DVR_GetDVRConfig NET_DVR_GET_TIMECFG error.\n");
        NET_DVR_Logout(userID);
        NET_DVR_Cleanup();
    }
    /*qDebug() <<CameraRealTime.dwYear<<"年"<<CameraRealTime.dwMonth<<"月"<<CameraRealTime.dwDay<<"日"
             <<CameraRealTime.dwHour<<":"<<CameraRealTime.dwMinute<<":"<<CameraRealTime.dwSecond<<endl;*/

    return;
}

//普通string类型转UTF-8编码格式字符串
std::string string_To_UTF8(const std::string& str)
{
    int nwLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);

    wchar_t* pwBuf = new wchar_t[nwLen + 1];//一定要加1,不然会出现尾巴
    ZeroMemory(pwBuf, nwLen * 2 + 2);

    ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length(), pwBuf, nwLen);

    int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL);

    char* pBuf = new char[nLen + 1];
    ZeroMemory(pBuf, nLen + 1);

    ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);

    std::string retStr(pBuf);

    delete[]pwBuf;
    delete[]pBuf;

    pwBuf = NULL;
    pBuf = NULL;

    return retStr;
}

//报警回调函数及HTTP传输
void CALLBACK MSesGCallback(LONG lCommand, NET_DVR_ALARMER* pAlarmer, char* pAlarmInfo, DWORD dwBufLen, void* pUser)
{
    //qDebug()<<"MSesGCallback Succeed!";
    int i = 0;
    char filename[100];
    FILE *fSnapPic = NULL;
    FILE *fSnapPicPlate = NULL;
    //cout << "lCommand=" << lCommand << endl;

    switch (lCommand)
    {
    //交通抓拍结果(新报警消息)
        case COMM_ITS_PLATE_RESULT:
        {
            NET_ITS_PLATE_RESULT struITSPlateResult = { 0 };

            memcpy(&struITSPlateResult, pAlarmInfo, sizeof(struITSPlateResult));

            //数据内容
            //将字符串通过指针存入容器
            char zuodakuohao[100] = { 0 };
            sprintf(zuodakuohao, "{");
            vecRealTimeCamera.push_back(zuodakuohao);
            char hkDataTime[100] = { 0 };
            sprintf(hkDataTime, "\"snapFirstPicTime\":\"%02d-%02d-%02d% 02d:%02d:%02d:%02d\",", struITSPlateResult.struSnapFirstPicTime.wYear, struITSPlateResult.struSnapFirstPicTime.byMonth, struITSPlateResult.struSnapFirstPicTime.byDay, struITSPlateResult.struSnapFirstPicTime.byHour, struITSPlateResult.struSnapFirstPicTime.byMinute, struITSPlateResult.struSnapFirstPicTime.bySecond, struITSPlateResult.struSnapFirstPicTime.wMilliSec);
            vecRealTimeCamera.push_back(hkDataTime);
            char hkcarNum[100] = { 0 };
            sprintf(hkcarNum, "\"carNum\":\"%s\",", struITSPlateResult.struPlateInfo.sLicense);
            qDebug()<<hkcarNum<<endl;std::string carNum = string_To_UTF8((std::string) hkcarNum);
            vecRealTimeCamera.push_back(carNum);
            char hkcarType[100] = { 0 };
            sprintf(hkcarType, "\"carType\":\"%d\",", struITSPlateResult.byVehicleType);
            vecRealTimeCamera.push_back(hkcarType);
            char hkdriveChan[100] = { 0 };
            sprintf(hkdriveChan, "\"driveChan\":\"%d\",", struITSPlateResult.byDriveChan);
            vecRealTimeCamera.push_back(hkdriveChan);
            char hkcarDirectiontype[100] = { 0 };
            sprintf(hkcarDirectiontype, "\"carDirectiontype\":\"%d\",", struITSPlateResult.byCarDirectionType);
            vecRealTimeCamera.push_back(hkcarDirectiontype);
            char hkcarSpeed[100] = { 0 };
            sprintf(hkcarSpeed, "\"carSpeed\":\"%d\",", struITSPlateResult.struVehicleInfo.wSpeed);
            vecRealTimeCamera.push_back(hkcarSpeed);
            char hkPilotSafebelt[100] = { 0 };
            sprintf(hkPilotSafebelt, "\"PilotSafebelt\":\"%d\",", struITSPlateResult.byPilotSafebelt);
            vecRealTimeCamera.push_back(hkPilotSafebelt);
            char hkIllegalType[100] = { 0 };
            sprintf(hkIllegalType, "\"IllegalType\":\"%d\",", struITSPlateResult.wIllegalType);
            vecRealTimeCamera.push_back(hkIllegalType);
            char hkPendant[100] = { 0 };
            sprintf(hkPendant, "\"byPendant\":\"%d\",", struITSPlateResult.byPendant);
            vecRealTimeCamera.push_back(hkPendant);
            char hkCopilotSafebelt[100] = { 0 };
            sprintf(hkCopilotSafebelt, "\"CopilotSafebelt\":\"%d\",", struITSPlateResult.byCopilotSafebelt);
            vecRealTimeCamera.push_back(hkCopilotSafebelt);
            char hkPilotCall[100] = { 0 };
            sprintf(hkPilotCall, "\"PilotCall\":\"%d\",", struITSPlateResult.byPilotCall);
            vecRealTimeCamera.push_back(hkPilotCall);
            char hkCarColor[100] = { 0 };
            sprintf(hkCarColor, "\"CarColor\":\"%d\",", struITSPlateResult.struVehicleInfo.byColor);
            vecRealTimeCamera.push_back(hkCarColor);
            char hkVehicleLogoRecog[100] = { 0 };
            sprintf(hkVehicleLogoRecog, "\"VehicleLogoRecog\":\"%d\"", struITSPlateResult.struVehicleInfo.byVehicleLogoRecog);
            vecRealTimeCamera.push_back(hkVehicleLogoRecog);
            char youdakuohao[100] = { 0 };
            sprintf(youdakuohao, "}");
            vecRealTimeCamera.push_back(youdakuohao);
            /*sprintf(youdakuohao, "},");
            vecRealTimeCamera.push_back(youdakuohao);*/
            writeCamera(vecRealTimeCamera);

            /*QString str=postParams;//
            QByteArray  strBytes=str.toUtf8();//转换为字节数组
            aFile->write(strBytes,strBytes.length());*/  //写入文件

/*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            for (i = 0; i < struITSPlateResult.dwPicNum; i++)
            {
                //保存场景图
                //struITSPlateResult.struPicInfo[i].byType:数据类型,0 - 车牌图、1 - 场景图、2 - 合成图、3 - 特写图
                //struPicInfo[6];    //图片信息,单张回调,最多6张图,由序号区分
                if ((struITSPlateResult.struPicInfo[i].dwDataLen != 0) && (struITSPlateResult.struPicInfo[i].byType == 1) || (struITSPlateResult.struPicInfo[i].byType == 2))
                {
                    sprintf(filename, "C:\\Users\\LLW\\Desktop\\Qt_HK_camera\\Qt_HK_camera\\pic\\%s_%d.jpg", struITSPlateResult.struPlateInfo.sLicense, i);
                    fSnapPic = fopen(filename, "wb");   //w是以文本方式打开文件,wb是二进制方式打开文件
                    fwrite(struITSPlateResult.struPicInfo[i].pBuffer, struITSPlateResult.struPicInfo[i].dwDataLen, 1, fSnapPic);
                    //iNum++;
                    fclose(fSnapPic);
                }

                //车牌图
                if (struITSPlateResult.struPicInfo[i].dwDataLen != 0 && struITSPlateResult.struPicInfo[i].byType == 0)
                {
                    sprintf(filename, "C:\\Users\\LLW\\Desktop\\Qt_HK_camera\\Qt_HK_camera\\pic\\%s.jpg", struITSPlateResult.struPlateInfo.sLicense);
                    fSnapPicPlate = fopen(filename, "wb");
                    fwrite(struITSPlateResult.struPicInfo[i].pBuffer, struITSPlateResult.struPicInfo[i].dwDataLen, 1, fSnapPicPlate);
                    //iNum++;
                    fclose(fSnapPicPlate);
                }
            }	//其他信息处理
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/

            break;
        }
        default:
        {
            //std::cout << lCommand << endl;
            break;
        }
    }

    return;
}

void Widget::SetMessageCallBack(){
    qDebug()<<"SetMessageCallBack Succeed!";

    NET_DVR_SetDVRMessageCallBack_V30(MSesGCallback, NULL);//设置报警回调函数
    if(NET_DVR_SetDVRMessageCallBack_V30(MSesGCallback, NULL) == false){
            qDebug() << "NET_DVR_SetDVRMessageCallBack_V30 error;error number is " << NET_DVR_GetLastError();
            QMessageBox::warning(this, "Warning","NET_DVR_SetDVRMessageCallBack_V30 error;error number " + QString::number(NET_DVR_GetLastError()));//弹出提示框
            return;
        }

    return;
}

void Widget::on_SetupAlarm_clicked()
{
    //文件命名
    QTime time=QTime::currentTime();
    QString strtime;
    strtime=time.toString("hhmmss");
    filestr="C:\\Users\\LLW\\Desktop\\Qt_HK_camera\\Qt_HK_camera\\data\\20220111_"+strtime+"camera.txt";

    //启用布防
   NET_DVR_SETUPALARM_PARAM struSetupParam = { 0 };
   struSetupParam.dwSize = sizeof(NET_DVR_SETUPALARM_PARAM);
   struSetupParam.byLevel = 1; //布防优先级:0- 一等级(高),1- 二等级(中)
   struSetupParam.byAlarmInfoType = 1; //上传报警信息类型: 0- 老报警信息(NET_DVR_PLATE_RESULT), 1- 新报警信息(NET_ITS_PLATE_RESULT)

   lHandle = NET_DVR_SetupAlarmChan_V41(userID, &struSetupParam);
   if (lHandle < 0)
   {
       qDebug()<<"NET_DVR_SetupAlarmChan_V41 failed, error code: %d\n"<< NET_DVR_GetLastError();
       QMessageBox::warning(this, "Warning","NET_DVR_SetupAlarmChan_V41 error;error number " + QString::number(NET_DVR_GetLastError()));//
       NET_DVR_Logout(userID);
       NET_DVR_Cleanup();
       return;
   }
   else
   {
       qDebug()<<"SetupAlarm Succeed!\n";
   }

/*------------------------------------------------------------------------------------------------------------------------------------------------------------*/
   NET_DVR_SetDVRMessageCallBack_V30(MSesGCallback, NULL);//设置报警回调函数
   if(NET_DVR_SetDVRMessageCallBack_V30(MSesGCallback, NULL) == false){
           qDebug() << "NET_DVR_SetDVRMessageCallBack_V30 error;error number is " << NET_DVR_GetLastError();
           QMessageBox::warning(this, "Warning","NET_DVR_SetDVRMessageCallBack_V30 error;error number " + QString::number(NET_DVR_GetLastError()));//
           return;
       }
   //Sleep(60000);
}

void Widget::on_CloseAlarm_clicked()
{
//撤销布防上传通道
    NET_DVR_CloseAlarmChan_V30(lHandle);

    /*if (NET_DVR_CloseAlarmChan_V30(lHandle) == false) //布防句柄lHandle
    {
        qDebug() << "NET_DVR_CloseAlarmChan_V30 Failed! Error number:" << NET_DVR_GetLastError();
        QMessageBox::warning(this, "Warning","NET_DVR_CloseAlarmChan_V30 error;error number " + QString::number(NET_DVR_GetLastError()));//
        NET_DVR_Logout(userID);
        NET_DVR_Cleanup();
        return;
    }*/

    lHandle = -1;//布防句柄

    qDebug()<<"CloseAlarm Succeed!\n";
}

void Widget::on_Exit_clicked()
{
    this->close();
}

/*void Widget::on_savejson_clicked()
{
    if(ui->savejson->text()=="SaveJson")
    {
        ui->savejson->setText("Stop");
    QString curPath=QDir::currentPath();//获取系统当前目录
    QString dlgTitle="存为一个文件"; //对话框标题
    QString filter="文本文件(*.txt);;所有文件(*.*)"; //文件过滤器
    QString aFileName=QFileDialog::getSaveFileName(this,dlgTitle,curPath,filter);

    if (aFileName.isEmpty())
        return;
    //aFile->copy(aFileName);
    aFile = new QFile(aFileName);
    QString str="postParams";//整个内容作为字符串

    QByteArray  strBytes=str.toUtf8();//转换为字节数组

    aFile->write(strBytes,strBytes.length());  //写入文件
  }
    else
    {
        ui->savejson->setText("SaveJson");
        aFile->close();

    }

}*/

void writeCamera(std::vector<std::string> &vecRealTimeCamera) {

    // 写入文件保存
    FILE *fp;
    fp = fopen(filestr.toLocal8Bit().data(), "w");//打开要输出的文件  w:每次覆盖原文件

    for (int i = 0; i < vecRealTimeCamera.size(); i++) {
        fwrite(&vecRealTimeCamera[i][0], 1, vecRealTimeCamera[i].size(), fp);
        fwrite("\n", 1, 1, fp);
    }

    fclose(fp);

}

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wrdoct

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值