diff --git a/libOnvifConnect/onvifdeviceconnection.cpp b/libOnvifConnect/onvifdeviceconnection.cpp index 4440f76..81e8abc 100644 --- a/libOnvifConnect/onvifdeviceconnection.cpp +++ b/libOnvifConnect/onvifdeviceconnection.cpp @@ -45,6 +45,14 @@ OnvifDeviceConnection::OnvifDeviceConnection(QObject* parent) : d_ptr(new OnvifDeviceConnectionPrivate(this)) { Q_D(OnvifDeviceConnection); + connect(&d->soapService, &DeviceBindingService::getSystemDateAndTimeDone, + [d](const OnvifSoapDevicemgmt::TDS__GetSystemDateAndTimeResponse & parameters) { + d->getSystemDateAndTimeDone(parameters); + }); + connect(&d->soapService, &DeviceBindingService::getSystemDateAndTimeError, + [d](const KDSoapMessage & fault) { + d->getSystemDateAndTimeError(fault); + }); connect(&d->soapService, &DeviceBindingService::getServicesDone, [d](const OnvifSoapDevicemgmt::TDS__GetServicesResponse & parameters) { d->getServicesDone(parameters); @@ -96,12 +104,7 @@ void OnvifDeviceConnection::connectToDevice() d->isGetCapabilitiesFinished = false; d->isGetServicesFinished = false; - TDS__GetServices request; - request.setIncludeCapability(true); - // Access level pre-auth => no credentials needes - d->soapService.asyncGetServices(request); - // Access level pre-auth => no credentials needes - d->soapService.asyncGetCapabilities(TDS__GetCapabilities()); + d->getSystemDateAndTime(std::bind(&OnvifDeviceConnectionPrivate::getServicesAndCapabilities, d)); d->errorString.clear(); emit errorStringChanged(d->errorString); @@ -112,6 +115,8 @@ void OnvifDeviceConnection::disconnectFromDevice() Q_D(OnvifDeviceConnection); d->isGetCapabilitiesFinished = false; d->isGetServicesFinished = false; + d->deviceDateAndTime = QDateTime(); + d->elapsedTime.invalidate(); if (d->deviceService) { d->deviceService->deleteLater(); @@ -134,6 +139,41 @@ void OnvifDeviceConnection::disconnectFromDevice() d->ptzService = nullptr; } +void OnvifDeviceConnectionPrivate::getServicesAndCapabilities() +{ + updateSoapCredentials(soapService.clientInterface()); + TDS__GetServices request; + request.setIncludeCapability(true); + // Access level pre-auth => no credentials needed + soapService.asyncGetServices(request); + // Access level pre-auth => no credentials needed + soapService.asyncGetCapabilities(TDS__GetCapabilities()); +} + +void OnvifDeviceConnectionPrivate::getSystemDateAndTimeDone(const OnvifSoapDevicemgmt::TDS__GetSystemDateAndTimeResponse& parameters) +{ + if (parameters.systemDateAndTime().hasValueForUTCDateTime()) { + const QString dateTime = QString("%1-%2-%3 %4:%5:%6").arg(parameters.systemDateAndTime().uTCDateTime().date().year()) + .arg(parameters.systemDateAndTime().uTCDateTime().date().month(), 2, 10, QLatin1Char('0')) + .arg(parameters.systemDateAndTime().uTCDateTime().date().day(), 2, 10, QLatin1Char('0')) + .arg(parameters.systemDateAndTime().uTCDateTime().time().hour(), 2, 10, QLatin1Char('0')) + .arg(parameters.systemDateAndTime().uTCDateTime().time().minute(), 2, 10, QLatin1Char('0')) + .arg(parameters.systemDateAndTime().uTCDateTime().time().second(), 2, 10, QLatin1Char('0')); + deviceDateAndTime = QDateTime::fromString(dateTime, QString("yyyy-MM-dd HH:mm:ss")); + } + if (systemDateAndTimeResumeFunction) { + systemDateAndTimeResumeFunction(); + } +} + +void OnvifDeviceConnectionPrivate::getSystemDateAndTimeError(const KDSoapMessage& fault) +{ + qDebug() << "The GetSystemDateAndTime call failed; using client date and time:" << fault.faultAsString(); + if (systemDateAndTimeResumeFunction) { + systemDateAndTimeResumeFunction(); + } +} + void OnvifDeviceConnectionPrivate::getServicesDone(const TDS__GetServicesResponse& parameters) { Q_Q(OnvifDeviceConnection); @@ -298,11 +338,33 @@ void OnvifDeviceConnectionPrivate::updateSoapCredentials(KDSoapClientInterface* auth.setUser(username); auth.setPassword(password); auth.setUseWSUsernameToken(isUsernameTokenSupported); + if (deviceDateAndTime.isValid()) { + if(elapsedTime.isValid()){ + deviceDateAndTime = deviceDateAndTime.addMSecs(elapsedTime.elapsed()); + } else { + elapsedTime.start(); + } + auth.setOverrideWSUsernameCreatedTime(deviceDateAndTime); + } clientInterface->setAuthentication(auth); } // Some camera's don't require authentication and therefore don't ask for any } +void OnvifDeviceConnectionPrivate::clearSoapCredentials() +{ + deviceDateAndTime = QDateTime(); + elapsedTime.invalidate(); + soapService.clientInterface()->setAuthentication(KDSoapAuthentication()); +} + +void OnvifDeviceConnectionPrivate::getSystemDateAndTime(std::function resumeFunction) +{ + systemDateAndTimeResumeFunction = resumeFunction; + clearSoapCredentials(); + soapService.asyncGetSystemDateAndTime(TDS__GetSystemDateAndTime()); +} + void OnvifDeviceConnectionPrivate::updateUrlCredentials(QUrl* url) { Q_ASSERT(url); @@ -321,6 +383,17 @@ void OnvifDeviceConnectionPrivate::handleSoapError(const KDSoapMessage& fault, c if (!isHttpDigestSupported && !isUsernameTokenSupported) { errorString = "None of the authentication methods are available"; } + } else if (location.contains("OnvifPtzServicePrivate::getServiceCapabilitiesError") && + (fault.faultAsString().contains("not implemented") || fault.faultAsString().contains("Action failed"))) { + // Some devices report having PTZ service but fail with "not implemented" or "Action failed" when getServiceCapabilities is called + // Therefore we disable the service and ignore any error + qDebug() << "The PTZ GetServicesCapabilities call failed; this is expected for some ONVIF devices:" << fault.faultAsString(); + if (ptzService) { + ptzService->disconnectFromService(); + ptzService->deleteLater(); + } + ptzService = nullptr; + return; } else { errorString = location + ": " + fault.faultAsString(); } diff --git a/libOnvifConnect/onvifdeviceconnection_p.h b/libOnvifConnect/onvifdeviceconnection_p.h index b69816e..873b5b7 100644 --- a/libOnvifConnect/onvifdeviceconnection_p.h +++ b/libOnvifConnect/onvifdeviceconnection_p.h @@ -20,6 +20,8 @@ #include "onvifdeviceconnection.h" #include "wsdl_devicemgmt.h" +#include +#include class OnvifDeviceConnectionPrivate { @@ -42,6 +44,9 @@ class OnvifDeviceConnectionPrivate QString errorString; + QDateTime deviceDateAndTime; + QElapsedTimer elapsedTime; + bool isUsernameTokenSupported = false; bool isHttpDigestSupported = false; @@ -50,6 +55,11 @@ class OnvifDeviceConnectionPrivate static const QString c_baseEndpointURI; + std::function systemDateAndTimeResumeFunction; + + void getServicesAndCapabilities(); + void getSystemDateAndTimeDone(const OnvifSoapDevicemgmt::TDS__GetSystemDateAndTimeResponse ¶meters); + void getSystemDateAndTimeError(const KDSoapMessage& fault); void getServicesDone(const OnvifSoapDevicemgmt::TDS__GetServicesResponse& parameters); void getServicesError(const KDSoapMessage& fault); void getCapabilitiesDone(const OnvifSoapDevicemgmt::TDS__GetCapabilitiesResponse& parameters); @@ -60,6 +70,8 @@ class OnvifDeviceConnectionPrivate public: void updateUrlHost(QUrl* url); void updateSoapCredentials(KDSoapClientInterface* clientInterface); + void clearSoapCredentials(); + void getSystemDateAndTime(std::function resumeFunction); void updateUrlCredentials(QUrl* url); void handleSoapError(const KDSoapMessage& fault, const QString& location); }; diff --git a/libOnvifConnect/onvifmedia2service.cpp b/libOnvifConnect/onvifmedia2service.cpp index 3241fe6..3a2b4b2 100644 --- a/libOnvifConnect/onvifmedia2service.cpp +++ b/libOnvifConnect/onvifmedia2service.cpp @@ -47,6 +47,7 @@ class OnvifMedia2ServicePrivate QUrl streamUri; QString preferredVideoStreamProtocol; + void selectProfile(); void getProfilesDone(const OnvifSoapMedia2::TR2__GetProfilesResponse& parameters); void getProfilesError(const KDSoapMessage& fault); void getSnapshotUriDone(const OnvifSoapMedia2::TR2__GetSnapshotUriResponse& parameters); @@ -123,20 +124,7 @@ void OnvifMedia2Service::selectProfile(const OnvifMediaProfile& profile) Q_D(OnvifMedia2Service); d->selectedProfile = profile; - if (d->supportsSnapshotUri) { - TR2__GetSnapshotUri requestSnapshot; - requestSnapshot.setProfileToken(d->selectedProfile.token()); - d->device->d_ptr->updateSoapCredentials(d->soapService.clientInterface()); - d->soapService.asyncGetSnapshotUri(requestSnapshot); - } - - TR2__GetStreamUri requestStream; - requestStream.setProfileToken(d->selectedProfile.token()); - if (!d->preferredVideoStreamProtocol.isEmpty()) { - requestStream.setProtocol(d->preferredVideoStreamProtocol); - } - d->device->d_ptr->updateSoapCredentials(d->soapService.clientInterface()); - d->soapService.asyncGetStreamUri(requestStream); + d->device->d_ptr->getSystemDateAndTime(std::bind(&OnvifMedia2ServicePrivate::selectProfile, d)); } bool OnvifMedia2Service::supportsSnapshotUri() const @@ -163,6 +151,24 @@ void OnvifMedia2Service::setPreferredVideoStreamProtocol(const QString& preferre d->preferredVideoStreamProtocol = preferredVideoStreamProtocol; } +void OnvifMedia2ServicePrivate::selectProfile() +{ + if (supportsSnapshotUri) { + TR2__GetSnapshotUri requestSnapshot; + requestSnapshot.setProfileToken(selectedProfile.token()); + device->d_ptr->updateSoapCredentials(soapService.clientInterface()); + soapService.asyncGetSnapshotUri(requestSnapshot); + } + + TR2__GetStreamUri requestStream; + requestStream.setProfileToken(selectedProfile.token()); + if (!preferredVideoStreamProtocol.isEmpty()) { + requestStream.setProtocol(preferredVideoStreamProtocol); + } + device->d_ptr->updateSoapCredentials(soapService.clientInterface()); + soapService.asyncGetStreamUri(requestStream); +} + void OnvifMedia2ServicePrivate::getProfilesDone(const TR2__GetProfilesResponse& parameters) { Q_Q(OnvifMedia2Service); diff --git a/libOnvifConnect/onvifmediaservice.cpp b/libOnvifConnect/onvifmediaservice.cpp index 86ac8cb..2a56ce2 100644 --- a/libOnvifConnect/onvifmediaservice.cpp +++ b/libOnvifConnect/onvifmediaservice.cpp @@ -49,6 +49,7 @@ class OnvifMediaServicePrivate QUrl streamUri; QString preferredVideoStreamProtocol; + void selectProfile(); void getServiceCapabilitiesDone(const OnvifSoapMedia::TRT__GetServiceCapabilitiesResponse& parameters); void getServiceCapabilitiesError(const KDSoapMessage& fault); void getProfilesDone(const OnvifSoapMedia::TRT__GetProfilesResponse& parameters); @@ -134,39 +135,7 @@ void OnvifMediaService::selectProfile(const OnvifMediaProfile& profile) Q_D(OnvifMediaService); d->selectedProfile = profile; - OnvifSoapMedia::TRT__GetSnapshotUri requestSnapshot; - requestSnapshot.setProfileToken(d->selectedProfile.token()); - d->device->d_ptr->updateSoapCredentials(d->soapService.clientInterface()); - d->soapService.asyncGetSnapshotUri(requestSnapshot); - - OnvifSoapMedia::TRT__GetStreamUri requestStream; - requestStream.setProfileToken(d->selectedProfile.token()); - if (d->preferredVideoStreamProtocol == "RtspOverHttp") { - OnvifSoapMedia::TT__StreamSetup streamSetup; - streamSetup.setStream(OnvifSoapMedia::TT__StreamType::RTP_Unicast); - OnvifSoapMedia::TT__Transport transport; - transport.setProtocol(OnvifSoapMedia::TT__TransportProtocol::HTTP); - streamSetup.setTransport(transport); - requestStream.setStreamSetup(streamSetup); - } else if (d->preferredVideoStreamProtocol == "RtspUnicast") { - OnvifSoapMedia::TT__StreamSetup streamSetup; - streamSetup.setStream(OnvifSoapMedia::TT__StreamType::RTP_Unicast); - OnvifSoapMedia::TT__Transport transport; - transport.setProtocol(OnvifSoapMedia::TT__TransportProtocol::UDP); - streamSetup.setTransport(transport); - requestStream.setStreamSetup(streamSetup); - } else if (d->preferredVideoStreamProtocol == "RTSP") { - OnvifSoapMedia::TT__StreamSetup streamSetup; - streamSetup.setStream(OnvifSoapMedia::TT__StreamType::RTP_Unicast); - OnvifSoapMedia::TT__Transport transport; - transport.setProtocol(OnvifSoapMedia::TT__TransportProtocol::RTSP); - streamSetup.setTransport(transport); - requestStream.setStreamSetup(streamSetup); - } else if (!d->preferredVideoStreamProtocol.isEmpty()) { - qWarning() << "Warning: unknown preferredVideoStreamProtocol" << d->preferredVideoStreamProtocol; - } - d->device->d_ptr->updateSoapCredentials(d->soapService.clientInterface()); - d->soapService.asyncGetStreamUri(requestStream); + d->device->d_ptr->getSystemDateAndTime(std::bind(&OnvifMediaServicePrivate::selectProfile, d)); } bool OnvifMediaService::supportsSnapshotUri() const @@ -210,6 +179,43 @@ void OnvifMediaService::setPreferredVideoStreamProtocol(const QString& preferred d->preferredVideoStreamProtocol = preferredVideoStreamProtocol; } +void OnvifMediaServicePrivate::selectProfile() +{ + OnvifSoapMedia::TRT__GetSnapshotUri requestSnapshot; + requestSnapshot.setProfileToken(selectedProfile.token()); + device->d_ptr->updateSoapCredentials(soapService.clientInterface()); + soapService.asyncGetSnapshotUri(requestSnapshot); + + OnvifSoapMedia::TRT__GetStreamUri requestStream; + requestStream.setProfileToken(selectedProfile.token()); + if (preferredVideoStreamProtocol == "RtspOverHttp") { + OnvifSoapMedia::TT__StreamSetup streamSetup; + streamSetup.setStream(OnvifSoapMedia::TT__StreamType::RTP_Unicast); + OnvifSoapMedia::TT__Transport transport; + transport.setProtocol(OnvifSoapMedia::TT__TransportProtocol::HTTP); + streamSetup.setTransport(transport); + requestStream.setStreamSetup(streamSetup); + } else if (preferredVideoStreamProtocol == "RtspUnicast") { + OnvifSoapMedia::TT__StreamSetup streamSetup; + streamSetup.setStream(OnvifSoapMedia::TT__StreamType::RTP_Unicast); + OnvifSoapMedia::TT__Transport transport; + transport.setProtocol(OnvifSoapMedia::TT__TransportProtocol::UDP); + streamSetup.setTransport(transport); + requestStream.setStreamSetup(streamSetup); + } else if (preferredVideoStreamProtocol == "RTSP") { + OnvifSoapMedia::TT__StreamSetup streamSetup; + streamSetup.setStream(OnvifSoapMedia::TT__StreamType::RTP_Unicast); + OnvifSoapMedia::TT__Transport transport; + transport.setProtocol(OnvifSoapMedia::TT__TransportProtocol::RTSP); + streamSetup.setTransport(transport); + requestStream.setStreamSetup(streamSetup); + } else if (!preferredVideoStreamProtocol.isEmpty()) { + qWarning() << "Warning: unknown preferredVideoStreamProtocol" << preferredVideoStreamProtocol; + } + device->d_ptr->updateSoapCredentials(soapService.clientInterface()); + soapService.asyncGetStreamUri(requestStream); +} + void OnvifMediaServicePrivate::getServiceCapabilitiesDone(const TRT__GetServiceCapabilitiesResponse& parameters) { Q_Q(OnvifMediaService); @@ -262,7 +268,10 @@ void OnvifMediaServicePrivate::getSnapshotUriDone(const OnvifSoapMedia::TRT__Get void OnvifMediaServicePrivate::getSnapshotUriError(const KDSoapMessage& fault) { - device->d_ptr->handleSoapError(fault, Q_FUNC_INFO_AS_STRING); + Q_Q(OnvifMediaService); + qDebug() << "The GetSnapshotUri call failed:" << fault.faultAsString(); + supportsSnapshotUri = false; + emit q->supportsSnapshotUriAvailable(supportsSnapshotUri); } void OnvifMediaServicePrivate::getStreamUriDone(const OnvifSoapMedia::TRT__GetStreamUriResponse& parameters) diff --git a/libOnvifConnect/onvifptzservice.cpp b/libOnvifConnect/onvifptzservice.cpp index 3481fb6..b0ac3ac 100644 --- a/libOnvifConnect/onvifptzservice.cpp +++ b/libOnvifConnect/onvifptzservice.cpp @@ -21,6 +21,7 @@ #include #include #include "wsdl_ptz.h" +#include using namespace OnvifSoapPtz; @@ -42,6 +43,9 @@ class OnvifPtzServicePrivate OnvifDeviceConnection* device; OnvifSoapPtz::PTZBindingService soapService; QList< OnvifSoapPtz::TT__PTZNode > nodeList; + QList< OnvifSoapPtz::TT__PTZConfiguration > configurationList; + QList< OnvifSoapPtz::TT__PTZConfigurationOptions > configurationOptionsList; + QStringList configurationNameList; bool recievedServiceCapabilities; void getServiceCapabilitiesDone(const OnvifSoapPtz::TPTZ__GetServiceCapabilitiesResponse& parameters); @@ -64,6 +68,8 @@ class OnvifPtzServicePrivate void setHomePositionError(const KDSoapMessage& fault); void stopDone(const OnvifSoapPtz::TPTZ__StopResponse& parameters); void stopError(const KDSoapMessage& fault); + void setConfigurationDone(const OnvifSoapPtz::TPTZ__SetConfigurationResponse& parameters); + void setConfigurationError(const KDSoapMessage& fault); }; OnvifPtzService::OnvifPtzService(const QString& endpointAddress, OnvifDeviceConnection* parent) : @@ -153,6 +159,14 @@ OnvifPtzService::OnvifPtzService(const QString& endpointAddress, OnvifDeviceConn [d](const KDSoapMessage & fault) { d->stopError(fault); }); + connect(&d->soapService, &PTZBindingService::setConfigurationDone, + [d](const OnvifSoapPtz::TPTZ__SetConfigurationResponse & parameters) { + d->setConfigurationDone(parameters); + }); + connect(&d->soapService, &PTZBindingService::setConfigurationError, + [d](const KDSoapMessage & fault) { + d->setConfigurationError(fault); + }); } OnvifPtzService::~OnvifPtzService() = default; @@ -187,7 +201,7 @@ void OnvifPtzService::setServiceCapabilities(const OnvifSoapPtz::TPTZ__Capabilit void OnvifPtzService::setServiceCapabilities(const OnvifSoapDevicemgmt::TT__PTZCapabilities& /*capabilities*/) { - // No useful capabilities are defines + // No useful capabilities are defined } void OnvifPtzService::absoluteMove(const OnvifMediaProfile& profile, float xFraction, float yFraction) @@ -310,6 +324,515 @@ void OnvifPtzService::stopMovement(const OnvifMediaProfile& profile) d->soapService.asyncStop(request); } +void OnvifPtzService::getStatus(const OnvifMediaProfile& profile) +{ + Q_D(OnvifPtzService); + OnvifSoapPtz::TPTZ__GetStatus request; + request.setProfileToken(profile.token()); + + d->device->d_ptr->updateSoapCredentials(d->soapService.clientInterface()); + d->soapService.asyncGetStatus(request); +} + +void OnvifPtzService::setConfiguration(const QString& configuration) +{ + Q_D(OnvifPtzService); + OnvifSoapPtz::TPTZ__SetConfiguration request; + for (auto& config : d->configurationList) { + if (config.name().value() == configuration) { + request.setPTZConfiguration(config); + break; + } + } + + d->device->d_ptr->updateSoapCredentials(d->soapService.clientInterface()); + d->soapService.asyncSetConfiguration(request); +} + +bool OnvifPtzService::isSpaceSupported(const OnvifMediaProfile &profile, PTZSpaces space, const QString& uri) const +{ + bool found = false; + Q_D(const OnvifPtzService); + for(auto& node : d->nodeList) { + if (node.name().value() != profile.ptzNodeToken()) { + continue; + } + switch(space) { + case AbsolutePanTiltPositionSpace: + if (node.supportedPTZSpaces().hasValueForAbsolutePanTiltPositionSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().absolutePanTiltPositionSpace()) { + found = ptzSpace.uRI().contains(uri); + if (found) { + break; + } + } + } + break; + case AbsoluteZoomPositionSpace: + if (node.supportedPTZSpaces().hasValueForAbsoluteZoomPositionSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().absoluteZoomPositionSpace()) { + found = ptzSpace.uRI().contains(uri); + if (found) { + break; + } + } + } + break; + case RelativePanTiltTranslationSpace: + if (node.supportedPTZSpaces().hasValueForRelativePanTiltTranslationSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().relativePanTiltTranslationSpace()) { + found = ptzSpace.uRI().contains(uri); + if (found) { + break; + } + } + } + break; + case RelativeZoomTranslationSpace: + if (node.supportedPTZSpaces().hasValueForRelativeZoomTranslationSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().relativeZoomTranslationSpace()) { + found = ptzSpace.uRI().contains(uri); + if (found) { + break; + } + } + } + break; + case ContinuousPanTiltVelocitySpace: + if (node.supportedPTZSpaces().hasValueForContinuousPanTiltVelocitySpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().continuousPanTiltVelocitySpace()) { + found = ptzSpace.uRI().contains(uri); + if (found) { + break; + } + } + } + break; + case ContinuousZoomVelocitySpace: + if (node.supportedPTZSpaces().hasValueForContinuousZoomVelocitySpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().continuousZoomVelocitySpace()) { + found = ptzSpace.uRI().contains(uri); + if (found) { + break; + } + } + } + break; + case PanTiltSpeedSpace: + if (node.supportedPTZSpaces().hasValueForPanTiltSpeedSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().panTiltSpeedSpace()) { + found = ptzSpace.uRI().contains(uri); + if (found) { + break; + } + } + } + break; + case ZoomSpeedSpace: + if (node.supportedPTZSpaces().hasValueForZoomSpeedSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().zoomSpeedSpace()) { + found = ptzSpace.uRI().contains(uri); + if (found) { + break; + } + } + } + break; + default: + continue; + } + if (found) { + break; + } + } + + return found; +} + +qreal OnvifPtzService::panSpaceMax(const OnvifMediaProfile &profile, PTZSpaces space, const QString& uri) const +{ + qreal max = std::numeric_limits::quiet_NaN(); + Q_D(const OnvifPtzService); + for(auto& node : d->nodeList) { + if (node.name().value() != profile.ptzNodeToken()) { + continue; + } + switch(space) { + case AbsolutePanTiltPositionSpace: + if (node.supportedPTZSpaces().hasValueForAbsolutePanTiltPositionSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().absolutePanTiltPositionSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.xRange().max(); + break; + } + } + } + break; + case RelativePanTiltTranslationSpace: + if (node.supportedPTZSpaces().hasValueForRelativePanTiltTranslationSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().relativePanTiltTranslationSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.xRange().max(); + break; + } + } + } + break; + case ContinuousPanTiltVelocitySpace: + if (node.supportedPTZSpaces().hasValueForContinuousPanTiltVelocitySpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().continuousPanTiltVelocitySpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.xRange().max(); + break; + } + } + } + break; + case PanTiltSpeedSpace: + if (node.supportedPTZSpaces().hasValueForPanTiltSpeedSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().panTiltSpeedSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.xRange().max(); + break; + } + } + } + break; + default: + continue; + } + if (!std::isnan(max)) { + break; + } + } + + return max; +} + +qreal OnvifPtzService::panSpaceMin(const OnvifMediaProfile &profile, PTZSpaces space, const QString& uri) const +{ + qreal min = std::numeric_limits::quiet_NaN(); + Q_D(const OnvifPtzService); + for(auto& node : d->nodeList) { + if (node.name().value() != profile.ptzNodeToken()) { + continue; + } + switch(space) { + case AbsolutePanTiltPositionSpace: + if (node.supportedPTZSpaces().hasValueForAbsolutePanTiltPositionSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().absolutePanTiltPositionSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.xRange().min(); + break; + } + } + } + break; + case RelativePanTiltTranslationSpace: + if (node.supportedPTZSpaces().hasValueForRelativePanTiltTranslationSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().relativePanTiltTranslationSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.xRange().min(); + break; + } + } + } + break; + case ContinuousPanTiltVelocitySpace: + if (node.supportedPTZSpaces().hasValueForContinuousPanTiltVelocitySpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().continuousPanTiltVelocitySpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.xRange().min(); + break; + } + } + } + break; + case PanTiltSpeedSpace: + if (node.supportedPTZSpaces().hasValueForPanTiltSpeedSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().panTiltSpeedSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.xRange().min(); + break; + } + } + } + break; + default: + continue; + } + if (!std::isnan(min)) { + break; + } + } + + return min; +} + +qreal OnvifPtzService::tiltSpaceMax(const OnvifMediaProfile& profile, PTZSpaces space, const QString& uri) const +{ + qreal max = std::numeric_limits::quiet_NaN(); + Q_D(const OnvifPtzService); + for(auto& node : d->nodeList) { + if (node.name().value() != profile.ptzNodeToken()) { + continue; + } + switch(space) { + case AbsolutePanTiltPositionSpace: + if (node.supportedPTZSpaces().hasValueForAbsolutePanTiltPositionSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().absolutePanTiltPositionSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.yRange().max(); + break; + } + } + } + break; + case RelativePanTiltTranslationSpace: + if (node.supportedPTZSpaces().hasValueForRelativePanTiltTranslationSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().relativePanTiltTranslationSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.yRange().max(); + break; + } + } + } + break; + case ContinuousPanTiltVelocitySpace: + if (node.supportedPTZSpaces().hasValueForContinuousPanTiltVelocitySpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().continuousPanTiltVelocitySpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.yRange().max(); + break; + } + } + } + break; + case PanTiltSpeedSpace: + if (node.supportedPTZSpaces().hasValueForPanTiltSpeedSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().panTiltSpeedSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.xRange().max(); + break; + } + } + } + break; + default: + continue; + } + if (!std::isnan(max)) { + break; + } + } + + return max; +} + +qreal OnvifPtzService::tiltSpaceMin(const OnvifMediaProfile& profile, PTZSpaces space, const QString& uri) const +{ + qreal min = std::numeric_limits::quiet_NaN(); + Q_D(const OnvifPtzService); + for(auto& node : d->nodeList) { + if (node.name().value() != profile.ptzNodeToken()) { + continue; + } + switch(space) { + case AbsolutePanTiltPositionSpace: + if (node.supportedPTZSpaces().hasValueForAbsolutePanTiltPositionSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().absolutePanTiltPositionSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.yRange().min(); + break; + } + } + } + break; + case RelativePanTiltTranslationSpace: + if (node.supportedPTZSpaces().hasValueForRelativePanTiltTranslationSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().relativePanTiltTranslationSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.yRange().min(); + break; + } + } + } + break; + case ContinuousPanTiltVelocitySpace: + if (node.supportedPTZSpaces().hasValueForContinuousPanTiltVelocitySpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().continuousPanTiltVelocitySpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.yRange().min(); + break; + } + } + } + break; + case PanTiltSpeedSpace: + if (node.supportedPTZSpaces().hasValueForPanTiltSpeedSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().panTiltSpeedSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.xRange().min(); + break; + } + } + } + break; + default: + continue; + } + if (!std::isnan(min)) { + break; + } + } + + return min; +} + +qreal OnvifPtzService::zoomSpaceMax(const OnvifMediaProfile& profile, PTZSpaces space, const QString& uri) const +{ + qreal max = std::numeric_limits::quiet_NaN(); + Q_D(const OnvifPtzService); + for(auto& node : d->nodeList) { + if (node.name().value() != profile.ptzNodeToken()) { + continue; + } + switch(space) { + case AbsoluteZoomPositionSpace: + if (node.supportedPTZSpaces().hasValueForAbsoluteZoomPositionSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().absoluteZoomPositionSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.xRange().max(); + break; + } + } + } + break; + case RelativeZoomTranslationSpace: + if (node.supportedPTZSpaces().hasValueForRelativeZoomTranslationSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().relativeZoomTranslationSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.xRange().max(); + break; + } + } + } + break; + case ContinuousZoomVelocitySpace: + if (node.supportedPTZSpaces().hasValueForContinuousZoomVelocitySpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().continuousZoomVelocitySpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.xRange().max(); + break; + } + } + } + break; + case ZoomSpeedSpace: + if (node.supportedPTZSpaces().hasValueForZoomSpeedSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().zoomSpeedSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + max = ptzSpace.xRange().max(); + break; + } + } + } + break; + default: + continue; + } + if (!std::isnan(max)) { + break; + } + } + + return max; +} + +qreal OnvifPtzService::zoomSpaceMin(const OnvifMediaProfile& profile, PTZSpaces space, const QString& uri) const +{ + qreal min = std::numeric_limits::quiet_NaN(); + Q_D(const OnvifPtzService); + for(auto& node : d->nodeList) { + if (node.name().value() != profile.ptzNodeToken()) { + continue; + } + switch(space) { + case AbsoluteZoomPositionSpace: + if (node.supportedPTZSpaces().hasValueForAbsoluteZoomPositionSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().absoluteZoomPositionSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.xRange().min(); + break; + } + } + } + break; + case RelativeZoomTranslationSpace: + if (node.supportedPTZSpaces().hasValueForRelativeZoomTranslationSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().relativeZoomTranslationSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.xRange().min(); + break; + } + } + } + break; + case ContinuousZoomVelocitySpace: + if (node.supportedPTZSpaces().hasValueForContinuousZoomVelocitySpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().continuousZoomVelocitySpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.xRange().min(); + break; + } + } + } + break; + case ZoomSpeedSpace: + if (node.supportedPTZSpaces().hasValueForZoomSpeedSpace()) { + for (auto& ptzSpace : node.supportedPTZSpaces().zoomSpeedSpace()) { + const bool found = ptzSpace.uRI().contains(uri); + if (found) { + min = ptzSpace.xRange().min(); + break; + } + } + } + break; + default: + continue; + } + if (!std::isnan(min)) { + break; + } + } + + return min; +} + bool OnvifPtzService::isHomeSupported(const OnvifMediaProfile& profile) const { Q_D(const OnvifPtzService); @@ -371,9 +894,17 @@ void OnvifPtzServicePrivate::getNodesError(const KDSoapMessage& fault) device->d_ptr->handleSoapError(fault, Q_FUNC_INFO_AS_STRING); } -void OnvifPtzServicePrivate::getConfigurationsDone(const TPTZ__GetConfigurationsResponse& /*parameters*/) +void OnvifPtzServicePrivate::getConfigurationsDone(const TPTZ__GetConfigurationsResponse& parameters) { - // TODO: What can we do with the the PTZ configuration + configurationList = parameters.pTZConfiguration(); + configurationNameList.clear(); + configurationOptionsList.clear(); + for (const auto& config : configurationList) { + configurationNameList << config.name().value(); + } + if (!configurationNameList.isEmpty()) { + emit q_ptr->configurationsChanged(configurationNameList); + } } void OnvifPtzServicePrivate::getConfigurationsError(const KDSoapMessage& fault) @@ -381,9 +912,17 @@ void OnvifPtzServicePrivate::getConfigurationsError(const KDSoapMessage& fault) device->d_ptr->handleSoapError(fault, Q_FUNC_INFO_AS_STRING); } -void OnvifPtzServicePrivate::getStatusDone(const OnvifSoapPtz::TPTZ__GetStatusResponse& /*parameters*/) +void OnvifPtzServicePrivate::getStatusDone(const OnvifSoapPtz::TPTZ__GetStatusResponse& parameters) { - //TODO: What can we do with the PTZ status? + if(parameters.pTZStatus().hasValueForPosition()) { + if (parameters.pTZStatus().position().hasValueForPanTilt()) { + q_ptr->panChanged(parameters.pTZStatus().position().panTilt().x()); + q_ptr->tiltChanged(parameters.pTZStatus().position().panTilt().y()); + } + if (parameters.pTZStatus().position().hasValueForZoom()) { + q_ptr->zoomChanged(parameters.pTZStatus().position().zoom().x()); + } + } } void OnvifPtzServicePrivate::getStatusError(const KDSoapMessage& fault) @@ -449,3 +988,12 @@ void OnvifPtzServicePrivate::stopError(const KDSoapMessage& fault) { device->d_ptr->handleSoapError(fault, Q_FUNC_INFO_AS_STRING); } + +void OnvifPtzServicePrivate::setConfigurationDone(const OnvifSoapPtz::TPTZ__SetConfigurationResponse& /*parameters*/) +{ +} + +void OnvifPtzServicePrivate::setConfigurationError(const KDSoapMessage& fault) +{ + device->d_ptr->handleSoapError(fault, Q_FUNC_INFO_AS_STRING); +} diff --git a/libOnvifConnect/onvifptzservice.h b/libOnvifConnect/onvifptzservice.h index fc48f46..0a58700 100644 --- a/libOnvifConnect/onvifptzservice.h +++ b/libOnvifConnect/onvifptzservice.h @@ -64,7 +64,42 @@ class ONVIFCONNECT_EXPORT OnvifPtzService : public QObject void saveHomePosition(const OnvifMediaProfile& profile); void stopMovement(const OnvifMediaProfile& profile); -}; + void getStatus(const OnvifMediaProfile& profile); + + void setConfiguration(const QString& configuration); + + enum PTZSpaces { + AbsolutePanTiltPositionSpace, + AbsoluteZoomPositionSpace, + RelativePanTiltTranslationSpace, + RelativeZoomTranslationSpace, + ContinuousPanTiltVelocitySpace, + ContinuousZoomVelocitySpace, + PanTiltSpeedSpace, + ZoomSpeedSpace + }; + Q_ENUM(PTZSpaces) + + bool isSpaceSupported(const OnvifMediaProfile& profile, PTZSpaces space, const QString& uri) const; + + qreal panSpaceMax(const OnvifMediaProfile& profile, PTZSpaces space, const QString& uri) const; + + qreal panSpaceMin(const OnvifMediaProfile& profile, PTZSpaces space, const QString& uri) const; + + qreal tiltSpaceMax(const OnvifMediaProfile& profile, PTZSpaces space, const QString& uri) const; + + qreal tiltSpaceMin(const OnvifMediaProfile& profile, PTZSpaces space, const QString& uri) const; + + qreal zoomSpaceMax(const OnvifMediaProfile& profile, PTZSpaces space, const QString& uri) const; + + qreal zoomSpaceMin(const OnvifMediaProfile& profile, PTZSpaces space, const QString& uri) const; + +signals: + void configurationsChanged(const QStringList&); + void panChanged(qreal); + void tiltChanged(qreal); + void zoomChanged(qreal); +}; #endif // ONVIFPTZSERVICE_H diff --git a/src/DiscoverCamera.qml b/src/DiscoverCamera.qml index 59ed551..5535a46 100644 --- a/src/DiscoverCamera.qml +++ b/src/DiscoverCamera.qml @@ -56,8 +56,8 @@ Kirigami.ScrollablePage { selectedIndex = deviceManager.appendDevice() var newDevice = deviceManager.at(selectedIndex) newDevice.deviceName = modelData.name; - console.log(modelData.xAddr) - newDevice.hostName = modelData.host; + console.log(modelData.xAddr[0]) + newDevice.hostName = modelData.host[0]; newDevice.connectToDevice(); deviceManager.saveDevices() diff --git a/src/onvifdevice.cpp b/src/onvifdevice.cpp index 00cf71d..e033411 100644 --- a/src/onvifdevice.cpp +++ b/src/onvifdevice.cpp @@ -28,7 +28,11 @@ OnvifDevice::OnvifDevice(QObject* parent) : QObject(parent), m_preferContinuousMove(false), m_cachedDeviceInformation(new OnvifDeviceInformation(this)), - m_cachedSnapshotDownloader(new OnvifSnapshotDownloader(this)) + m_cachedSnapshotDownloader(new OnvifSnapshotDownloader(this)), + m_pan(std::numeric_limits::quiet_NaN()), + m_tilt(std::numeric_limits::quiet_NaN()), + m_zoom(std::numeric_limits::quiet_NaN()), + m_getPTZStatusInterval(-1) { connect(&m_connection, &OnvifDeviceConnection::servicesAvailable, this, &OnvifDevice::servicesAvailable); @@ -39,6 +43,10 @@ OnvifDevice::OnvifDevice(QObject* parent) : connect(&m_ptzStopTimer, &QTimer::timeout, this, &OnvifDevice::ptzStop); + m_getPTZStatusTimer.setInterval(std::numeric_limits::max()); + connect(&m_getPTZStatusTimer, &QTimer::timeout, + this, &OnvifDevice::getPTZStatus); + // TODO: Figure out why qRegisterMetaType is needed, when we already called Q_DECLARE_METATYPE qRegisterMetaType ("OnvifDeviceInformation"); } @@ -130,6 +138,7 @@ void OnvifDevice::servicesAvailable() OnvifMediaService* mediaService = device->getMediaService(); OnvifMedia2Service* media2Service = device->getMedia2Service(); + OnvifPtzService* ptzService = device->getPtzService(); if (media2Service) { media2Service->setPreferredVideoStreamProtocol(preferredVideoStreamProtocol()); connect(media2Service, &OnvifMedia2Service::profileListAvailable, @@ -155,6 +164,16 @@ void OnvifDevice::servicesAvailable() connect(mediaService, &OnvifMediaService::snapshotUriAvailable, m_cachedSnapshotDownloader, &OnvifSnapshotDownloader::setSnapshotUri); } + if (ptzService) { + connect(ptzService, &OnvifPtzService::configurationsChanged, + this, &OnvifDevice::ptzConfigurationsAvailable); + connect(ptzService, &OnvifPtzService::panChanged, + this, &OnvifDevice::setPan); + connect(ptzService, &OnvifPtzService::tiltChanged, + this, &OnvifDevice::setTilt); + connect(ptzService, &OnvifPtzService::zoomChanged, + this, &OnvifDevice::setZoom); + } } bool mediaProfileLessThan(const OnvifMediaProfile& p1, const OnvifMediaProfile& p2) @@ -194,16 +213,60 @@ void OnvifDevice::profileListAvailable(const QList& profileLi Q_ASSERT(mediaService || media2Service); Q_ASSERT(profileList.size()); - //TODO: Add a proper profile selection - auto sortedProfileList = profileList; - qSort(sortedProfileList.begin(), sortedProfileList.end(), mediaProfileLessThan); - m_selectedMediaProfile = sortedProfileList.first(); + + m_sortedProfileList = profileList; + qSort(m_sortedProfileList.begin(), m_sortedProfileList.end(), mediaProfileLessThan); + m_profileNames.clear(); + for(auto profile: m_sortedProfileList) + { + m_profileNames << profile.name(); + } + emit profileNamesChanged(m_profileNames); + selectMediaProfile(0); +} - if (media2Service) { - media2Service->selectProfile(m_selectedMediaProfile); - } else if (mediaService) { - mediaService->selectProfile(m_selectedMediaProfile); +void OnvifDevice::getPTZStatus() +{ + OnvifPtzService* ptzService = m_connection.getPtzService(); + if (ptzService) { + ptzService->getStatus(m_selectedMediaProfile); + } +} + +void OnvifDevice::setPan(qreal pan) +{ + if (pan == m_pan) { + return; + } + m_pan = pan; + emit panChanged(m_pan); +} + +void OnvifDevice::setTilt(qreal tilt) +{ + if (tilt == m_tilt) { + return; + } + m_tilt = tilt; + emit tiltChanged(m_tilt); +} + +void OnvifDevice::setZoom(qreal zoom) +{ + if (zoom == m_zoom) { + return; + } + m_zoom = zoom; + emit zoomChanged(m_zoom); +} + +void OnvifDevice::ptzConfigurationsAvailable(const QStringList& configurationNames) +{ + if (m_ptzConfigurationNames == configurationNames) { + return; } + m_ptzConfigurationNames = configurationNames; + emit ptzConfigurationNamesChanged(m_ptzConfigurationNames); } QString OnvifDevice::preferredVideoStreamProtocol() const @@ -236,6 +299,11 @@ void OnvifDevice::initByUrl(const QUrl& url) } } +QStringList OnvifDevice::profileNames() const +{ + return m_profileNames; +} + bool OnvifDevice::preferContinuousMove() const { return m_preferContinuousMove; @@ -320,6 +388,100 @@ bool OnvifDevice::isZoomSupported() const return false; } +qreal OnvifDevice::pan() const +{ + return m_pan; +} + +qreal OnvifDevice::tilt() const +{ + return m_tilt; +} + +qreal OnvifDevice::zoom() const +{ + return m_zoom; +} + +int OnvifDevice::getPTZStatusInterval() const +{ + return m_getPTZStatusInterval; +} + +QStringList OnvifDevice::ptzConfigurationNames() const +{ + return m_ptzConfigurationNames; +} + +bool OnvifDevice::isPtzSpaceSupported(int space, const QString& uri) const +{ + const OnvifPtzService* ptzService = m_connection.getPtzService(); + if (ptzService) { + return ptzService->isSpaceSupported(m_selectedMediaProfile, static_cast(space), uri); + } + return false; +} + +qreal OnvifDevice::panSpaceMax(int space, const QString& uri) const +{ + qreal max = std::numeric_limits::quiet_NaN(); + const OnvifPtzService* ptzService = m_connection.getPtzService(); + if (ptzService) { + return ptzService->panSpaceMax(m_selectedMediaProfile, static_cast(space), uri); + } + return max; +} + +qreal OnvifDevice::panSpaceMin(int space, const QString& uri) const +{ + qreal min = std::numeric_limits::quiet_NaN(); + const OnvifPtzService* ptzService = m_connection.getPtzService(); + if (ptzService) { + return ptzService->panSpaceMin(m_selectedMediaProfile, static_cast(space), uri); + } + return min; +} + +qreal OnvifDevice::tiltSpaceMax(int space, const QString& uri) const +{ + qreal max = std::numeric_limits::quiet_NaN(); + const OnvifPtzService* ptzService = m_connection.getPtzService(); + if (ptzService) { + return ptzService->tiltSpaceMax(m_selectedMediaProfile, static_cast(space), uri); + } + return max; +} + +qreal OnvifDevice::tiltSpaceMin(int space, const QString& uri) const +{ + qreal min = std::numeric_limits::quiet_NaN(); + const OnvifPtzService* ptzService = m_connection.getPtzService(); + if (ptzService) { + return ptzService->tiltSpaceMin(m_selectedMediaProfile, static_cast(space), uri); + } + return min; +} + +qreal OnvifDevice::zoomSpaceMax(int space, const QString& uri) const +{ + qreal max = std::numeric_limits::quiet_NaN(); + const OnvifPtzService* ptzService = m_connection.getPtzService(); + if (ptzService) { + return ptzService->zoomSpaceMax(m_selectedMediaProfile, static_cast(space), uri); + } + return max; +} + +qreal OnvifDevice::zoomSpaceMin(int space, const QString& uri) const +{ + qreal min = std::numeric_limits::quiet_NaN(); + const OnvifPtzService* ptzService = m_connection.getPtzService(); + if (ptzService) { + return ptzService->zoomSpaceMin(m_selectedMediaProfile, static_cast(space), uri); + } + return min; +} + QString OnvifDevice::userName() const { return m_userName; @@ -416,3 +578,42 @@ void OnvifDevice::ptzZoomOut() Q_ASSERT(ptzService); ptzService->relativeZoom(m_selectedMediaProfile, -0.1f); } + +void OnvifDevice::selectMediaProfile(int index) +{ + OnvifMedia2Service* media2Service = m_connection.getMedia2Service(); + OnvifMediaService* mediaService = m_connection.getMediaService(); + + if (m_selectedMediaProfile.name() != m_sortedProfileList[index].name()) + { + m_selectedMediaProfile = m_sortedProfileList[index]; + + if(media2Service) + media2Service->selectProfile(m_selectedMediaProfile); + else if(mediaService) + mediaService->selectProfile(m_selectedMediaProfile); + + m_getPTZStatusTimer.start(); + } +} + +void OnvifDevice::setGetPTZStatusInterval(int interval) +{ + if (m_getPTZStatusInterval == interval) { + return; + } + m_getPTZStatusInterval = interval; + emit getPTZStatusIntervalChanged(m_getPTZStatusInterval); + int minInterval = std::numeric_limits::max(); + if (interval != -1) { + minInterval = interval; + } + m_getPTZStatusTimer.setInterval(minInterval); +} + +void OnvifDevice::selectPTZConfiguration(const QString& configurationName) +{ + OnvifPtzService* ptzService = m_connection.getPtzService(); + Q_ASSERT(ptzService); + ptzService->setConfiguration(configurationName); +} diff --git a/src/onvifdevice.h b/src/onvifdevice.h index fd9ac16..059ed55 100644 --- a/src/onvifdevice.h +++ b/src/onvifdevice.h @@ -44,6 +44,12 @@ class OnvifDevice : public QObject Q_PROPERTY(bool isPtzHomeSupported READ isPtzHomeSupported) Q_PROPERTY(bool isZoomSupported READ isZoomSupported) Q_PROPERTY(OnvifSnapshotDownloader* snapshotDownloader READ snapshotDownloader NOTIFY snapshotDownloaderChanged) + Q_PROPERTY(QStringList profileNames READ profileNames NOTIFY profileNamesChanged) + Q_PROPERTY(qreal pan READ pan NOTIFY panChanged) + Q_PROPERTY(qreal tilt READ tilt NOTIFY tiltChanged) + Q_PROPERTY(qreal zoom READ zoom NOTIFY zoomChanged) + Q_PROPERTY(int getPTZStatusInterval READ getPTZStatusInterval WRITE setGetPTZStatusInterval NOTIFY getPTZStatusIntervalChanged) + Q_PROPERTY(QStringList ptzConfigurationNames READ ptzConfigurationNames NOTIFY ptzConfigurationNamesChanged) public: explicit OnvifDevice(QObject* parent = nullptr); @@ -80,6 +86,30 @@ class OnvifDevice : public QObject void setPreferredVideoStreamProtocol(const QString& preferredVideoStreamProtocol); void initByUrl(const QUrl& url); + + QStringList profileNames() const; + + qreal pan() const; + qreal tilt() const; + qreal zoom() const; + + int getPTZStatusInterval() const; + + QStringList ptzConfigurationNames() const; + + Q_INVOKABLE bool isPtzSpaceSupported(int space, const QString& uri) const; + + Q_INVOKABLE qreal panSpaceMax(int space, const QString& uri) const; + + Q_INVOKABLE qreal panSpaceMin(int space, const QString& uri) const; + + Q_INVOKABLE qreal tiltSpaceMax(int space, const QString& uri) const; + + Q_INVOKABLE qreal tiltSpaceMin(int space, const QString& uri) const; + + Q_INVOKABLE qreal zoomSpaceMax(int space, const QString& uri) const; + + Q_INVOKABLE qreal zoomSpaceMin(int space, const QString& uri) const; signals: void deviceNameChanged(const QString& deviceName); @@ -94,6 +124,12 @@ class OnvifDevice : public QObject void snapshotUriChanged(const QUrl& url); void streamUriChanged(const QUrl& url); void snapshotDownloaderChanged(OnvifSnapshotDownloader* snapshotDownloader); + void profileNamesChanged(const QStringList& profileNames); + void panChanged(qreal pan); + void tiltChanged(qreal tilt); + void zoomChanged(qreal zoom); + void getPTZStatusIntervalChanged(int interval); + void ptzConfigurationNamesChanged(const QStringList& configurationNames); public slots: void ptzUp(); @@ -106,11 +142,19 @@ public slots: void ptzStop(); void ptzZoomIn(); void ptzZoomOut(); + void selectMediaProfile(int index); + void setGetPTZStatusInterval(int interval); + void selectPTZConfiguration(const QString& configurationName); private slots: void servicesAvailable(); void deviceInformationAvailable(const OnvifDeviceInformation& deviceInformation); void profileListAvailable(const QList& profileList); + void getPTZStatus(); + void setPan(qreal pan); + void setTilt(qreal tilt); + void setZoom(qreal zoom); + void ptzConfigurationsAvailable(const QStringList& configurationNames); private: OnvifDeviceConnection m_connection; @@ -124,6 +168,14 @@ private slots: OnvifDeviceInformation* m_cachedDeviceInformation; QTimer m_ptzStopTimer; OnvifSnapshotDownloader* m_cachedSnapshotDownloader; + QStringList m_profileNames; + QList m_sortedProfileList; + qreal m_pan; + qreal m_tilt; + qreal m_zoom; + int m_getPTZStatusInterval; + QTimer m_getPTZStatusTimer; + QStringList m_ptzConfigurationNames; }; #endif // ONVIFDEVICE_H diff --git a/src/onvifdevicediscover.cpp b/src/onvifdevicediscover.cpp index c7ed106..d5db041 100644 --- a/src/onvifdevicediscover.cpp +++ b/src/onvifdevicediscover.cpp @@ -66,6 +66,13 @@ void OnvifDeviceDiscover::start() #endif } +void OnvifDeviceDiscover::stop() +{ +#ifdef WITH_KDSOAP_WSDISCOVERY_CLIENT + m_probeJob->stop(); +#endif +} + void OnvifDeviceDiscover::matchReceived(const WSDiscoveryTargetService& matchedService) { #ifdef WITH_KDSOAP_WSDISCOVERY_CLIENT @@ -86,8 +93,11 @@ void OnvifDeviceDiscover::matchReceived(const WSDiscoveryTargetService& matchedS } } } + + deviceMatch->m_xAddr = matchedService.xAddrList(); + deviceMatch->m_host.clear(); for (auto& xAddr : matchedService.xAddrList()) { - deviceMatch->m_xAddr = xAddr; + deviceMatch->m_host << xAddr.authority(); } deviceMatch->m_lastSeen = matchedService.lastSeen(); @@ -106,14 +116,14 @@ QString OnvifDeviceDiscoverMatch::getEndpoint() const return m_endpoint; } -QUrl OnvifDeviceDiscoverMatch::getXAddr() const +QList OnvifDeviceDiscoverMatch::getXAddr() const { return m_xAddr; } -QString OnvifDeviceDiscoverMatch::getHost() const +QStringList OnvifDeviceDiscoverMatch::getHost() const { - return m_xAddr.authority(); + return m_host; } QString OnvifDeviceDiscoverMatch::getName() const diff --git a/src/onvifdevicediscover.h b/src/onvifdevicediscover.h index eb3413d..651092e 100644 --- a/src/onvifdevicediscover.h +++ b/src/onvifdevicediscover.h @@ -18,6 +18,7 @@ #define ONVIFDEVICEDISCOVER_H #include +#include #include #include #include @@ -32,20 +33,21 @@ class OnvifDeviceDiscoverMatch : public QObject Q_PROPERTY(QString name READ getName CONSTANT) Q_PROPERTY(QString hardware READ getHardware CONSTANT) Q_PROPERTY(QString endpoint READ getEndpoint CONSTANT) - Q_PROPERTY(QUrl xAddr READ getXAddr CONSTANT) - Q_PROPERTY(QString host READ getHost CONSTANT) + Q_PROPERTY(QList xAddr READ getXAddr CONSTANT) + Q_PROPERTY(QStringList host READ getHost CONSTANT) public: QString getName() const; QString getHardware() const; QString getEndpoint() const; - QUrl getXAddr() const; - QString getHost() const; + QList getXAddr() const; + QStringList getHost() const; protected: QString m_name; QString m_hardware; QString m_endpoint; - QUrl m_xAddr; + QList m_xAddr; + QStringList m_host; QDateTime m_lastSeen; friend class OnvifDeviceDiscover; @@ -66,6 +68,7 @@ class OnvifDeviceDiscover : public QObject public slots: void start(); + void stop(); private slots: void matchReceived(const WSDiscoveryTargetService& matchedService);