【id:120】【25分】B. 虚拟电话(构造与析构)

该文描述了一个使用C++编程语言,基于面向对象思想实现的虚拟电话系统。系统包括电话号码类和虚拟电话类,支持构造、析构、打印和查询等操作。用户可以创建电话对象并进行查询,如果号码存在于电话对象中,则打印电话信息;否则输出错误提示。

时间限制
1s
内存限制
128MB
题目描述

虚拟电话包含属性:电话号、状态、机主姓名。
1、电话号是一个类,它包含号码和类型,其中号码是整数类型,类型用单个字母表示用户类别,A表示政府,B表示企业、C表示个人。类操作包括构造、属性的获取和设置等方法,根据需要自行编写。
2、状态用一个数字表示,1表示在用,0表示未用,
3、机主姓名是一个字符串

电话操作包括:构造、析构、打印和查询。
1、构造函数需要考虑复合类成员的构造,并且输出提示信息。假设电话号码为12345678,则构造函数输出"12345678 constructed."
2、打印是输出电话的相关信息,其中如果电话状态是在用则输出use;状态是未用则输出unuse,输出格式看示例。
3、析构函数是输出提示信息。假设电话号为12345678,则析构函数输出"12345678 destructed. "
4、查询操作是根据给定的号码查询电话,如果电话自身号码和给定号码不相同,则返回0;如果电话自身号码和给定号码相同,则返回1

用C++和面向对象思想实现以下要求:
1、输入相关数据,创建三个电话对象,并通过构造方法初始化。
2、输入若干个电话号码,通过查询操作查询这些号码是否在三个电话对象中,如果不存在输出"wrong number.",存在则调用打印操作输出电话信息,具体看输出样例。

输入

头三行输入三个电话信息,每行包括电话号码、电话类型、状态、机主姓名,机主姓名最大长度为20

接着一行输入t,表示有t个号码要查询

接着t行输入t个电话号码

输出

t行输出t个号码的查询结果

#include <iostream>
#include <cstring>
using namespace std;

class PhoneNum
{
private:
    int number;
    char type;
public:
    PhoneNum(int n, char t)
    {
        number = n;
        type = t;
        cout << number << " constructed." << endl;
    }
    int getNumber() const { return number; }
    void setNumber(int n) { number = n; }
    char getType() const { return type; }
    void setType(char t) { type = t; }
};

class VirtualPhone
{
private:
    PhoneNum phoneNum;
    int state;
    char owner[21];
public:
    VirtualPhone(int n, char t, int s, const char *o): phoneNum(n, t)
    {
        state = s;
        strcpy(owner, o);
    }
    ~VirtualPhone()
    {
        cout << phoneNum.getNumber() << " destructed." << endl;
    }
    void print() const
    {
        cout << "Phone=" << phoneNum.getNumber() << "--Type=" << phoneNum.getType() << "--State=";
        if (state)
        {
            cout << "use";
        }
        else
        {
            cout << "unuse";
        }
        cout << "--Owner=" << owner << endl;
    }
    int query(int n) const
    {
        if (phoneNum.getNumber() == n)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
};

int main()
{
    int n1, n2, n3;
    char t1, t2, t3;
    int s1, s2, s3;
    char o1[21], o2[21], o3[21];
    cin >> n1 >> t1 >> s1 >> o1;
    cin >> n2 >> t2 >> s2 >> o2;
    cin >> n3 >> t3 >> s3 >> o3;
    VirtualPhone vp1(n1, t1, s1, o1);
    VirtualPhone vp2(n2, t2, s2, o2);
    VirtualPhone vp3(n3, t3, s3, o3);
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        int q1 = vp1.query(n);
        int q2 = vp2.query(n);
        int q3 = vp3.query(n);
        if (q1 == 0 && q2 == 0 && q3 == 0)
        {
            cout << "wrong number." << endl;
        }
        else
        {
            if (q1)
            {
                vp1.print();
            }
            if (q2)
            {
                vp2.print();
            }
            if (q3)
            {
                vp3.print();
            }
        }
    }
    return 0;
}

#include "form_voip.h" #include "ui_form_voip.h" #include "parammanager.h" #include <QPushButton> #include <QMediaDevices> #include <QVideoSink> #include <QCamera> #include <QTimer> #include <QAudioInput> #include <QVideoFrame> using namespace pj; class MyEndpoint : public Endpoint { public: MyEndpoint() : Endpoint() {}; virtual pj_status_t onCredAuth(OnCredAuthParam &prm) { PJ_UNUSED_ARG(prm); std::cout << "*** Callback onCredAuth called ***" << std::endl; /* Return PJ_ENOTSUP to use * pjsip_auth_create_aka_response()/<b>libmilenage</b> (default), * if PJSIP_HAS_DIGEST_AKA_AUTH is defined. */ return PJ_ENOTSUP; } }; class MyAccount; class MyAudioMediaPort: public AudioMediaPort { virtual void onFrameRequested(MediaFrame &frame) { // Give the input frame here frame.type = PJMEDIA_FRAME_TYPE_AUDIO; // frame.buf.assign(frame.size, 'c'); } virtual void onFrameReceived(MediaFrame &frame) { PJ_UNUSED_ARG(frame); // Process the incoming frame here } }; class MyCall : public Call { private: MyAccount *myAcc; AudioMediaPlayer *wav_player; AudioMediaPort *med_port; // 视频窗口指针 VideoWindow *localVideoWin = nullptr; VideoWindow *remoteVideoWin = nullptr; public: MyCall(Account &acc, int call_id = PJSUA_INVALID_ID) : Call(acc, call_id) { wav_player = NULL; med_port = NULL; myAcc = (MyAccount *)&acc; } ~MyCall() { if (wav_player) delete wav_player; if (med_port) delete med_port; // 清理视频窗口 if (localVideoWin) delete localVideoWin; if (remoteVideoWin) delete remoteVideoWin; } virtual void onCallState(OnCallStateParam &prm); virtual void onCallTransferRequest(OnCallTransferRequestParam &prm); virtual void onCallReplaceRequest(OnCallReplaceRequestParam &prm); virtual void onCallMediaState(OnCallMediaStateParam &prm); }; class MyAccount : public Account { public: std::vector<Call *> calls; public: MyAccount() {} ~MyAccount() { std::cout << "*** Account is being deleted: No of calls=" << calls.size() << std::endl; for (std::vector<Call *>::iterator it = calls.begin(); it != calls.end(); ) { delete (*it); it = calls.erase(it); } } void removeCall(Call *call) { for (std::vector<Call *>::iterator it = calls.begin(); it != calls.end(); ++it) { if (*it == call) { calls.erase(it); break; } } } //‌ OnRegStateParam ‌是 PJSUA2 开发框架中的一个参数类,用于处理注册状态的变化。当帐户的注册状态发生变化时, Account 类会触发一个事件,该事件传递 OnRegStateParam 参数,其中包含了注册状态变化的详细信息。 virtual void onRegState(OnRegStateParam &prm) { AccountInfo ai = getInfo(); std::cout << (ai.regIsActive? "***** Register: code=" : "***** Unregister: code=") << prm.code << std::endl; } virtual void onIncomingCall(OnIncomingCallParam &iprm) { Call *call = new MyCall(*this, iprm.callId); CallInfo ci = call->getInfo(); CallOpParam prm; std::cout << "*** Incoming Call: " << ci.remoteUri << " [" << ci.stateText << "]" << std::endl; calls.push_back(call); prm.statusCode = (pjsip_status_code)200; // 2.15.1 必须设置这两个参数 prm.opt.audioCount = 1; prm.opt.videoCount = 1; //prm.opt.audioDir = PJMEDIA_DIR_ENCODING_DECODING; //prm.opt.videoDir = PJMEDIA_DIR_ENCODING_DECODING; prm.opt.mediaDir.push_back(PJMEDIA_DIR_ENCODING_DECODING); // 音频 prm.opt.mediaDir.push_back(PJMEDIA_DIR_ENCODING_DECODING); // 视频 call->answer(prm); //currentCall = call; // 启动来电铃声 // pjmedia_tonegen_param toneParam; // pj_bzero(&toneParam, sizeof(toneParam)); // toneParam.flags = PJMEDIA_TONEGEN_LOOP; // toneParam.tones[0].freq1 = 440; // 音频频率 // toneParam.tones[0].duration = -1; // 持续时间(无限循环) // pjmedia_tonegen_create(pjsua_get_media_transport(), NULL, &toneParam, &call->ringToneId); //使用 pjmedia_tonegen_create 函数创建一个音频生成器来播放铃声。上述代码中,ringToneId 是生成的铃声音频流 ID,用于后续停止铃声 //当用户点击接听按钮时,可以通过调用 answer 方法接听电话,并停止铃声播放 // void MyCall::answerCall() { // CallOpParam answer_op; // answer_op.statusCode = PJSIP_SC_OK; // 使用 200 OK 表示接听电话 // answer(answer_op); // // 停止来电铃声 // if (ringToneId != PJMEDIA_TONEGEN_ID_INVALID) { // pjmedia_tonegen_destroy(ringToneId); // ringToneId = PJMEDIA_TONEGEN_ID_INVALID; // } // PJ_LOG(3, (pj_get_log_writer(), "Call answered!")); // } // 假设这是 GUI 框架中的按钮点击事件处理函数 // void onAnswerButtonClicked(MyCall *call) { // call->answerCall(); // } } // void MyAccount::onIncomingCall(OnIncomingCallParam &iprm) { // PJ_LOG(3, (pj_get_log_writer(), "Incoming call from %.*s!", // (int)iprm.rinfo.fromUri.slen, iprm.rinfo.fromUri.ptr)); // // 创建一个新的 Call 对象 // MyCall *call = new MyCall(*this, iprm.callId); // // 启动来电铃声 // pjmedia_tonegen_param toneParam; // pj_bzero(&toneParam, sizeof(toneParam)); // toneParam.flags = PJMEDIA_TONEGEN_LOOP; // toneParam.tones[0].freq1 = 440; // 音频频率 // toneParam.tones[0].duration = -1; // 持续时间(无限循环) // pjmedia_tonegen_create(pjsua_get_media_transport(), NULL, &toneParam, &call->ringToneId); // } }; //condition_variable cv; void MyCall::onCallState(OnCallStateParam &prm) { PJ_UNUSED_ARG(prm); CallInfo ci = getInfo(); std::cout << "*** Call: " << ci.remoteUri << " [" << ci.stateText << "]" << std::endl; if (ci.state == PJSIP_INV_STATE_DISCONNECTED) { //myAcc->removeCall(this); /* Delete the call */ //delete this; } else if (ci.state == PJSIP_INV_STATE_CONFIRMED) { std::cout << "*** 通话已连接" << std::endl; //callConnected = true; //cv.notify_one(); } } // 创建视频窗口 // void createVideoWindows(int med_idx) { // // 获取视频媒体 // VideoMedia vid_med = getVideoMedia(med_idx); // // 创建本地预览窗口 // VideoPreviewOpParam pre_param; // VideoWindowHandle wnd_handle; // try { // VideoPreview *preview = vid_med.startPreview(pre_param); // VideoWindow win = preview->getVideoWindow(); // win.Show(true); // cout << "本地预览窗口ID: " << win.getInfo().winId << endl; // } catch(Error& err) { // cout << "预览窗口错误: " << err.info() << endl; // } // // 创建远程视频窗口 // try { // VideoWindow remote_win = vid_med.getRemoteVideoWindow(); // remote_win.Show(true); // cout << "远程视频窗口ID: " << remote_win.getInfo().winId << endl; // } catch(Error& err) { // cout << "远程窗口错误: " << err.info() << endl; // } // } void MyCall::onCallMediaState(OnCallMediaStateParam &prm) { PJ_UNUSED_ARG(prm); CallInfo ci = getInfo(); AudioMedia aud_med; AudioMedia& play_dev_med = MyEndpoint::instance().audDevManager().getPlaybackDevMedia(); qDebug()<<"AudioMedia& play_dev_med = MyEndpoint::instance().audDevManager().getPlaybackDevMedia();"; try { // Get the first audio media aud_med = getAudioMedia(-1); } catch(...) { std::cout << "Failed to get audio media" << std::endl; return; } if (!wav_player) { wav_player = new AudioMediaPlayer(); try { wav_player->createPlayer( "./closecall.wav", 0); std::cout << "Success opened wav file" << std::endl; } catch (...) { std::cout << "Failed opening wav file" << std::endl; delete wav_player; wav_player = NULL; } } // This will connect the wav file to the call audio media if (wav_player) wav_player->startTransmit(aud_med); if (!med_port) { med_port = new MyAudioMediaPort(); MediaFormatAudio fmt; fmt.init(PJMEDIA_FORMAT_PCM, 16000, 1, 20000, 16); med_port->createPort("med_port", fmt); // Connect the media port to the call audio media in both directions med_port->startTransmit(aud_med); aud_med.startTransmit(*med_port); } // And this will connect the call audio media to the sound device/speaker aud_med.startTransmit(play_dev_med); // 遍历媒体流 for (unsigned i = 0; i < ci.media.size(); i++) { if (ci.media[i].type == PJMEDIA_TYPE_VIDEO) { // 获取视频媒体流 VideoMedia *vid_med = (VideoMedia *)getMedia(i); if (vid_med) { // 创建本地预览窗口 try { VideoPreviewOpParam pre_param; VideoPreview *preview = vid_med->startPreview(pre_param); localVideoWin = new VideoWindow(preview->getVideoWindow()); localVideoWin->Show(true); //std::cout << "本地预览窗口句柄: " << localVideoWin->getInfo().hwnd << std::endl; } catch(Error& err) { std::cout << "预览窗口错误: " << err.info() << std::endl; } // 创建远程视频窗口 try { remoteVideoWin = new VideoWindow(vid_med->getRemoteVideoWindow()); remoteVideoWin->Show(true); //std::cout << "远程窗口句柄: " << remoteVideoWin->getInfo().hwnd << std::endl; } catch(Error& err) { std::cout << "远程窗口错误: " << err.info() << std::endl; } } } } } void MyCall::onCallTransferRequest(OnCallTransferRequestParam &prm) { /* Create new Call for call transfer */ prm.newCall = new MyCall(*myAcc); } void MyCall::onCallReplaceRequest(OnCallReplaceRequestParam &prm) { /* Create new Call for call replace */ prm.newCall = new MyCall(*myAcc); } Form_VoIP::Form_VoIP(QWidget *parent) : QWidget(parent) , ui(new Ui::Form_VoIP) { ui->setupUi(this); this->resize(1680,920); // 初始化GStreamer qputenv("GST_DEBUG", "2"); // 设置GStreamer调试级别 qputenv("GST_PLUGIN_PATH", "/usr/lib/gstreamer-1.0"); // 设置插件路径 connect(ui->registerButton, &QPushButton::clicked, this, &Form_VoIP::onRegisterClicked); connect(ui->unregisterButton, &QPushButton::clicked, this, &Form_VoIP::onUnRegisterClicked); connect(ui->callButton, &QPushButton::clicked, this, &Form_VoIP::onCallClicked); connect(ui->hangupButton, &QPushButton::clicked, this, &Form_VoIP::onHangupClicked); connect(this, &Form_VoIP::callStateChanged, this, &Form_VoIP::onCallStateChanged); connect(this, &Form_VoIP::incomingVideoFrame, this, &Form_VoIP::handleIncomingFrame); connect(this, &Form_VoIP::outgoingVideoFrame, this, &Form_VoIP::handleOutgoingFrame); } Form_VoIP::~Form_VoIP() { // if (registered) { // this->setRegistration(false); // } ep.libDestroy(); delete ui; } void Form_VoIP::PJSUA2_Init() { if (ep.libGetState() == PJSUA_STATE_NULL) { ep.libCreate(); // 安全调用 } else { return; } qDebug() << "ep.libCreate(); 完成"; pj::EpConfig ep_cfg; ep_cfg.logConfig.level = 5; // 关键设置:启用视频支持 ep_cfg.medConfig.vidCount = 1; // 必须 >=1 才能支持视频 ep_cfg.medConfig.hasIoqueue = true; ep_cfg.medConfig.clockRate = 16000; ep_cfg.medConfig.quality = 10; ep_cfg.medConfig.ecOptions = PJMEDIA_ECHO_USE_SW_ECHO; // 视频配置 - 根据您的ffmpeg参数调整 ep_cfg.vidConfig.capDev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV; ep_cfg.vidConfig.rendDev = PJMEDIA_VID_DEFAULT_RENDER_DEV; ep_cfg.vidConfig.autoShowIncoming = true; ep_cfg.vidConfig.autoTransmitOutgoing = true; // 设置视频格式参数(您的ffmpeg命令匹配) MediaFormatVideo videoFormat; videoFormat.init(PJMEDIA_FORMAT_RGB24, // formatId 640, // width 480, // height 30, // fpsNum (帧率) 1, // fpsDenum (帧率) 0, // avgBps (平均比特率,0=自动) 0); // maxBps (最大比特率,0=自动) // 将视频格式应用到配置中 ep_cfg.medConfig.videoConfig.defaultCaptureFormat = videoFormat.toPj(); // 检查是否已执行 libCreate // 检查是否已执行 libInit if (ep.libGetState() == PJSUA_STATE_CREATED) { qDebug() << "开始 ep.libInit(ep_cfg)"; ep.libInit(ep_cfg); } else { return; } qDebug() << "配置视频编解码器参数"; // 配置视频编解码器参数 VidCodecParam vid_codec_param; vid_codec_param.ignoreFmtp = true; // 优先使用H264,如果没有则使用VP8 try { ep.videoCodecSetPriority("H264/97/98", PJMEDIA_CODEC_PRIO_HIGHEST); ep.setVideoCodecParam("H264", vid_codec_param); } catch (...) { qDebug() << "H264编解码器不可用,尝试VP8"; ep.videoCodecSetPriority("VP8", PJMEDIA_CODEC_PRIO_HIGHEST); } qDebug() << "传输配置"; // 传输配置 pj::TransportConfig tcfg; tcfg.port = 5060; ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg); // 设置默认设备 // ep.audDevManager().setCaptureDev(1); // ep.audDevManager().setPlaybackDev(1); //ep.audDevManager().refreshDevs();// 刷新设备列表 qDebug() << "检查设备"; // 检查设备 if (ep.audDevManager().getDevCount() == 0) { qDebug() << "无音频设备,加载虚拟设备..."; // 调用系统命令加载 snd-dummy(需提前配置) system("sudo modprobe snd-dummy"); ep.audDevManager().refreshDevs(); // 重新刷新 } else { qDebug() << "音频设备数量:"<<ep.audDevManager().getDevCount(); AudioDevInfoVector2 aud_devices = ep.audDevManager().enumDev2(); qDebug() << aud_devices.size(); for (int i = 0; i < aud_devices.size(); i++) { qDebug()<< "设备 " << i << ": " << aud_devices[i].name << " (输入通道: " << aud_devices[i].inputCount << ", 输出通道: " << aud_devices[i].outputCount << ")\n"; } } // ep.audDevManager().setCaptureDev(3); // ep.audDevManager().setPlaybackDev(3); ep.vidDevManager().refreshDevs(); // 手动刷新设备枚举 if (ep.vidDevManager().getDevCount() == 0) { qDebug() << "无视频设备,..."; } else { qDebug() << "视频设备数量:"<<ep.vidDevManager().getDevCount(); } // 获取当前音频设备参数 // AudDevMgr::Param param = ep.audDevMgr().getParam(); // // 设置输入和输出设备ID // param.input_dev = inputDevId; // param.output_dev = outputDevId; // try { // // 应用新的音频设备参数 // ep.audDevMgr().setParam(param); // std::cout << "Successfully configured audio devices with IDs: " // << inputDevId << " (input), " << outputDevId << " (output)." << std::endl; // } catch (Error &err) { // std::cerr << "Failed to set audio device settings: " << err.info() << std::endl; // } // 启动 PJSUA2 ep.libStart(); // 新增自动注册逻辑 // QTimer::singleShot(1000, this, [this]() { // auto& paramMgr = ParamManager::instance(); // QString server = paramMgr.getParamValue("SIP", "Server").toString(); // QString user = paramMgr.getParamValue("SIP", "User").toString(); // QString pass = paramMgr.getParamValue("SIP", "Password").toString(); // if(!server.isEmpty() && !user.isEmpty()) { // registerAccount(server, user, pass); // } // }); } void Form_VoIP::showEvent(QShowEvent *event) { Q_UNUSED(event); qDebug()<<"Form_VoIP::showEvent"; PJSUA2_Init(); //qDebug() << "音频设备数量:"<<ep.audDevManager().getDevCount(); } void Form_VoIP::startStream() { if (streaming) return; streaming = true; VideoDevice::init(); AudioDevice::init(); // 启动视频采集定时器 QTimer *videoTimer = new QTimer(this); connect(videoTimer, &QTimer::timeout, [this](){ QVideoFrame frame = VideoDevice::captureFrame(); if (frame.isValid()) { emit outgoingVideoFrame(frame); } }); videoTimer->start(33); // ~30fps // 音频流处理类似... } void Form_VoIP::stopStream() { if (!streaming) return; streaming = false; VideoDevice::cleanup(); AudioDevice::cleanup(); } void Form_VoIP::handleIncomingFrame(const QVideoFrame &frame) { // 处理接收到的视频帧 // 这里可以添加解码和显示逻辑 } void Form_VoIP::handleOutgoingFrame(const QVideoFrame &frame) { // 处理要发送的视频帧 // 这里可以添加编码和网络传输逻辑 } void Form_VoIP::onRegisterClicked() { m_sipserver = ui->serverEdit->text(); m_sipuser = ui->userEdit->text(); m_sippassword = ui->passwordEdit->text(); registerAccount(m_sipserver, m_sipuser, m_sippassword); } void Form_VoIP::onUnRegisterClicked() { try { if(!acc)return; // 获取已注册的账户对象 Account& account = *acc; // 发送Expires=0的REGISTER请求(注销) account.setRegistration(false); // 核心注销指令 若Account已经注销,则该方法会报错 } catch (pj::Error &e) { std::cerr << "PJSIP Error: " << e.reason << std::endl; // 输出具体错误:ml-citation{ref="4" data="citationList"} } } void Form_VoIP::onCallClicked() { QString number = ui->numberEdit->text(); //makeCall(number); if (currentCall) return; // Make outgoing call Call *call = new MyCall(*acc); acc->calls.push_back(call); CallOpParam prm(true); prm.opt.audioCount = 1; prm.opt.videoCount = 1; // 带视频呼叫 // 设置媒体方向(2.15.1 要求) //prm.opt.audioDir = PJMEDIA_DIR_ENCODING_DECODING; //prm.opt.videoDir = PJMEDIA_DIR_ENCODING_DECODING; prm.opt.mediaDir.push_back(PJMEDIA_DIR_ENCODING_DECODING); // 音频 prm.opt.mediaDir.push_back(PJMEDIA_DIR_ENCODING_DECODING); // 视频 const QString uri = QString("sip:%1@%2").arg(number).arg(m_sipserver); try { call->makeCall(uri.toStdString(), prm); currentCall = call; qDebug() << "发起视频呼叫:" << uri; } catch (pj::Error &e) { delete call; currentCall = nullptr; std::cerr << "PJSIP Error: " << e.reason << std::endl; // 输出具体错误:ml-citation{ref="4" data="citationList"} qDebug() << "呼叫失败:" << e.reason.c_str(); } // pj::Call *call = new pj::Call(*this); // pj::CallOpParam prm(true); // currentCall = call; // try { // call->makeCall("sip:" + uri.toStdString(), prm); // emit callStateChanged(1); // 连接中 // } catch (...) { // delete call; // currentCall = nullptr; // emit callStateChanged(0); // 空闲状态 // } } void Form_VoIP::onHangupClicked() { //hangup(); currentCall = nullptr; ep.hangupAllCalls(); } void Form_VoIP::onCallStateChanged(int state) { // 更新界面状态 // 0: 空闲, 1: 连接中, 2: 通话中 if (state == 2) { // 通话中 setupVideoCall(); } //有拨号界面时,关闭拨号 // else if (callWindow) { // callWindow->close(); // callWindow = nullptr; // } } void Form_VoIP::setupVideoCall() { // if (!callWindow) // { // callWindow = new CallWindow(this); // VideoDevice::init(); // connect(VideoDevice::instance(), &VideoDevice::frameAvailable, // callWindow, [this](const QVideoFrame &frame) { // // 获取本地视频项的videoSink并设置视频帧 // if (auto sink = callWindow->localVideoItem()->videoSink()) { // sink->setVideoFrame(frame); // } // }); // } // callWindow->show(); VideoDevice::init(); connect(VideoDevice::instance(), &VideoDevice::frameAvailable, this, [this](const QVideoFrame &frame) { // if (auto* sink = this->localVideoItem()->videoSink()) { // 明确指出 sink 是指针类型 // if (sink && sink->isActive()) { // 添加必要的有效性检查 // qDebug() << "Video sink is active."; // sink->setVideoFrame(frame); // } else { // qDebug() << "No valid video sink or it's inactive."; // } // } // 获取本地视频项的videoSink并设置视频帧 if (auto sink = this->localVideoItem->videoSink()) { sink->setVideoFrame(frame); } }); } AudioFilter::AudioFilter(int frameSize, int sampleRate) : frameSize(frameSize), sampleRate(sampleRate) { m_format.setSampleRate(sampleRate); m_format.setChannelCount(1); m_format.setSampleFormat(QAudioFormat::Int16); // 获取默认音频输入设备并创建QAudioInput QAudioDevice inputDevice = QMediaDevices::defaultAudioInput(); if (!inputDevice.isFormatSupported(m_format)) { qWarning() << "Default audio input device does not support the required format"; m_format = inputDevice.preferredFormat(); } // m_audioInput = new QAudioInput(inputDevice, m_format, this); // // 设置音频处理选项 // QAudioInputOptions options; // options.setEchoCancel(true); // options.setNoiseSuppression(true); // m_audioInput->setOptions(options); // // 启动音频输入 // m_audioInput->start(); } AudioFilter::~AudioFilter() { //m_audioDevice.stop(); } void AudioFilter::processCapture(const QAudioBuffer &input, QAudioBuffer &output) { // Qt6.7处理逻辑 - 使用QAudioSink处理输入 // QAudioSink sink(m_audioDevice, m_format); // sink.start(); // sink.write(input.data(), input.byteCount()); // // 处理输出缓冲区 // QAudioSource source(m_audioDevice, m_format); // source.start(); // source.read(output.data(), output.byteCount()); } void AudioFilter::processPlayback(const QAudioBuffer &input) { // QAudioSink sink(m_audioDevice, m_format); // QIODevice *ioDevice = sink.start(); // if (ioDevice) { // ioDevice->write(input.constData<char>(), input.byteCount()); // } // sink.stop(); // sink.write(input.data(), input.byteCount()); } // 静态成员变量定义 QAudioDevice AudioDevice::m_inputDevice; QAudioDevice AudioDevice::m_outputDevice; QAudioFormat AudioDevice::m_format; void AudioDevice::init() { // 初始化音频格式 m_format.setSampleRate(44100); m_format.setChannelCount(2); m_format.setSampleFormat(QAudioFormat::Int16); // 获取默认输入输出设备 m_inputDevice = QMediaDevices::defaultAudioInput(); m_outputDevice = QMediaDevices::defaultAudioOutput(); // 验证设备是否支持所需格式 if (!m_inputDevice.isFormatSupported(m_format)) { qWarning() << "Default input device does not support required format"; } if (!m_outputDevice.isFormatSupported(m_format)) { qWarning() << "Default output device does not support required format"; } } void AudioDevice::cleanup() { // 清理音频资源 m_inputDevice = QAudioDevice(); m_outputDevice = QAudioDevice(); } int AudioDevice::capture(void *buffer, int samples) { static QAudioSource *input = nullptr; if (!input) { input = new QAudioSource(m_inputDevice, m_format); } if (input->state() == QAudio::StoppedState) { //input->start(buffer); } return 0;//input->read((char*)buffer, samples * m_format.bytesPerSample()); } int AudioDevice::play(void *buffer, int samples) { static QAudioSink *output = nullptr; static QIODevice *ioDevice = nullptr; if (!output) { output = new QAudioSink(m_outputDevice, m_format); ioDevice = output->start(); } if (ioDevice) { return ioDevice->write(static_cast<const char*>(buffer), samples); } return 0; if (output->state() == QAudio::StoppedState) { //output->start(buffer); } return 0;//output->write((const char*)buffer, samples * m_format.bytesPerSample()); } VideoDevice *VideoDevice::m_instance = nullptr; QCamera *VideoDevice::camera = nullptr; VideoDevice *VideoDevice::instance() { if (!m_instance) { m_instance = new VideoDevice(); } return m_instance; } VideoDevice::VideoDevice(QObject *parent) : QObject(parent) {} void VideoDevice::init() { auto videoInputs = QMediaDevices::videoInputs(); if (videoInputs.isEmpty()) { qWarning() << "No video input devices found"; return; } // 配置视频格式以匹配您的ffmpeg参数 QCameraFormat format; foreach (auto fmt, videoInputs.first().videoFormats()) { if (fmt.resolution() == QSize(640, 480) && fmt.maxFrameRate() >= 30) { format = fmt; break; } } if (!format.isNull()) { camera = new QCamera(videoInputs.first()); camera->setCameraFormat(format); auto *captureSession = new QMediaCaptureSession; captureSession->setCamera(camera); auto *videoSink = new QVideoSink; QObject::connect(videoSink, &QVideoSink::videoFrameChanged, [](const QVideoFrame &frame) { emit VideoDevice::instance()->frameAvailable(frame); }); captureSession->setVideoSink(videoSink); camera->start(); qDebug() << "摄像头启动: 640x480 @ 30fps"; } else { qWarning() << "未找到匹配的视频格式"; } } void VideoDevice::cleanup() { if (camera) { camera->stop(); delete camera; camera = nullptr; } } QVideoFrame VideoDevice::captureFrame() { if (!camera) return QVideoFrame(); // 创建视频帧并映射内存 //QVideoFrame frame(QSize(640, 480), QVideoFrameFormat::Format_ARGB8888); // 先创建格式对象 QVideoFrameFormat format( QSize(640, 480), QVideoFrameFormat::Format_ARGB8888 ); // 再创建视频帧对象 QVideoFrame frame(format); if (frame.map(QVideoFrame::WriteOnly)) { // 这里可以填充帧数据 frame.unmap(); } return frame; } void Form_VoIP::registerAccount(const QString &server, const QString &user, const QString &pass) { accCfg.idUri = "sip:" + user.toStdString() + "@" + server.toStdString(); accCfg.regConfig.registrarUri = "sip:" + server.toStdString(); accCfg.callConfig.timerMinSESec = 90; // 强制最小值 accCfg.callConfig.timerSessExpiresSec = 1800; // 推荐30钟超时 accCfg.sipConfig.authCreds.push_back( pj::AuthCredInfo("digest", "*", user.toStdString(), 0, pass.toStdString()) ); // 关键:启用视频配置 accCfg.videoConfig.autoShowIncoming = true; accCfg.videoConfig.autoTransmitOutgoing = true; accCfg.videoConfig.defaultCaptureDevice = PJMEDIA_VID_DEFAULT_CAPTURE_DEV; accCfg.videoConfig.defaultRenderDevice = PJMEDIA_VID_DEFAULT_RENDER_DEV; // 设置媒体功能 MediaFormatVideo accVideoFormat; accVideoFormat.init(PJMEDIA_FORMAT_RGB24, 640, 480, 30, 1, 0, 0); // acc= new MyAccount; try { acc->create(accCfg); registered = true; emit callStateChanged(0); } catch (...) { std::cout << "Adding account failed" << std::endl; registered = false; } } void Form_VoIP::hangup() { if (currentCall) { try { pj::CallOpParam prm; currentCall->hangup(prm); currentCall = nullptr; emit callStateChanged(0); // 空闲状态 } catch (pj::Error &e) { currentCall = nullptr; std::cerr << "PJSIP Error: " << e.reason << std::endl; // 输出具体错误:ml-citation{ref="4" data="citationList"} } } //hangUpAllCalls(); } //还没调好 void Form_VoIP::hangUpAllCalls() { std::vector<pj::Call*> calls = acc->calls; for (auto call : calls) { CallInfo ci = call->getInfo(); if (ci.state != PJSIP_INV_STATE_DISCONNECTED) { try { pj::CallOpParam prm; call->hangup(prm); } catch (...) { // 处理可能的异常 } } } } void Form_VoIP::setVideoWindows(QVideoSink *local, QVideoSink *remote) { // Qt6.7视频架兼容实现 if (remote) { connect(this, &Form_VoIP::incomingVideoFrame, this, [remote](const QVideoFrame &frame) { QVideoFrame clonedFrame(frame); clonedFrame.map(QVideoFrame::ReadOnly); remote->setVideoFrame(clonedFrame); clonedFrame.unmap(); }, Qt::QueuedConnection); } if (local) { connect(this, &Form_VoIP::outgoingVideoFrame, this, [local](const QVideoFrame &frame) { QVideoFrame clonedFrame(frame); clonedFrame.map(QVideoFrame::ReadOnly); local->setVideoFrame(clonedFrame); clonedFrame.unmap(); }, Qt::QueuedConnection); } } 编译报错/home/shz/Project/appHDQt6/form_voip.cpp:322: error: ‘class pj::VideoMedia’ has no member named ‘startPreview’ ../../form_voip.cpp: In member function ‘virtual void MyCall::onCallMediaState(pj::OnCallMediaStateParam&): ../../form_voip.cpp:322:54: error: ‘class pj::VideoMedia’ has no member named ‘startPreview’ 322 | VideoPreview *preview = vid_med->startPreview(pre_param); | ^~~~~~~~~~~~/home/shz/Project/appHDQt6/form_voip.cpp:332: error: ‘class pj::VideoMedia’ has no member named ‘getRemoteVideoWindow’ ../../form_voip.cpp:332:63: error: ‘class pj::VideoMedia’ has no member named ‘getRemoteVideoWindow’ 332 | remoteVideoWin = new VideoWindow(vid_med->getRemoteVideoWindow()); | ^~~~~~~~~~~~~~~~~~~~/home/shz/Project/appHDQt6/form_voip.cpp:410: error: ‘struct pj::MediaConfig’ has no member named ‘vidCount’ ../../form_voip.cpp: In member function ‘void Form_VoIP::PJSUA2_Init(): ../../form_voip.cpp:410:22: error: ‘struct pj::MediaConfig’ has no member named ‘vidCount’ 410 | ep_cfg.medConfig.vidCount = 1; // 必须 >=1 才能支持视频 | ^~~~~~~~/home/shz/Project/appHDQt6/form_voip.cpp:417: error: ‘struct pj::EpConfig’ has no member named ‘vidConfig’; did you mean ‘medConfig’? ../../form_voip.cpp:417:12: error: ‘struct pj::EpConfig’ has no member named ‘vidConfig’; did you mean ‘medConfig’? 417 | ep_cfg.vidConfig.capDev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV; | ^~~~~~~~~ | medConfig/home/shz/Project/appHDQt6/form_voip.cpp:433: error: ‘struct pj::MediaConfig’ has no member named ‘videoConfig’ ../../form_voip.cpp:433:22: error: ‘struct pj::MediaConfig’ has no member named ‘videoConfig’ 433 | ep_cfg.medConfig.videoConfig.defaultCaptureFormat = videoFormat.toPj(); | ^~~~~~~~~~~/home/shz/Project/appHDQt6/form_voip.cpp:885: error: invalid use of incomplete type ‘class QMediaCaptureSession’ ../../form_voip.cpp: In static member function ‘static void VideoDevice::init(): ../../form_voip.cpp:885:36: error: invalid use of incomplete type ‘class QMediaCaptureSession’ 885 | auto *captureSession = new QMediaCaptureSession; | ^~~~~~~~~~~~~~~~~~~~
09-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

谁的BUG最难改

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

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

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

打赏作者

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

抵扣说明:

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

余额充值