/*************************************************************************************** * * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. * * By downloading, copying, installing or using the software you agree to this license. * If you do not agree to this license, do not download, install, * copy or use the software. * * Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved. * * Redistribution and use in binary forms, with or without modification, are permitted. * * Unless required by applicable law or agreed to in writing, software distributed * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific * language governing permissions and limitations under the License. * ****************************************************************************************/ #include "MediaClient.h" #include "InstMsgDialog.h" #include "OpenMedia.h" #include "config.h" #include "FileBrowse.h" #include "About.h" #include "utils.h" #include "SystemSetting.h" #include "rtsp_cln.h" #include "rtmp_cln.h" #include "http_flv_cln.h" #include "http_mjpeg_cln.h" #include "srt_cln.h" #include #include #include #include #include #if defined(ANDROID) #include #endif MediaClient::MediaClient(QWidget *parent, Qt::WindowFlags flags) : QDialog(parent, flags) , m_curWidget(NULL) , m_recvBytes(0) { ui.setupUi(this); initDialog(); connSignalSlot(); } MediaClient::~MediaClient() { QList channels; saveChannel(channels, ui.videoWidget1); saveChannel(channels, ui.videoWidget2); saveChannel(channels, ui.videoWidget3); saveChannel(channels, ui.videoWidget4); saveChannel(channels, ui.videoWidget5); saveChannel(channels, ui.videoWidget6); saveChannel(channels, ui.videoWidget7); saveChannel(channels, ui.videoWidget8); saveChannel(channels, ui.videoWidget9); saveChannels(channels); saveLayoutMode(m_layoutMode); ui.videoWidget1->stop(); ui.videoWidget2->stop(); ui.videoWidget3->stop(); ui.videoWidget4->stop(); ui.videoWidget5->stop(); ui.videoWidget6->stop(); ui.videoWidget7->stop(); ui.videoWidget8->stop(); ui.videoWidget9->stop(); #if defined(ANDROID) QJniObject::callStaticMethod("org/happytimesoft/util/HtUtil", "enableLockScreen"); #endif log_close(); QDesktopServices::openUrl(QUrl("https://www.happytimesoft.com/", QUrl::TolerantMode)); } void MediaClient::saveChannel(QList &channels, VideoWidget * widget) { CHANNEL channel; if (widget->isPlaying()) { channel.url = widget->getUrl(); channel.user = widget->getUser(); channel.pass = widget->getPass(); } else { channel.url = ""; channel.user = ""; channel.pass = ""; } channels.append(channel); } void MediaClient::closeEvent(QCloseEvent * event) { if (QMessageBox::Yes == QMessageBox::question(this, tr("Quit"), tr("Are you sure want to quit?"))) { event->accept(); } else { event->ignore(); } } void MediaClient::initLog() { char file[512] = {'\0'}; QString path = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).last(); sprintf(file, "%s/mediaclient.log", path.toStdString().c_str()); log_init(file); } void MediaClient::initDialog() { setWindowTitle(QString("Happytime media client %1").arg(VERSION_STRING)); #if defined(ANDROID) QJniObject str = QJniObject::fromString("android.permission.WRITE_EXTERNAL_STORAGE"); QJniObject::callStaticMethod("org/happytimesoft/util/HtUtil", "requestPermission", "(Landroid/content/Context;Ljava/lang/String;)I", QNativeInterface::QAndroidApplication::context(), str.object()); #endif loadSystemConfig(); if (m_syscfg.enableLog) { initLog(); log_set_level(m_syscfg.logLevel); } ui.labTipInfo->setText(""); ui.labStatistics->setText(""); ui.cmbLayout->addItem(QIcon(":/res/Resources/1.png"), "1", 1); ui.cmbLayout->addItem(QIcon(":/res/Resources/2.png"), "2", 2); ui.cmbLayout->addItem(QIcon(":/res/Resources/4.png"), "4", 4); ui.cmbLayout->addItem(QIcon(":/res/Resources/6.png"), "6", 6); ui.cmbLayout->addItem(QIcon(":/res/Resources/9.png"), "9", 9); QScreen * pScreen = QApplication::primaryScreen(); setupLayout(pScreen->orientation()); connect(pScreen, SIGNAL(orientationChanged(Qt::ScreenOrientation)), this, SLOT(slotOrientationChanged(Qt::ScreenOrientation))); if (1 == m_syscfg.layoutMode) { slotLayoutOne(); ui.cmbLayout->setCurrentIndex(0); } else if (2 == m_syscfg.layoutMode) { slotLayoutTwo(); ui.cmbLayout->setCurrentIndex(1); } else if (6 == m_syscfg.layoutMode) { slotLayoutSix(); ui.cmbLayout->setCurrentIndex(3); } else if (9 == m_syscfg.layoutMode) { slotLayoutNine(); ui.cmbLayout->setCurrentIndex(4); } else { slotLayoutFour(); ui.cmbLayout->setCurrentIndex(2); } m_curWidget = ui.videoWidget1; m_curWidget->parentWidget()->setStyleSheet("background-color: blue;"); // when video playing, prevent auto lock screen #if defined(ANDROID) QJniObject::callStaticMethod("org/happytimesoft/util/HtUtil", "disableLockScreen", "(Landroid/content/Context;)V", QNativeInterface::QAndroidApplication::context()); #endif loadChannels(); #ifndef IOS ui.btnQuit->hide(); #endif } void MediaClient::connSignalSlot() { connect(ui.cmbLayout, SIGNAL(currentIndexChanged(int)), this, SLOT(slotLayoutChanged(int))); connect(ui.btnPlay, SIGNAL(clicked()), this, SLOT(slotPlay())); connect(ui.btnPause, SIGNAL(clicked()), this, SLOT(slotPause())); connect(ui.btnStop, SIGNAL(clicked()), this, SLOT(slotStop())); connect(ui.btnSnapshot, SIGNAL(clicked()), this, SLOT(slotSnapshot())); connect(ui.btnRecord, SIGNAL(clicked()), this, SLOT(slotRecord())); connect(ui.btnVolume, SIGNAL(clicked()), this, SLOT(slotVolume())); connect(ui.btnSetting, SIGNAL(clicked()), this, SLOT(slotSystemSetting())); connect(ui.btnOpenSnapshot, SIGNAL(clicked()), this, SLOT(slotOpenSnapshot())); connect(ui.btnOpenRecord, SIGNAL(clicked()), this, SLOT(slotOpenRecording())); connect(ui.btnAbout, SIGNAL(clicked()), this, SLOT(slotAbout())); connect(ui.btnQuit, SIGNAL(clicked()), this, SLOT(close())); connVideoWidget(ui.videoWidget1); connVideoWidget(ui.videoWidget2); connVideoWidget(ui.videoWidget3); connVideoWidget(ui.videoWidget4); connVideoWidget(ui.videoWidget5); connVideoWidget(ui.videoWidget6); connVideoWidget(ui.videoWidget7); connVideoWidget(ui.videoWidget8); connVideoWidget(ui.videoWidget9); } void MediaClient::connVideoWidget(VideoWidget * widget) { connect(widget, SIGNAL(snapshotResult(bool)), this, SLOT(slotSnapshotResult(bool))); connect(widget, SIGNAL(recordResult(bool)), this, SLOT(slotRecordResult(bool))); connect(widget, SIGNAL(updateStatistics(int)), this, SLOT(slotUpdateStatistics(int))); connect(widget, SIGNAL(widgetSelecting(QWidget*)), this, SLOT(slotWidgetSelecting(QWidget*))); connect(widget, SIGNAL(callState(QWidget*,int)), this, SLOT(slotCallState(QWidget*,int))); } void MediaClient::loadSystemConfig() { m_syscfg.enableLog = getEnableLogFlag(); m_syscfg.videoRenderMode = getVideoRenderMode(); m_syscfg.rtpMulticast = getRtpMulticast(); m_syscfg.rtpOverUdp = getRtpOverUdp(); m_syscfg.rtspOverHttp = getRtspOverHttp(); m_syscfg.rtspOverHttpPort = getRtspOverHttpPort(); m_syscfg.rtspOverWs = getRtspOverWs(); m_syscfg.rtspOverWsPort = getRtspOverWsPort(); m_syscfg.logLevel = getLogLevel(); m_syscfg.hwDecoding = getHWDecoding(); m_syscfg.layoutMode = getLayoutMode(); m_syscfg.recordTime = getRecordTime(); m_syscfg.recordSize = getRecordSize(); } void MediaClient::loadChannels() { QList channels; getChannels(channels); if (channels.size() > 0 && !channels[0].url.isEmpty()) { ui.videoWidget1->play(channels[0].url, channels[0].user, channels[0].pass); } if (channels.size() > 1 && !channels[1].url.isEmpty()) { ui.videoWidget2->play(channels[1].url, channels[1].user, channels[1].pass); } if (channels.size() > 2 && !channels[2].url.isEmpty()) { ui.videoWidget3->play(channels[2].url, channels[2].user, channels[2].pass); } if (channels.size() > 3 && !channels[3].url.isEmpty()) { ui.videoWidget4->play(channels[3].url, channels[3].user, channels[3].pass); } if (channels.size() > 4 && !channels[4].url.isEmpty()) { ui.videoWidget5->play(channels[4].url, channels[4].user, channels[4].pass); } if (channels.size() > 5 && !channels[5].url.isEmpty()) { ui.videoWidget6->play(channels[5].url, channels[5].user, channels[5].pass); } if (channels.size() > 6 && !channels[6].url.isEmpty()) { ui.videoWidget7->play(channels[6].url, channels[6].user, channels[6].pass); } if (channels.size() > 7 && !channels[7].url.isEmpty()) { ui.videoWidget8->play(channels[7].url, channels[7].user, channels[7].pass); } if (channels.size() > 8 && !channels[8].url.isEmpty()) { ui.videoWidget9->play(channels[8].url, channels[8].user, channels[8].pass); } } void MediaClient::slotLayoutOne() { ui.widget1->show(); ui.widget2->hide(); ui.widget3->hide(); ui.widget4->hide(); ui.widget5->hide(); ui.widget6->hide(); ui.widget7->hide(); ui.widget8->hide(); ui.widget9->hide(); m_layoutMode = 1; } void MediaClient::slotLayoutTwo() { QScreen * pScreen = QApplication::primaryScreen(); Qt::ScreenOrientation orientation = pScreen->orientation(); if (orientation == Qt::LandscapeOrientation || orientation == Qt::InvertedLandscapeOrientation) // width > height { ui.widget1->show(); ui.widget2->show(); ui.widget3->hide(); ui.widget4->hide(); ui.widget5->hide(); ui.widget6->hide(); ui.widget7->hide(); ui.widget8->hide(); ui.widget9->hide(); } else { ui.widget1->show(); ui.widget2->hide(); ui.widget3->hide(); ui.widget4->show(); ui.widget5->hide(); ui.widget6->hide(); ui.widget7->hide(); ui.widget8->hide(); ui.widget9->hide(); } m_layoutMode = 2; } void MediaClient::slotLayoutFour() { ui.widget1->show(); ui.widget2->show(); ui.widget3->hide(); ui.widget4->show(); ui.widget5->show(); ui.widget6->hide(); ui.widget7->hide(); ui.widget8->hide(); ui.widget9->hide(); m_layoutMode = 4; } void MediaClient::slotLayoutSix() { QScreen * pScreen = QApplication::primaryScreen(); Qt::ScreenOrientation orientation = pScreen->orientation(); if (orientation == Qt::LandscapeOrientation || orientation == Qt::InvertedLandscapeOrientation) // width > height { ui.widget1->show(); ui.widget2->show(); ui.widget3->show(); ui.widget4->show(); ui.widget5->show(); ui.widget6->show(); ui.widget7->hide(); ui.widget8->hide(); ui.widget9->hide(); } else { ui.widget1->show(); ui.widget2->show(); ui.widget3->hide(); ui.widget4->show(); ui.widget5->show(); ui.widget6->hide(); ui.widget7->show(); ui.widget8->show(); ui.widget9->hide(); } m_layoutMode = 6; } void MediaClient::slotLayoutNine() { ui.widget1->show(); ui.widget2->show(); ui.widget3->show(); ui.widget4->show(); ui.widget5->show(); ui.widget6->show(); ui.widget7->show(); ui.widget8->show(); ui.widget9->show(); m_layoutMode = 9; } void MediaClient::slotLayoutChanged(int index) { if (0 == index) { slotLayoutOne(); } else if (1 == index) { slotLayoutTwo(); } else if (2 == index) { slotLayoutFour(); } else if (3 == index) { slotLayoutSix(); } else if (4 == index) { slotLayoutNine(); } } void MediaClient::slotWidgetSelecting(QWidget * pWidget) { if (m_curWidget != pWidget) { m_curWidget->parentWidget()->setStyleSheet("background-color: white;"); m_curWidget = (VideoWidget *) pWidget; m_curWidget->parentWidget()->setStyleSheet("background-color: blue;"); if (m_curWidget->isRecording()) { ui.labTipInfo->setText(tr("Recording ...")); QIcon icon; icon.addFile(QString(":/res/Resources/stop_record.png")); ui.btnRecord->setIcon(icon); } else { ui.labTipInfo->setText(""); QIcon icon; icon.addFile(QString(":/res/Resources/video_record.png")); ui.btnRecord->setIcon(icon); } if (m_curWidget->isMute()) { ui.btnVolume->setIcon(QIcon(QString::fromUtf8(":/res/Resources/mute.png"))); } else { ui.btnVolume->setIcon(QIcon(QString::fromUtf8(":/res/Resources/volume.png"))); } } } void MediaClient::slotPlay() { QString url = m_curWidget->getUrl(); QString user = m_curWidget->getUser(); QString pass = m_curWidget->getPass(); if (url.isEmpty()) { url = m_url; user = m_user; pass = m_pass; } OpenMedia dlg(url, user, pass, this); dlg.resize(width(), dlg.height()); if (QDialog::Accepted == dlg.exec()) { m_url = dlg.getUrl(); m_user = dlg.getUser(); m_pass = dlg.getPass(); m_curWidget->play(m_url, m_user, m_pass); } } void MediaClient::slotPause() { m_curWidget->pause(); } void MediaClient::slotStop() { m_curWidget->stop(); ui.labTipInfo->setText(tr("")); ui.btnVolume->setIcon(QIcon(QString::fromUtf8(":/res/Resources/volume.png"))); ui.btnRecord->setIcon(QIcon(QString::fromUtf8(":/res/Resources/video_record.png"))); } void MediaClient::slotCallState(QWidget* pWidget, int event) { if (m_curWidget != pWidget) { return; } if (event == RTSP_EVE_CONNECTING || event == RTMP_EVE_CONNECTING || event == HTTP_FLV_EVE_CONNECTING || event == MJPEG_EVE_CONNECTING || event == SRT_EVE_CONNECTING) { ui.labTipInfo->setText(tr("Connecting...")); } else if (event == RTSP_EVE_CONNFAIL || event == RTMP_EVE_CONNFAIL || event == HTTP_FLV_EVE_CONNFAIL || event == MJPEG_EVE_CONNFAIL || event == SRT_EVE_CONNFAIL) { ui.labTipInfo->setText(tr("Connect failed")); } else if (event == RTSP_EVE_CONNSUCC || event == MJPEG_EVE_CONNSUCC || event == RTMP_EVE_VIDEOREADY || event == RTMP_EVE_AUDIOREADY || event == HTTP_FLV_EVE_VIDEOREADY || event == HTTP_FLV_EVE_AUDIOREADY || event == SRT_EVE_VIDEOREADY || event == SRT_EVE_AUDIOREADY) { ui.labTipInfo->setText(tr("")); } else if (event == RTSP_EVE_NOSIGNAL || event == RTMP_EVE_NOSIGNAL || event == HTTP_FLV_EVE_NOSIGNAL || event == MJPEG_EVE_NOSIGNAL || event == SRT_EVE_NOSIGNAL) { ui.labTipInfo->setText(tr("NO Signal")); } else if (event == RTSP_EVE_NODATA || event == RTMP_EVE_NODATA || event == HTTP_FLV_EVE_NODATA || event == MJPEG_EVE_NODATA || event == SRT_EVE_NODATA) { ui.labTipInfo->setText(tr("No Data")); } else if (event == RTSP_EVE_RESUME || event == RTMP_EVE_RESUME || event == HTTP_FLV_EVE_RESUME || event == MJPEG_EVE_RESUME || event == SRT_EVE_RESUME) { ui.labTipInfo->setText(tr("")); } else if (event == RTSP_EVE_AUTHFAILED || event == RTMP_EVE_AUTHFAILED|| event == HTTP_FLV_EVE_AUTHFAILED || event == MJPEG_EVE_AUTHFAILED || event == SRT_EVE_AUTHFAILED) { ui.labTipInfo->setText(tr("Authenticate failed")); } } void MediaClient::slotSnapshot() { m_curWidget->snapshot(); } void MediaClient::slotRecord() { m_curWidget->record(); if (m_curWidget->isRecording()) { ui.labTipInfo->setText(tr(" Recording ...")); ui.btnRecord->setIcon(QIcon(QString::fromUtf8(":/res/Resources/stop_record.png"))); } else { ui.labTipInfo->setText(""); ui.btnRecord->setIcon(QIcon(QString::fromUtf8(":/res/Resources/video_record.png"))); } } void MediaClient::slotSnapshotResult(bool ret) { QString msg; if (ret) { msg = tr("Snapshot success"); } else { msg = tr("Snapshot failed"); } InstMsgDialog dlg(msg, 2000); dlg.exec(); } void MediaClient::slotRecordResult(bool ret) { ui.btnRecord->setIcon(QIcon(QString::fromUtf8(":/res/Resources/video_record.png"))); QString msg; if (ret) { msg = tr("Video recording success"); } else { msg = tr("Video recording failed"); } InstMsgDialog dlg(msg, 2000); dlg.exec(); } void MediaClient::slotVolume() { if (!m_curWidget->isPlaying()) { return; } if (m_curWidget->isMute()) { ui.btnVolume->setIcon(QIcon(QString::fromUtf8(":/res/Resources/volume.png"))); m_curWidget->setMute(FALSE); } else { ui.btnVolume->setIcon(QIcon(QString::fromUtf8(":/res/Resources/mute.png"))); m_curWidget->setMute(TRUE); } } void MediaClient::slotUpdateStatistics(int bytes) { int kb = 0, mb = 0; char buff[100]; m_recvBytes += bytes; if (m_recvBytes >= 1000) { kb = m_recvBytes / 1000; } if (kb >= 1000) { mb = kb / 1000; kb = kb % 1000; kb = kb / 10; sprintf(buff, " %d.%02d MB", mb, kb); } else { sprintf(buff, " %d KB", kb); } ui.labStatistics->setText(tr("RX:") + buff); } void MediaClient::slotOrientationChanged(Qt::ScreenOrientation orientation) { setupLayout(orientation); } void MediaClient::setupLayout(Qt::ScreenOrientation orientation) { log_print(HT_LOG_DBG, "setupLayout, orientation = %d\r\n", orientation); if (orientation == Qt::LandscapeOrientation || orientation == Qt::InvertedLandscapeOrientation) // width > height { ui.infoWidget->layout()->removeWidget(ui.cmbLayout); ui.infoWidget->layout()->removeWidget(ui.labTipInfo); ui.infoWidget->layout()->removeWidget(ui.labStatistics); ui.infoWidget->hide(); ui.toolWidget->layout()->removeItem(ui.horizontalSpacer1); ui.toolWidget->layout()->removeWidget(ui.btnPlay); ui.toolWidget->layout()->removeWidget(ui.btnPause); ui.toolWidget->layout()->removeWidget(ui.btnStop); ui.toolWidget->layout()->removeItem(ui.horizontalSpacer2); ui.toolWidget->layout()->removeWidget(ui.btnVolume); ui.toolWidget->layout()->removeWidget(ui.btnSnapshot); ui.toolWidget->layout()->removeWidget(ui.btnRecord); ui.toolWidget->layout()->removeItem(ui.horizontalSpacer3); ui.toolWidget->layout()->addWidget(ui.cmbLayout); ui.toolWidget->layout()->addItem(ui.horizontalSpacer1); ui.toolWidget->layout()->addWidget(ui.btnPlay); ui.toolWidget->layout()->addWidget(ui.btnPause); ui.toolWidget->layout()->addWidget(ui.btnStop); ui.toolWidget->layout()->addItem(ui.horizontalSpacer2); ui.toolWidget->layout()->addWidget(ui.btnVolume); ui.toolWidget->layout()->addWidget(ui.btnSnapshot); ui.toolWidget->layout()->addWidget(ui.btnRecord); ui.toolWidget->layout()->addItem(ui.horizontalSpacer3); ui.toolWidget->layout()->addWidget(ui.labStatistics); } else // height > width { ui.infoWidget->layout()->removeWidget(ui.cmbLayout); ui.infoWidget->layout()->removeWidget(ui.labTipInfo); ui.infoWidget->layout()->removeWidget(ui.labStatistics); ui.toolWidget->layout()->removeWidget(ui.labTipInfo); ui.toolWidget->layout()->removeWidget(ui.labStatistics); ui.toolWidget->layout()->removeWidget(ui.btnPlay); ui.toolWidget->layout()->removeWidget(ui.btnPause); ui.toolWidget->layout()->removeWidget(ui.btnStop); ui.toolWidget->layout()->removeWidget(ui.btnVolume); ui.toolWidget->layout()->removeWidget(ui.btnSnapshot); ui.toolWidget->layout()->removeWidget(ui.btnRecord); ui.toolWidget->layout()->removeItem(ui.horizontalSpacer1); ui.toolWidget->layout()->removeItem(ui.horizontalSpacer2); ui.toolWidget->layout()->removeItem(ui.horizontalSpacer3); ui.infoWidget->layout()->addWidget(ui.cmbLayout); ui.infoWidget->layout()->addWidget(ui.labTipInfo); ui.infoWidget->layout()->addWidget(ui.labStatistics); ui.infoWidget->show(); ui.toolWidget->layout()->addItem(ui.horizontalSpacer1); ui.toolWidget->layout()->addWidget(ui.btnPlay); ui.toolWidget->layout()->addWidget(ui.btnPause); ui.toolWidget->layout()->addWidget(ui.btnStop); ui.toolWidget->layout()->addItem(ui.horizontalSpacer2); ui.toolWidget->layout()->addWidget(ui.btnVolume); ui.toolWidget->layout()->addWidget(ui.btnSnapshot); ui.toolWidget->layout()->addWidget(ui.btnRecord); ui.toolWidget->layout()->addItem(ui.horizontalSpacer3); } if (m_layoutMode == 2) { slotLayoutTwo(); } else if (m_layoutMode == 6) { slotLayoutSix(); } } void MediaClient::slotSystemSetting() { SystemSetting dlg(m_syscfg, this); if (QDialog::Accepted == dlg.exec()) { SysConfig config = dlg.getSysConfig(); /* apply system config parameter */ if (m_syscfg.enableLog != config.enableLog) { if (m_syscfg.enableLog) { log_close(); } else { initLog(); } } log_set_level(config.logLevel); /* update system config parameter */ m_syscfg.enableLog = config.enableLog; m_syscfg.videoRenderMode = config.videoRenderMode; m_syscfg.rtpMulticast = config.rtpMulticast; m_syscfg.rtpOverUdp = config.rtpOverUdp; m_syscfg.rtspOverHttp = config.rtspOverHttp; m_syscfg.rtspOverHttpPort = config.rtspOverHttpPort; m_syscfg.rtspOverWs = config.rtspOverWs; m_syscfg.rtspOverWsPort = config.rtspOverWsPort; m_syscfg.logLevel = config.logLevel; m_syscfg.hwDecoding = config.hwDecoding; m_syscfg.recordTime = config.recordTime; m_syscfg.recordSize = config.recordSize; } } void MediaClient::slotOpenSnapshot() { FileBrowse * pDlg = new FileBrowse(FILE_FORMAT_PIC, this); pDlg->exec(); delete pDlg; } void MediaClient::slotOpenRecording() { FileBrowse * pDlg = new FileBrowse(FILE_FORMAT_VIDEO, this); pDlg->exec(); delete pDlg; } void MediaClient::slotAbout() { About dlg(this); dlg.exec(); }