diff --git a/LoadingPage.xaml b/LoadingPage.xaml index 72fa68c37aea70fbc142c0205d96be601c8e30c3..9d6641fc701ae824597f9daf98a53573a706b0ff 100644 --- a/LoadingPage.xaml +++ b/LoadingPage.xaml @@ -1,6 +1,7 @@ <!-- ********************************************************************** * Copyright (C) 2016 by Savoir-faire Linux * * Author: Jäger Nicolas<nicolas.jager@savoirfairelinux.com> * +* Author: Traczyk Andreas<andreas.traczyk@savoirfairelinux.com> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -23,8 +24,4 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" NavigationCacheMode="Enabled" mc:Ignorable="d"> - - <Grid> - <TextBlock Text="loading page"/> - </Grid> </Page> diff --git a/MainPage.xaml b/MainPage.xaml index 3aae9ae0a3cf2e3d7d97f212e852989d7bad6147..673aef6292bb01f55aa11c4db73ffdb3f25ebcf2 100644 --- a/MainPage.xaml +++ b/MainPage.xaml @@ -120,6 +120,10 @@ <Frame x:Name="_videoFrame_" Grid.Row="0" Visibility="Visible"/> + <Frame x:Name="_previewFrame_" + Grid.Row="1" + Canvas.ZIndex="99" + Visibility="Collapsed"/> </Grid> </SplitView.Content> </SplitView> diff --git a/MainPage.xaml.cpp b/MainPage.xaml.cpp index 18334536b01dad9da46e6b1740d1d24f4b228ffb..e9805bb48298748b3a35c60e4f3d2da88e3d0dfb 100644 --- a/MainPage.xaml.cpp +++ b/MainPage.xaml.cpp @@ -22,8 +22,11 @@ #include "SmartPanel.xaml.h" #include "RingConsolePanel.xaml.h" #include "VideoPage.xaml.h" +#include "PreviewPage.xaml.h" #include "WelcomePage.xaml.h" +#include "gnutls\gnutls.h" + #include "MainPage.xaml.h" using namespace RingClientUWP; @@ -60,6 +63,7 @@ MainPage::MainPage() _smartPanel_->Navigate(TypeName(RingClientUWP::Views::SmartPanel::typeid)); _consolePanel_->Navigate(TypeName(RingClientUWP::Views::RingConsolePanel::typeid)); _videoFrame_->Navigate(TypeName(RingClientUWP::Views::VideoPage::typeid)); + _previewFrame_->Navigate(TypeName(RingClientUWP::Views::PreviewPage::typeid)); _messageTextFrame_->Navigate(TypeName(RingClientUWP::Views::MessageTextPage::typeid)); /* connect to delegates */ @@ -67,6 +71,8 @@ MainPage::MainPage() auto smartPanel = dynamic_cast<SmartPanel^>(_smartPanel_->Content); smartPanel->summonMessageTextPage += ref new RingClientUWP::SummonMessageTextPage(this, &RingClientUWP::MainPage::OnsummonMessageTextPage); smartPanel->summonWelcomePage += ref new RingClientUWP::SummonWelcomePage(this, &RingClientUWP::MainPage::OnsummonWelcomePage); + smartPanel->summonPreviewPage += ref new RingClientUWP::SummonPreviewPage(this, &RingClientUWP::MainPage::OnsummonPreviewPage); + smartPanel->hidePreviewPage += ref new RingClientUWP::HidePreviewPage(this, &RingClientUWP::MainPage::OnhidePreviewPage); smartPanel->summonVideoPage += ref new RingClientUWP::SummonVideoPage(this, &RingClientUWP::MainPage::OnsummonVideoPage); auto videoPage = dynamic_cast<VideoPage^>(_videoFrame_->Content); videoPage->pressHangUpCall += ref new RingClientUWP::PressHangUpCall(this, &RingClientUWP::MainPage::OnpressHangUpCall); @@ -127,7 +133,10 @@ RingClientUWP::MainPage::showFrame(Windows::UI::Xaml::Controls::Frame^ frame) void RingClientUWP::MainPage::OnNavigatedTo(NavigationEventArgs ^ e) { - RingD::instance->startDaemon(); + gnutls_global_init(); + RingD::instance->registerCallbacks(); + RingD::instance->initDaemon( DRing::DRING_FLAG_CONSOLE_LOG | DRing::DRING_FLAG_DEBUG ); + Video::VideoManager::instance->captureManager()->EnumerateWebcamsAsync(); showLoadingOverlay(true, false); } @@ -231,6 +240,17 @@ void RingClientUWP::MainPage::OnsummonWelcomePage() showFrame(_welcomeFrame_); } +void RingClientUWP::MainPage::OnsummonPreviewPage() +{ + WriteLine("Show Settings Preview"); + _previewFrame_->Visibility = VIS::Visible; +} + +void RingClientUWP::MainPage::OnhidePreviewPage() +{ + WriteLine("Hide Settings Preview"); + _previewFrame_->Visibility = VIS::Collapsed; +} void RingClientUWP::MainPage::OnsummonVideoPage() { @@ -245,8 +265,6 @@ void RingClientUWP::MainPage::OnpressHangUpCall() OnsummonMessageTextPage(); } - - void RingClientUWP::MainPage::OnstateChange(Platform::String ^callId, RingClientUWP::CallStatus state, int code) { auto item = SmartPanelItemsViewModel::instance->_selectedItem; @@ -264,8 +282,7 @@ void RingClientUWP::MainPage::OnstateChange(Platform::String ^callId, RingClient } } -#include <dring.h> -#include "callmanager_interface.h" + void MainPage::Application_Suspending(Object^, Windows::ApplicationModel::SuspendingEventArgs^ e) { @@ -299,9 +316,9 @@ MainPage::Application_Suspending(Object^, Windows::ApplicationModel::SuspendingE void MainPage::Application_VisibilityChanged(Object^ sender, VisibilityChangedEventArgs^ e) { + auto vcm = Video::VideoManager::instance->captureManager(); if (e->Visible) { RingDebug::instance->WriteLine("->Visible"); - auto isPreviewing = Video::VideoManager::instance->captureManager()->isPreviewing; bool isInCall = false; for (auto item : SmartPanelItemsViewModel::instance->itemsList) { if (item->_callId && item->_callStatus == CallStatus::IN_PROGRESS) { @@ -312,13 +329,23 @@ MainPage::Application_VisibilityChanged(Object^ sender, VisibilityChangedEventAr if (isInCall) { /*if (RingD::instance->currentCallId) RingD::instance->unPauseCall(RingD::instance->currentCallId);*/ - Video::VideoManager::instance->captureManager()->InitializeCameraAsync(); - Video::VideoManager::instance->captureManager()->videoFrameCopyInvoker->Start(); + vcm->InitializeCameraAsync(false); + vcm->videoFrameCopyInvoker->Start(); + } + else if (vcm->isSettingsPreviewing) { + vcm->CleanupCameraAsync() + .then([=](task<void> cleanupTask){ + cleanupTask.get(); + CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::High, ref new DispatchedHandler([=]() + { + vcm->InitializeCameraAsync(true); + })); + }); } } else { RingDebug::instance->WriteLine("->Invisible"); - auto isPreviewing = Video::VideoManager::instance->captureManager()->isPreviewing; bool isInCall = false; for (auto item : SmartPanelItemsViewModel::instance->itemsList) { if (item->_callId && item->_callStatus == CallStatus::IN_PROGRESS) { @@ -328,17 +355,21 @@ MainPage::Application_VisibilityChanged(Object^ sender, VisibilityChangedEventAr } } if (isInCall) { + // TODO /*if (RingD::instance->currentCallId) { WriteLine("Pausing call: " + RingD::instance->currentCallId); RingD::instance->pauseCall(RingD::instance->currentCallId); }*/ - if (isPreviewing) { - Video::VideoManager::instance->captureManager()->StopPreviewAsync(); - if (Video::VideoManager::instance->captureManager()->captureTaskTokenSource) - Video::VideoManager::instance->captureManager()->captureTaskTokenSource->cancel(); - Video::VideoManager::instance->captureManager()->videoFrameCopyInvoker->Stop(); + if (vcm->isPreviewing) { + vcm->StopPreviewAsync(); + if (vcm->captureTaskTokenSource) + vcm->captureTaskTokenSource->cancel(); + vcm->videoFrameCopyInvoker->Stop(); } } + else if (vcm->isSettingsPreviewing) { + vcm->StopPreviewAsync(); + } } } @@ -383,11 +414,10 @@ MainPage::BeginExtendedExecution() session = newSession; RingDebug::instance->WriteLine("Request Extended Execution Allowed"); RingDebug::instance->WriteLine("Clean up camera..."); - Video::VideoManager::instance->captureManager()->CleanupCameraAsync() - .then([]() { - RingDebug::instance->WriteLine("Hang up calls..."); - DRing::fini(); - }); + Video::VideoManager::instance->captureManager()->CleanupCameraAsync(); + RingDebug::instance->WriteLine("Hang up calls..."); + DRing::fini(); + gnutls_global_init(); break; default: diff --git a/MainPage.xaml.h b/MainPage.xaml.h index 821efbfe72ebaae238d0c47104a36fb6d0ed4256..7252ac34c8b89c5d8820a0e2f9c8c8fae900375d 100644 --- a/MainPage.xaml.h +++ b/MainPage.xaml.h @@ -71,6 +71,8 @@ private: void showFrame(Windows::UI::Xaml::Controls::Frame^ frame); void OnsummonMessageTextPage(); void OnsummonWelcomePage(); + void OnsummonPreviewPage(); + void OnhidePreviewPage(); void OnsummonVideoPage(); void OnpressHangUpCall(); void OnstateChange(Platform::String ^callId, CallStatus state, int code); diff --git a/MessageTextPage.xaml b/MessageTextPage.xaml index 7c5562cbff67755b43c3792bf5eff778d6d4938d..e72ea6fff22a2daf6e71621b2bd09a12aa3cf7c6 100644 --- a/MessageTextPage.xaml +++ b/MessageTextPage.xaml @@ -1,4 +1,22 @@ -<Page +<!-- ********************************************************************** +* Copyright (C) 2016 by Savoir-faire Linux * +* Author: Jäger Nicolas<nicolas.jager@savoirfairelinux.com> * +* Author: Traczyk Andreas<andreas.traczyk@savoirfairelinux.com> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see <http://www.gnu.org/licenses/> . * +*********************************************************************** --> +<Page x:Class="RingClientUWP.Views.MessageTextPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" diff --git a/Package.appxmanifest b/Package.appxmanifest index c188372732248e3ce7f134c3f48ebad9517167c6..7f835645b6a9a0d58e44bed4e78ab53bba1dc2fa 100644 --- a/Package.appxmanifest +++ b/Package.appxmanifest @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp"> - <Identity Name="Savoir-faireLinux.GNURing" Publisher="CN=8121A5F7-3CA1-4CAA-92B2-4F595B011941" Version="1.1.6.0" /> + <Identity Name="Savoir-faireLinux.GNURing" Publisher="CN=8121A5F7-3CA1-4CAA-92B2-4F595B011941" Version="1.1.10.0" /> <mp:PhoneIdentity PhoneProductId="2385953f-9019-423d-aa82-d1bbacfa258b" PhonePublisherId="00000000-0000-0000-0000-000000000000" /> <Properties> <DisplayName>GNU Ring</DisplayName> diff --git a/PreviewPage.xaml b/PreviewPage.xaml new file mode 100644 index 0000000000000000000000000000000000000000..c043296ea356102b5f1b2f7b3b63d068867ac766 --- /dev/null +++ b/PreviewPage.xaml @@ -0,0 +1,37 @@ +<!-- ********************************************************************** +* Copyright (C) 2016 by Savoir-faire Linux * +* Author: Jäger Nicolas<nicolas.jager@savoirfairelinux.com> * +* Author: Traczyk Andreas<andreas.traczyk@savoirfairelinux.com> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see <http://www.gnu.org/licenses/> . * +*********************************************************************** --> +<Page x:Class="RingClientUWP.Views.PreviewPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:RingClientUWP" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + NavigationCacheMode="Enabled" + mc:Ignorable="d"> + + <Grid x:Name="_previewPage_" + Background="Black"> + <!--camera preview--> + <CaptureElement Name="SettingsPreviewImage" + VerticalAlignment="Center" + HorizontalAlignment="Center" + Stretch="Uniform"/> + </Grid> + +</Page> diff --git a/PreviewPage.xaml.cpp b/PreviewPage.xaml.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a89f5874dcaeb0e5d0a9b39afa8bbd3b3ddf205 --- /dev/null +++ b/PreviewPage.xaml.cpp @@ -0,0 +1,37 @@ +/************************************************************************** +* Copyright (C) 2016 by Savoir-faire Linux * +* Author: J�ger Nicolas <nicolas.jager@savoirfairelinux.com> * +* Author: Traczyk Andreas <andreas.traczyk@savoirfairelinux.com> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see <http://www.gnu.org/licenses/>. * +**************************************************************************/ +#include "pch.h" + +#include "PreviewPage.xaml.h" + +using namespace RingClientUWP; +using namespace RingClientUWP::Views; + +using namespace Video; + +PreviewPage::PreviewPage() +{ + InitializeComponent(); + + VideoManager::instance->captureManager()->getSettingsPreviewSink += + ref new GetSettingsPreviewSink([this]() + { + return SettingsPreviewImage; + }); +}; \ No newline at end of file diff --git a/PreviewPage.xaml.h b/PreviewPage.xaml.h new file mode 100644 index 0000000000000000000000000000000000000000..ef6270d19d902fb0f62af7eaaf259fbe52a91fdc --- /dev/null +++ b/PreviewPage.xaml.h @@ -0,0 +1,34 @@ +/************************************************************************** +* Copyright (C) 2016 by Savoir-faire Linux * +* Author: J�ger Nicolas <nicolas.jager@savoirfairelinux.com> * +* Author: Traczyk Andreas <andreas.traczyk@savoirfairelinux.com> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see <http://www.gnu.org/licenses/>. * +**************************************************************************/ +#pragma once + +#include "PreviewPage.g.h" + +namespace RingClientUWP +{ +namespace Views +{ +public ref class PreviewPage sealed +{ +public: + PreviewPage(); +protected: +}; +} +} \ No newline at end of file diff --git a/RingD.cpp b/RingD.cpp index 8495a12cf6a72db80745464fd55e01be30272669..4bab22f05b9ac9b56733ad1167a371410bb1e5ce 100644 --- a/RingD.cpp +++ b/RingD.cpp @@ -18,8 +18,6 @@ **************************************************************************/ #include "pch.h" -/* daemon */ -#include <dring.h> #include "callmanager_interface.h" #include "configurationmanager_interface.h" #include "presencemanager_interface.h" @@ -39,6 +37,7 @@ using namespace Windows::UI::Core; using namespace Windows::Media; using namespace Windows::Media::MediaProperties; using namespace Windows::Media::Capture; +using namespace Windows::System::Threading; using namespace RingClientUWP; using namespace RingClientUWP::Utils; @@ -116,8 +115,6 @@ RingClientUWP::RingD::reloadAccountList() } } - DRing::lookupName("", "", "wagaf"); - // load user preferences Configuration::UserPreferences::instance->load(); } @@ -365,150 +362,124 @@ void RingClientUWP::RingD::registerThisDevice(String ^ pin, String ^ archivePass } void -RingClientUWP::RingD::startDaemon() +RingD::registerCallbacks() { - if (daemonRunning) { - ERR_("daemon already runnging"); - return; - } - //eraseCacheFolder(); - editModeOn_ = true; + dispatcher = CoreApplication::MainView->CoreWindow->Dispatcher; + + callHandlers = { + // use IncomingCall only to register the call client sided, use StateChange to determine the impact on the UI + DRing::exportable_callback<DRing::CallSignal::IncomingCall>([this]( + const std::string& accountId, + const std::string& callId, + const std::string& from) + { + MSG_("<IncomingCall>"); + MSG_("accountId = " + accountId); + MSG_("callId = " + callId); + MSG_("from = " + from); - create_task([&]() - { - using SharedCallback = std::shared_ptr<DRing::CallbackWrapperBase>; - using namespace std::placeholders; + auto accountId2 = toPlatformString(accountId); + auto callId2 = toPlatformString(callId); + auto from2 = toPlatformString(from); - auto dispatcher = CoreApplication::MainView->CoreWindow->Dispatcher; + /* fix some issue in the daemon --> <...@...> */ + from2 = Utils::TrimRingId(from2); - std::map<std::string, SharedCallback> callHandlers = { - // use IncomingCall only to register the call client sided, use StateChange to determine the impact on the UI - DRing::exportable_callback<DRing::CallSignal::IncomingCall>([this]( - const std::string& accountId, - const std::string& callId, - const std::string& from) + CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::High, ref new DispatchedHandler([=]() { - MSG_("<IncomingCall>"); - MSG_("accountId = " + accountId); - MSG_("callId = " + callId); - MSG_("from = " + from); + incomingCall(accountId2, callId2, from2); + stateChange(callId2, CallStatus::INCOMING_RINGING, 0); - auto accountId2 = toPlatformString(accountId); - auto callId2 = toPlatformString(callId); - auto from2 = toPlatformString(from); + auto contact = ContactsViewModel::instance->findContactByName(from2); + auto item = SmartPanelItemsViewModel::instance->findItem(contact); + item->_callId = callId2; + })); + }), + DRing::exportable_callback<DRing::CallSignal::StateChange>([this]( + const std::string& callId, + const std::string& state, + int code) + { + MSG_("<StateChange>"); + MSG_("callId = " + callId); + MSG_("state = " + state); + MSG_("code = " + std::to_string(code)); - /* fix some issue in the daemon --> <...@...> */ - from2 = Utils::TrimRingId(from2); + auto callId2 = toPlatformString(callId); + auto state2 = toPlatformString(state); - CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( - CoreDispatcherPriority::High, ref new DispatchedHandler([=]() - { - incomingCall(accountId2, callId2, from2); - stateChange(callId2, CallStatus::INCOMING_RINGING, 0); + auto state3 = translateCallStatus(state2); - auto contact = ContactsViewModel::instance->findContactByName(from2); - auto item = SmartPanelItemsViewModel::instance->findItem(contact); - item->_callId = callId2; - })); - }), - DRing::exportable_callback<DRing::CallSignal::StateChange>([this]( - const std::string& callId, - const std::string& state, - int code) - { - MSG_("<StateChange>"); - MSG_("callId = " + callId); - MSG_("state = " + state); - MSG_("code = " + std::to_string(code)); + if (state3 == CallStatus::ENDED) + DRing::hangUp(callId); // solve a bug in the daemon API. - auto callId2 = toPlatformString(callId); - auto state2 = toPlatformString(state); - - auto state3 = translateCallStatus(state2); + CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::High, ref new DispatchedHandler([=]() + { + stateChange(callId2, state3, code); + })); + }), + DRing::exportable_callback<DRing::ConfigurationSignal::IncomingAccountMessage>([&]( + const std::string& accountId, + const std::string& from, + const std::map<std::string, std::string>& payloads) + { + MSG_("<IncomingAccountMessage>"); + MSG_("accountId = " + accountId); + MSG_("from = " + from); - if (state3 == CallStatus::ENDED) - DRing::hangUp(callId); // solve a bug in the daemon API. + auto accountId2 = toPlatformString(accountId); + auto from2 = toPlatformString(from); + for (auto i : payloads) { + MSG_("payload = " + i.second); + auto payload = Utils::toPlatformString(i.second); CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( CoreDispatcherPriority::High, ref new DispatchedHandler([=]() { - stateChange(callId2, state3, code); + incomingAccountMessage(accountId2, from2, payload); })); - }), - DRing::exportable_callback<DRing::ConfigurationSignal::IncomingAccountMessage>([&]( - const std::string& accountId, - const std::string& from, - const std::map<std::string, std::string>& payloads) - { - MSG_("<IncomingAccountMessage>"); - MSG_("accountId = " + accountId); - MSG_("from = " + from); - - auto accountId2 = toPlatformString(accountId); - auto from2 = toPlatformString(from); - - for (auto i : payloads) { - MSG_("payload = " + i.second); - auto payload = Utils::toPlatformString(i.second); - CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( - CoreDispatcherPriority::High, ref new DispatchedHandler([=]() - { - incomingAccountMessage(accountId2, from2, payload); - })); - } - }), - DRing::exportable_callback<DRing::CallSignal::IncomingMessage>([&]( - const std::string& callId, - const std::string& from, - const std::map<std::string, std::string>& payloads) - { - MSG_("<IncomingMessage>"); - MSG_("callId = " + callId); - MSG_("from = " + from); + } + }), + DRing::exportable_callback<DRing::CallSignal::IncomingMessage>([&]( + const std::string& callId, + const std::string& from, + const std::map<std::string, std::string>& payloads) + { + MSG_("<IncomingMessage>"); + MSG_("callId = " + callId); + MSG_("from = " + from); - auto callId2 = toPlatformString(callId); - auto from2 = toPlatformString(from); + auto callId2 = toPlatformString(callId); + auto from2 = toPlatformString(from); - const std::string PROFILE_VCF = "x-ring/ring.profile.vcard"; - static const unsigned int profileSize = PROFILE_VCF.size(); + const std::string PROFILE_VCF = "x-ring/ring.profile.vcard"; + static const unsigned int profileSize = PROFILE_VCF.size(); - for (auto i : payloads) { - if (i.first.compare(0, profileSize, PROFILE_VCF) == 0) { - MSG_("VCARD"); - return; - } - MSG_("payload.first = " + i.first); - MSG_("payload.second = " + i.second); - - auto payload = Utils::toPlatformString(i.second); - CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( - CoreDispatcherPriority::High, ref new DispatchedHandler([=]() - { - incomingMessage(callId2, payload); - MSG_("message recu :" + i.second); - })); + for (auto i : payloads) { + if (i.first.compare(0, profileSize, PROFILE_VCF) == 0) { + MSG_("VCARD"); + return; } - }), - DRing::exportable_callback<DRing::ConfigurationSignal::RegistrationStateChanged>([this]( - const std::string& account_id, const std::string& state, - int detailsCode, const std::string& detailsStr) - { - MSG_("<RegistrationStateChanged>: ID = " + account_id + " state = " + state); - if (state == DRing::Account::States::REGISTERED) { - CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::High, - ref new DispatchedHandler([=]() { - reloadAccountList(); - if (editModeOn_) { - auto frame = dynamic_cast<Frame^>(Window::Current->Content); - dynamic_cast<RingClientUWP::MainPage^>(frame->Content)->showLoadingOverlay(false, false); - editModeOn_ = false; - } - })); - } - }), - DRing::exportable_callback<DRing::ConfigurationSignal::AccountsChanged>([this]() - { - MSG_("<AccountsChanged>"); + MSG_("payload.first = " + i.first); + MSG_("payload.second = " + i.second); + + auto payload = Utils::toPlatformString(i.second); + CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::High, ref new DispatchedHandler([=]() + { + incomingMessage(callId2, payload); + MSG_("message recu :" + i.second); + })); + } + }), + DRing::exportable_callback<DRing::ConfigurationSignal::RegistrationStateChanged>([this]( + const std::string& account_id, const std::string& state, + int detailsCode, const std::string& detailsStr) + { + MSG_("<RegistrationStateChanged>: ID = " + account_id + " state = " + state); + if (state == DRing::Account::States::REGISTERED) { CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::High, ref new DispatchedHandler([=]() { reloadAccountList(); @@ -518,165 +489,214 @@ RingClientUWP::RingD::startDaemon() editModeOn_ = false; } })); - }), - DRing::exportable_callback<DRing::Debug::MessageSend>([&](const std::string& toto) - { - if (debugModeOn_) - dispatcher->RunAsync(CoreDispatcherPriority::High, - ref new DispatchedHandler([=]() { - RingDebug::instance->print(toto); - })); - }), - - - DRing::exportable_callback<DRing::ConfigurationSignal::KnownDevicesChanged>([&](const std::string& accountId, const std::map<std::string, std::string>& devices) - { - dispatcher->RunAsync(CoreDispatcherPriority::High, - ref new DispatchedHandler([=]() { - RingDebug::instance->print("KnownDevicesChanged ---> C PAS FINI"); - })); - }), - DRing::exportable_callback<DRing::ConfigurationSignal::ExportOnRingEnded>([&](const std::string& accountId, int status, const std::string& pin) - { - auto accountId2 = Utils::toPlatformString(accountId); - auto pin2 = (pin.empty()) ? "Error bad password" : "Your generated pin : " + Utils::toPlatformString(pin); - dispatcher->RunAsync(CoreDispatcherPriority::High, - ref new DispatchedHandler([=]() { - exportOnRingEnded(accountId2, pin2); - })); - }) - }; - registerCallHandlers(callHandlers); - - std::map<std::string, SharedCallback> getAppPathHandler = - { - DRing::exportable_callback<DRing::ConfigurationSignal::GetAppDataPath> - ([this](std::vector<std::string>* paths) { - paths->emplace_back(localFolder_); - }) - }; - registerCallHandlers(getAppPathHandler); - - std::map<std::string, SharedCallback> getAppUserNameHandler = + } + }), + DRing::exportable_callback<DRing::ConfigurationSignal::AccountsChanged>([this]() { - DRing::exportable_callback<DRing::ConfigurationSignal::GetAppUserName> - ([this](std::vector<std::string>* unames) { - unames->emplace_back(Utils::toString( - UserModel::instance->firstName + - "." + - UserModel::instance->lastName)); - }) - }; - registerCallHandlers(getAppUserNameHandler); - - std::map<std::string, SharedCallback> incomingVideoHandlers = + MSG_("<AccountsChanged>"); + CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::High, + ref new DispatchedHandler([=]() { + reloadAccountList(); + if (editModeOn_) { + auto frame = dynamic_cast<Frame^>(Window::Current->Content); + dynamic_cast<RingClientUWP::MainPage^>(frame->Content)->showLoadingOverlay(false, false); + editModeOn_ = false; + } + })); + }), + DRing::exportable_callback<DRing::Debug::MessageSend>([&](const std::string& toto) { - DRing::exportable_callback<DRing::VideoSignal::DeviceEvent> - ([this]() { - MSG_("<DeviceEvent>"); - }), - DRing::exportable_callback<DRing::VideoSignal::DecodingStarted> - ([&](const std::string &id, const std::string &shmPath, int width, int height, bool isMixer) { + if (debugModeOn_) dispatcher->RunAsync(CoreDispatcherPriority::High, ref new DispatchedHandler([=]() { - Video::VideoManager::instance->rendererManager()->startedDecoding( - Utils::toPlatformString(id), - width, - height); - auto callId2 = Utils::toPlatformString(id); - incomingVideoMuted(callId2, false); - })); - }), - DRing::exportable_callback<DRing::VideoSignal::DecodingStopped> - ([&](const std::string &id, const std::string &shmPath, bool isMixer) { - dispatcher->RunAsync(CoreDispatcherPriority::High, - ref new DispatchedHandler([=]() { - Video::VideoManager::instance->rendererManager()->removeRenderer(Utils::toPlatformString(id)); - auto callId2 = Utils::toPlatformString(id); - incomingVideoMuted(callId2, true); - })); - }) - }; - registerVideoHandlers(incomingVideoHandlers); + RingDebug::instance->print(toto); + })); + }), + - using namespace Video; - std::map<std::string, SharedCallback> outgoingVideoHandlers = + DRing::exportable_callback<DRing::ConfigurationSignal::KnownDevicesChanged>([&](const std::string& accountId, const std::map<std::string, std::string>& devices) { - DRing::exportable_callback<DRing::VideoSignal::GetCameraInfo> - ([this](const std::string& device, - std::vector<std::string> *formats, - std::vector<unsigned> *sizes, - std::vector<unsigned> *rates) { - auto device_list = VideoManager::instance->captureManager()->deviceList; - - for (unsigned int i = 0; i < device_list->Size; i++) { - auto dev = device_list->GetAt(i); - if (device == Utils::toString(dev->name())) { - auto channel = dev->channel(); - Vector<Video::Resolution^>^ resolutions = channel->resolutionList(); - for (auto res : resolutions) { - formats->emplace_back(Utils::toString(res->format())); - sizes->emplace_back(res->size()->width()); - sizes->emplace_back(res->size()->height()); - rates->emplace_back(res->activeRate()->value()); + dispatcher->RunAsync(CoreDispatcherPriority::High, + ref new DispatchedHandler([=]() { + RingDebug::instance->print("KnownDevicesChanged ---> C PAS FINI"); + })); + }), + DRing::exportable_callback<DRing::ConfigurationSignal::ExportOnRingEnded>([&](const std::string& accountId, int status, const std::string& pin) + { + auto accountId2 = Utils::toPlatformString(accountId); + auto pin2 = (pin.empty()) ? "Error bad password" : "Your generated pin : " + Utils::toPlatformString(pin); + dispatcher->RunAsync(CoreDispatcherPriority::High, + ref new DispatchedHandler([=]() { + exportOnRingEnded(accountId2, pin2); + })); + }) + }; + registerCallHandlers(callHandlers); + + getAppPathHandler = + { + DRing::exportable_callback<DRing::ConfigurationSignal::GetAppDataPath> + ([this](std::vector<std::string>* paths) { + paths->emplace_back(localFolder_); + }) + }; + registerCallHandlers(getAppPathHandler); + + getAppUserNameHandler = + { + DRing::exportable_callback<DRing::ConfigurationSignal::GetAppUserName> + ([this](std::vector<std::string>* unames) { + unames->emplace_back(Utils::toString( + UserModel::instance->firstName + + "." + + UserModel::instance->lastName)); + }) + }; + registerCallHandlers(getAppUserNameHandler); + + incomingVideoHandlers = + { + DRing::exportable_callback<DRing::VideoSignal::DeviceEvent> + ([this]() { + MSG_("<DeviceEvent>"); + }), + DRing::exportable_callback<DRing::VideoSignal::DecodingStarted> + ([&](const std::string &id, const std::string &shmPath, int width, int height, bool isMixer) { + dispatcher->RunAsync(CoreDispatcherPriority::High, + ref new DispatchedHandler([=]() { + Video::VideoManager::instance->rendererManager()->startedDecoding( + Utils::toPlatformString(id), + width, + height); + auto callId2 = Utils::toPlatformString(id); + incomingVideoMuted(callId2, false); + })); + }), + DRing::exportable_callback<DRing::VideoSignal::DecodingStopped> + ([&](const std::string &id, const std::string &shmPath, bool isMixer) { + dispatcher->RunAsync(CoreDispatcherPriority::High, + ref new DispatchedHandler([=]() { + Video::VideoManager::instance->rendererManager()->removeRenderer(Utils::toPlatformString(id)); + auto callId2 = Utils::toPlatformString(id); + incomingVideoMuted(callId2, true); + })); + }) + }; + registerVideoHandlers(incomingVideoHandlers); + + using namespace Video; + outgoingVideoHandlers = + { + DRing::exportable_callback<DRing::VideoSignal::GetCameraInfo> + ([this](const std::string& device, + std::vector<std::string> *formats, + std::vector<unsigned> *sizes, + std::vector<unsigned> *rates) { + MSG_("<GetCameraInfo>"); + auto device_list = VideoManager::instance->captureManager()->deviceList; + + for (unsigned int i = 0; i < device_list->Size; i++) { + auto dev = device_list->GetAt(i); + if (device == Utils::toString(dev->name())) { + Vector<Video::Resolution^>^ resolutions = dev->resolutionList(); + for (auto res : resolutions) { + formats->emplace_back(Utils::toString(res->activeRate()->format())); + sizes->emplace_back(res->width()); + sizes->emplace_back(res->height()); + for (auto rate : res->rateList()) { + rates->emplace_back(rate->value()); } } } - }), - DRing::exportable_callback<DRing::VideoSignal::SetParameters> - ([&](const std::string& device, - std::string format, - const int width, - const int height, - const int rate) { - dispatcher->RunAsync(CoreDispatcherPriority::High, - ref new DispatchedHandler([=]() { - VideoManager::instance->captureManager()->activeDevice->SetDeviceProperties( - Utils::toPlatformString(format),width,height,rate); - })); - }), - DRing::exportable_callback<DRing::VideoSignal::StartCapture> - ([&](const std::string& device) { - dispatcher->RunAsync(CoreDispatcherPriority::High, - ref new DispatchedHandler([=]() { - VideoManager::instance->captureManager()->InitializeCameraAsync(); - VideoManager::instance->captureManager()->videoFrameCopyInvoker->Start(); - })); - }), - DRing::exportable_callback<DRing::VideoSignal::StopCapture> - ([&]() { - dispatcher->RunAsync(CoreDispatcherPriority::High, - ref new DispatchedHandler([=]() { - VideoManager::instance->captureManager()->StopPreviewAsync(); - if (VideoManager::instance->captureManager()->captureTaskTokenSource) - VideoManager::instance->captureManager()->captureTaskTokenSource->cancel(); - VideoManager::instance->captureManager()->videoFrameCopyInvoker->Stop(); - })); - }) - }; - registerVideoHandlers(outgoingVideoHandlers); - - std::map<std::string, SharedCallback> nameRegistrationHandlers = - { - DRing::exportable_callback<DRing::ConfigurationSignal::NameRegistrationEnded>( - [this](const std::string &accountId, int status, const std::string &name) { - MSG_("\n<NameRegistrationEnded>\n"); + } + }), + DRing::exportable_callback<DRing::VideoSignal::SetParameters> + ([&](const std::string& device, + std::string format, + const int width, + const int height, + const int rate) { + dispatcher->RunAsync(CoreDispatcherPriority::High, + ref new DispatchedHandler([=]() { + MSG_("<SetParameters>"); + VideoManager::instance->captureManager()->activeDevice->SetDeviceProperties( + Utils::toPlatformString(format),width,height,rate); + })); + }), + DRing::exportable_callback<DRing::VideoSignal::StartCapture> + ([&](const std::string& device) { + dispatcher->RunAsync(CoreDispatcherPriority::High, + ref new DispatchedHandler([=]() { + VideoManager::instance->captureManager()->InitializeCameraAsync(false); + VideoManager::instance->captureManager()->videoFrameCopyInvoker->Start(); + })); + }), + DRing::exportable_callback<DRing::VideoSignal::StopCapture> + ([&]() { + dispatcher->RunAsync(CoreDispatcherPriority::High, + ref new DispatchedHandler([=]() { + VideoManager::instance->captureManager()->StopPreviewAsync(); + if (VideoManager::instance->captureManager()->captureTaskTokenSource) + VideoManager::instance->captureManager()->captureTaskTokenSource->cancel(); + VideoManager::instance->captureManager()->videoFrameCopyInvoker->Stop(); + })); + }) + }; + registerVideoHandlers(outgoingVideoHandlers); - }), - DRing::exportable_callback<DRing::ConfigurationSignal::RegisteredNameFound>( - [this](const std::string &accountId, int status, const std::string &address, const std::string &name) { - MSG_("<RegisteredNameFound>" + name + " : " + address); - }) - }; - registerConfHandlers(nameRegistrationHandlers); + nameRegistrationHandlers = + { + DRing::exportable_callback<DRing::ConfigurationSignal::NameRegistrationEnded>( + [this](const std::string &accountId, int status, const std::string &name) { + MSG_("\n<NameRegistrationEnded>\n"); + + }), + DRing::exportable_callback<DRing::ConfigurationSignal::RegisteredNameFound>( + [this](const std::string &accountId, int status, const std::string &address, const std::string &name) { + MSG_("<RegisteredNameFound>" + name + " : " + address); + }) + }; + registerConfHandlers(nameRegistrationHandlers); +} - gnutls_global_init(); +void +RingD::initDaemon(int flags) +{ + DRing::init(static_cast<DRing::InitFlag>(flags)); +} - DRing::init(static_cast<DRing::InitFlag>(DRing::DRING_FLAG_CONSOLE_LOG | - DRing::DRING_FLAG_DEBUG)); +void +RingD::startDaemon() +{ + if (daemonRunning_) { + ERR_("daemon already runnging"); + return; + } + //eraseCacheFolder(); + editModeOn_ = true; + IAsyncAction^ action = ThreadPool::RunAsync(ref new WorkItemHandler([=](IAsyncAction^ spAction) + { daemonRunning_ = DRing::start(); + auto vcm = Video::VideoManager::instance->captureManager(); + std::string deviceName = DRing::getDefaultDevice(); + std::map<std::string, std::string> settings = DRing::getSettings(deviceName); + int rate = stoi(settings["rate"]); + std::string size = settings["size"]; + std::string::size_type pos = size.find('x'); + int width = std::stoi(size.substr(0, pos)); + int height = std::stoi(size.substr(pos + 1, size.length())); + for (auto dev : vcm->deviceList) { + if (!Utils::toString(dev->name()).compare(deviceName)) + vcm->activeDevice = dev; + } + vcm->activeDevice->SetDeviceProperties("", width, height, rate); + CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, + ref new DispatchedHandler([=]() { + finishCaptureDeviceEnumeration(); + })); + if (!daemonRunning_) { ERR_("\ndaemon didn't start.\n"); return; @@ -712,17 +732,13 @@ RingClientUWP::RingD::startDaemon() } }); - - while (daemonRunning) { + while (daemonRunning_) { DRing::pollEvents(); dequeueTasks(); Sleep(5); } - DRing::fini(); - - gnutls_global_deinit(); } - }); + },Platform::CallbackContext::Any), WorkItemPriority::High); } RingD::RingD() diff --git a/RingD.h b/RingD.h index f3f52acc20de9ee2b8f890979addcb7830cb3ea2..35f69566855dd4c24b197dcba8351c76f25fe1af 100644 --- a/RingD.h +++ b/RingD.h @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see <http://www.gnu.org/licenses/>. * **************************************************************************/ +#include <dring.h> using namespace concurrency; @@ -37,7 +38,10 @@ delegate void ExportOnRingEnded(String^ accountId, String^ pin); delegate void SummonWizard(); delegate void AccountUpdated(Account^ account); delegate void IncomingVideoMuted(String^ callId, bool state); +delegate void FinishCaptureDeviceEnumeration(); +using SharedCallback = std::shared_ptr<DRing::CallbackWrapperBase>; +using namespace std::placeholders; public ref class RingD sealed { @@ -77,6 +81,8 @@ internal: // why this property has to be internal and not public ? internal: /* functions */ + void registerCallbacks(); + void initDaemon(int flags); void startDaemon(); void reloadAccountList(); void sendAccountTextMessage(String^ message); @@ -119,6 +125,7 @@ internal: event SummonWizard^ summonWizard; event AccountUpdated^ accountUpdated; event IncomingVideoMuted^ incomingVideoMuted; + event FinishCaptureDeviceEnumeration^ finishCaptureDeviceEnumeration; private: /* sub classes */ @@ -181,11 +188,20 @@ private: // CallStatus translateCallStatus(String^ state); /* members */ + Windows::UI::Core::CoreDispatcher^ dispatcher; + std::string localFolder_; bool daemonRunning_ = false; std::queue<Task^> tasksList_; StartingStatus startingStatus_ = StartingStatus::NORMAL; bool editModeOn_ = false; bool debugModeOn_ = true; + + std::map<std::string, SharedCallback> callHandlers; + std::map<std::string, SharedCallback> getAppPathHandler; + std::map<std::string, SharedCallback> getAppUserNameHandler; + std::map<std::string, SharedCallback> incomingVideoHandlers; + std::map<std::string, SharedCallback> outgoingVideoHandlers; + std::map<std::string, SharedCallback> nameRegistrationHandlers; }; } \ No newline at end of file diff --git a/RingDebug.cpp b/RingDebug.cpp index 8835c79faff6d0522f7500bf649aaf5302a2e312..732c5b5a74c9eca8a3609a9dc5fa262a36585453 100644 --- a/RingDebug.cpp +++ b/RingDebug.cpp @@ -20,6 +20,8 @@ /* client */ #include "pch.h" +#include "fileutils.h" + using namespace RingClientUWP; using namespace Platform; @@ -56,14 +58,15 @@ RingDebug::print(const std::string& message, /* fire the event. */ auto line = ref new String(wString.c_str(), wString.length()); messageToScreen(line); - FileIO::AppendTextAsync(_logFile, line + "\n"); + + std::ofstream ofs; + ofs.open ("debug.log", std::ofstream::out | std::ofstream::app); + ofs << Utils::toString(line) << "\n"; + ofs.close(); } void RingClientUWP::RingDebug::WriteLine(String^ str) { - /* save in file */ - //FileIO::AppendTextAsync(_videoFile, str + "\n"); - /* screen in visual studio console */ std::wstringstream wStringstream; wStringstream << str->Data() << "\n"; @@ -72,19 +75,4 @@ void RingClientUWP::RingDebug::WriteLine(String^ str) RingClientUWP::RingDebug::RingDebug() { - StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder; - - StorageFile^ logFile; - - task<StorageFile^>(storageFolder->CreateFileAsync("debug.log", CreationCollisionOption::ReplaceExisting)).then([this](StorageFile^ file) - { - this->_logFile = file; - }); - - task<StorageFile^>(storageFolder->CreateFileAsync("video.log", CreationCollisionOption::ReplaceExisting)).then([this](StorageFile^ file) - { - this->_videoFile = file; - }); - -} - +} \ No newline at end of file diff --git a/RingDebug.h b/RingDebug.h index 20467badd281a746ba0847328eabcbe86f0a42e1..f9ca4b408a830f537c001a47f2eac69951be9dff 100644 --- a/RingDebug.h +++ b/RingDebug.h @@ -42,9 +42,6 @@ public: } } - property StorageFile^ _logFile; - property StorageFile^ _videoFile; - /* properties */ /* functions */ diff --git a/SmartPanel.xaml b/SmartPanel.xaml index 95d8ce8404d877441265ee6738bbb189c40f5cc3..3d0492e5e69399453c6e731b9c4be196b14db257 100644 --- a/SmartPanel.xaml +++ b/SmartPanel.xaml @@ -880,7 +880,30 @@ <Grid x:Name="_settings_" Grid.Row="0" Visibility="Collapsed"> - <TextBlock>some settings</TextBlock> + <Grid.RowDefinitions> + <RowDefinition Height="*"/> + </Grid.RowDefinitions> + <Grid x:Name="_videoSettings_" + Grid.Row="0"> + <StackPanel Margin="10"> + <TextBlock Text="Video Device" + Margin="10"/> + <ComboBox x:Name="_videoDeviceComboBox_" + Margin="10" + SelectionChanged="_videoDeviceComboBox__SelectionChanged"> + </ComboBox> + <TextBlock Text="Video Resolution" Margin="10"/> + <ComboBox x:Name="_videoResolutionComboBox_" + Margin="10" + SelectionChanged="_videoResolutionComboBox__SelectionChanged"> + </ComboBox> + <TextBlock Text="Video Rate" Margin="10"/> + <ComboBox x:Name="_videoRateComboBox_" + Margin="10" + SelectionChanged="_videoRateComboBox__SelectionChanged"> + </ComboBox> + </StackPanel> + </Grid> </Grid> </Grid> </Grid> diff --git a/SmartPanel.xaml.cpp b/SmartPanel.xaml.cpp index 29ede5a43e5d07673a4d030e87689dd011ffc85a..a72d332599fd4adf49bdbfd72329509e83c34268 100644 --- a/SmartPanel.xaml.cpp +++ b/SmartPanel.xaml.cpp @@ -29,6 +29,7 @@ using namespace RingClientUWP::Controls; using namespace RingClientUWP::Views; using namespace RingClientUWP::ViewModel; using namespace Windows::Media::Capture; +using namespace Windows::Media::MediaProperties; using namespace Windows::UI::Xaml; using namespace Windows::Storage; using namespace Windows::UI::Xaml::Media::Imaging; @@ -41,8 +42,6 @@ using namespace Windows::Foundation; using namespace Concurrency; using namespace Platform::Collections; - - using namespace Windows::ApplicationModel::Core; using namespace Windows::Storage; using namespace Windows::UI::Core; @@ -141,7 +140,9 @@ SmartPanel::SmartPanel() RingD::instance->exportOnRingEnded += ref new RingClientUWP::ExportOnRingEnded(this, &RingClientUWP::Views::SmartPanel::OnexportOnRingEnded); RingD::instance->accountUpdated += ref new RingClientUWP::AccountUpdated(this, &RingClientUWP::Views::SmartPanel::OnaccountUpdated); - + RingD::instance->finishCaptureDeviceEnumeration += ref new RingClientUWP::FinishCaptureDeviceEnumeration([this]() { + populateVideoDeviceSettingsComboBox(); + }); } void @@ -200,12 +201,30 @@ void RingClientUWP::Views::SmartPanel::_settings__Checked(Object^ sender, Routed { _smartGrid_->Visibility = Windows::UI::Xaml::Visibility::Collapsed; _settings_->Visibility = Windows::UI::Xaml::Visibility::Visible; + auto vcm = Video::VideoManager::instance->captureManager(); + if (!vcm->isInitialized) + vcm->InitializeCameraAsync(true); + else + vcm->StartPreviewAsync(true); + summonPreviewPage(); } void RingClientUWP::Views::SmartPanel::_settings__Unchecked(Object^ sender, RoutedEventArgs^ e) { _settings_->Visibility = Windows::UI::Xaml::Visibility::Collapsed; _smartGrid_->Visibility = Windows::UI::Xaml::Visibility::Visible; + Video::VideoManager::instance->captureManager()->StopPreviewAsync() + .then([](task<void> stopPreviewTask) + { + try { + stopPreviewTask.get(); + Video::VideoManager::instance->captureManager()->isSettingsPreviewing = false; + } + catch (Exception^ e) { + WriteException(e); + } + }); + hidePreviewPage(); } void RingClientUWP::Views::SmartPanel::setMode(RingClientUWP::Views::SmartPanel::Mode mode) @@ -888,11 +907,10 @@ void RingClientUWP::Views::SmartPanel::_selectedAccountAvatarContainer__PointerR .then([this](StorageFile^ photoFile) { if (photoFile != nullptr) { - // maybe it would be possible to move some logics to the style sheet auto brush = ref new ImageBrush(); auto circle = ref new Ellipse(); - circle->Height = 80; // TODO : use some global constant when ready + circle->Height = 80; circle->Width = 80; auto path = photoFile->Path; auto uri = ref new Windows::Foundation::Uri(path); @@ -950,3 +968,147 @@ void RingClientUWP::Views::SmartPanel::Grid_PointerMoved(Platform::Object^ sende item->_hovered = Windows::UI::Xaml::Visibility::Visible; } + +// VIDEO + +void +SmartPanel::_videoDeviceComboBox__SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^) +{ + if (_videoDeviceComboBox_->Items->Size) { + Video::VideoCaptureManager^ vcm = Video::VideoManager::instance->captureManager(); + auto selectedItem = static_cast<ComboBoxItem^>(static_cast<ComboBox^>(sender)->SelectedItem); + auto deviceId = static_cast<String^>(selectedItem->Tag); + std::vector<int>::size_type index; + for(index = 0; index != vcm->deviceList->Size; index++) { + auto dev = vcm->deviceList->GetAt(index); + if (dev->id() == deviceId) { + break; + } + } + vcm->activeDevice = vcm->deviceList->GetAt(index); + + populateVideoResolutionSettingsComboBox(); + } +} + +void +SmartPanel::_videoResolutionComboBox__SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^) +{ + if (_videoResolutionComboBox_->Items->Size) { + Video::VideoCaptureManager^ vcm = Video::VideoManager::instance->captureManager(); + auto device = vcm->activeDevice; + auto selectedItem = static_cast<ComboBoxItem^>(static_cast<ComboBox^>(sender)->SelectedItem); + auto selectedResolution = static_cast<String^>(selectedItem->Tag); + std::vector<int>::size_type index; + for(index = 0; index != device->resolutionList()->Size; index++) { + auto res = device->resolutionList()->GetAt(index); + if (res->getFriendlyName() == selectedResolution) { + break; + } + } + vcm->activeDevice->setCurrentResolution( device->resolutionList()->GetAt(index) ); + populateVideoRateSettingsComboBox(); + } +} + +void +SmartPanel::_videoRateComboBox__SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^) +{ + if (_videoRateComboBox_->Items->Size) { + Video::VideoCaptureManager^ vcm = Video::VideoManager::instance->captureManager(); + auto resolution = vcm->activeDevice->currentResolution(); + auto selectedItem = static_cast<ComboBoxItem^>(static_cast<ComboBox^>(sender)->SelectedItem); + unsigned int frame_rate = static_cast<unsigned int>(selectedItem->Tag); + std::vector<int>::size_type index; + for(index = 0; index != resolution->rateList()->Size; index++) { + auto rate = resolution->rateList()->GetAt(index); + if (rate->value() == frame_rate) + break; + } + vcm->activeDevice->currentResolution()->setActiveRate( resolution->rateList()->GetAt(index) ); + if (vcm->isPreviewing) { + vcm->CleanupCameraAsync() + .then([=](task<void> cleanupCameraTask) { + try { + cleanupCameraTask.get(); + vcm->InitializeCameraAsync(true); + } + catch (Exception^ e) { + WriteException(e); + } + }); + } + } +} + +void +SmartPanel::populateVideoDeviceSettingsComboBox() +{ + _videoDeviceComboBox_->Items->Clear(); + auto devices = Video::VideoManager::instance->captureManager()->deviceList; + int index = 0; + bool deviceSelected = false; + for (auto device : devices) { + ComboBoxItem^ comboBoxItem = ref new ComboBoxItem(); + comboBoxItem->Content = device->name(); + comboBoxItem->Tag = device->id(); + _videoDeviceComboBox_->Items->Append(comboBoxItem); + if (device->isActive() && !deviceSelected) { + _videoDeviceComboBox_->SelectedIndex = index; + deviceSelected = true; + } + ++index; + } + if (!deviceSelected && devices->Size > 0) + _videoDeviceComboBox_->SelectedIndex = 0; +} + +void +SmartPanel::populateVideoResolutionSettingsComboBox() +{ + _videoResolutionComboBox_->Items->Clear(); + Video::Device^ device = Video::VideoManager::instance->captureManager()->activeDevice; + std::map<std::string, std::string> settings = DRing::getSettings(Utils::toString(device->name())); + std::string preferredResolution = settings[Video::Device::PreferenceNames::SIZE]; + auto resolutions = device->resolutionList(); + int index = 0; + bool resolutionSelected = false; + for (auto resolution : resolutions) { + ComboBoxItem^ comboBoxItem = ref new ComboBoxItem(); + comboBoxItem->Content = resolution->getFriendlyName(); + comboBoxItem->Tag = resolution->getFriendlyName(); + _videoResolutionComboBox_->Items->Append(comboBoxItem); + if (!preferredResolution.compare(Utils::toString(resolution->getFriendlyName())) && !resolutionSelected) { + _videoResolutionComboBox_->SelectedIndex = index; + resolutionSelected = true; + } + ++index; + } + if (!resolutionSelected && resolutions->Size > 0) + _videoResolutionComboBox_->SelectedIndex = 0; +} + +void +SmartPanel::populateVideoRateSettingsComboBox() +{ + _videoRateComboBox_->Items->Clear(); + Video::Device^ device = Video::VideoManager::instance->captureManager()->activeDevice; + std::map<std::string, std::string> settings = DRing::getSettings(Utils::toString(device->name())); + std::string preferredRate = settings[Video::Device::PreferenceNames::RATE]; + auto resolution = device->currentResolution(); + int index = 0; + bool rateSelected = false; + for (auto rate : resolution->rateList()) { + ComboBoxItem^ comboBoxItem = ref new ComboBoxItem(); + comboBoxItem->Content = rate->name(); + comboBoxItem->Tag = rate->value(); + _videoRateComboBox_->Items->Append(comboBoxItem); + if (std::stoi(preferredRate) == rate->value() && !rateSelected) { + _videoRateComboBox_->SelectedIndex = index; + rateSelected = true; + } + ++index; + } + if (!rateSelected && resolution->rateList()->Size > 0) + _videoRateComboBox_->SelectedIndex = 0; +} \ No newline at end of file diff --git a/SmartPanel.xaml.h b/SmartPanel.xaml.h index cc53de793e4ee68931d418e9acb28ba794842317..6c623680d3c380d9f9efb37f2d42e1cd83ced0c6 100644 --- a/SmartPanel.xaml.h +++ b/SmartPanel.xaml.h @@ -25,6 +25,8 @@ delegate void ToggleSmartPan(); delegate void SummonMessageTextPage(); delegate void SummonVideoPage(); delegate void SummonWelcomePage(); +delegate void SummonPreviewPage(); +delegate void HidePreviewPage(); namespace Views { @@ -91,6 +93,8 @@ internal: event SummonMessageTextPage^ summonMessageTextPage; event SummonVideoPage^ summonVideoPage; event SummonWelcomePage^ summonWelcomePage; + event SummonPreviewPage^ summonPreviewPage; + event HidePreviewPage^ hidePreviewPage; void setMode(RingClientUWP::Views::SmartPanel::Mode mode); private: @@ -116,6 +120,12 @@ private: void Grid_PointerExited(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e); void _contactItem__PointerReleased(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e); void generateQRcode(); + void _videoDeviceComboBox__SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^); + void _videoResolutionComboBox__SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^); + void _videoRateComboBox__SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^); + void populateVideoDeviceSettingsComboBox(); + void populateVideoResolutionSettingsComboBox(); + void populateVideoRateSettingsComboBox(); /* members */ void _devicesMenuButton__Unchecked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); diff --git a/Utils.h b/Utils.h index aacef25abef04d36ac384fbfa030fd5f15988233..5739619ce2311e82a44bdf991c2b42533e2526c2 100644 --- a/Utils.h +++ b/Utils.h @@ -32,12 +32,11 @@ namespace RingClientUWP { namespace Utils { - task<bool> fileExists(StorageFolder^ folder, String^ fileName) { return create_task(folder->GetFileAsync(fileName)) - .then([](task<StorageFile^> taskResult) + .then([](task<StorageFile^> taskResult) { bool exists; try { @@ -176,82 +175,91 @@ TrimCmd(Platform::String^ s) const WCHAR* first = s->Begin(); const WCHAR* last = s->End(); - while (first != last && last[0] != '\ ' ) - --last; + while (first != last && last[0] != '\ ') + --last; - //last--; + //last--; - return ref new Platform::String(first, sizeof(last)); - } + return ref new Platform::String(first, sizeof(last)); +} - Platform::String^ - TrimParameter(Platform::String^ s) - { - const WCHAR* first = s->Begin(); - const WCHAR* last = s->End(); +Platform::String^ +TrimParameter(Platform::String^ s) +{ + const WCHAR* first = s->Begin(); + const WCHAR* last = s->End(); - while (first != last && *first != '[') - ++first; + while (first != last && *first != '[') + ++first; - while (first != last && last[-1] != ']') - --last; + while (first != last && last[-1] != ']') + --last; - first++; - last--; + first++; + last--; - if (static_cast<unsigned int>(last - first) > 0) - return ref new Platform::String(first, static_cast<unsigned int>(last - first)); - else - return ""; - } + if (static_cast<unsigned int>(last - first) > 0) + return ref new Platform::String(first, static_cast<unsigned int>(last - first)); + else + return ""; +} - Platform::String^ - GetNewGUID() - { - GUID result; - HRESULT hr = CoCreateGuid(&result); +Platform::String^ +GetNewGUID() +{ + GUID result; + HRESULT hr = CoCreateGuid(&result); - if (SUCCEEDED(hr)) { - Guid guid(result); - return guid.ToString(); - } + if (SUCCEEDED(hr)) { + Guid guid(result); + return guid.ToString(); + } - throw Exception::CreateException(hr); - } + throw Exception::CreateException(hr); +} - std::string - getStringFromFile(const std::string& filename) - { - std::ifstream file(filename); - return std::string((std::istreambuf_iterator<char>(file)), - (std::istreambuf_iterator<char>())); - } +std::string +getStringFromFile(const std::string& filename) +{ + std::ifstream file(filename); + return std::string((std::istreambuf_iterator<char>(file)), + (std::istreambuf_iterator<char>())); +} - inline Map<String^,String^>^ - convertMap(const std::map<std::string, std::string>& m) - { - auto temp = ref new Map<String^,String^>; - for (const auto& pair : m) { - temp->Insert( +inline Map<String^, String^>^ +convertMap(const std::map<std::string, std::string>& m) +{ + auto temp = ref new Map<String^, String^>; + for (const auto& pair : m) { + temp->Insert( Utils::toPlatformString(pair.first), Utils::toPlatformString(pair.second) - ); - } - return temp; - } + ); + } + return temp; +} - inline std::map<std::string, std::string> - convertMap(Map<String^,String^>^ m) - { - std::map<std::string, std::string> temp; - for (const auto& pair : m) { - temp.insert( +inline std::map<std::string, std::string> +convertMap(Map<String^, String^>^ m) +{ + std::map<std::string, std::string> temp; + for (const auto& pair : m) { + temp.insert( std::make_pair( - Utils::toString(pair->Key), - Utils::toString(pair->Value))); - } - return temp; - } + Utils::toString(pair->Key), + Utils::toString(pair->Value))); + } + return temp; +} - } - } +template<class T> +bool +findIn(std::vector<T> vec, T val) +{ + if (std::find(vec.begin(), vec.end(), val) == vec.end()) + return true; + return false; +} + +} /*namespace Utils*/ +} /*namespace RingClientUWP*/ \ No newline at end of file diff --git a/Video.cpp b/Video.cpp index e9e71a06ffdcc80b1edd8182946b3a0a7f4c70af..2a178d6133d52e1e766edb54b345a4eeebe110c5 100644 --- a/Video.cpp +++ b/Video.cpp @@ -25,43 +25,18 @@ using namespace RingClientUWP; using namespace Video; using namespace Platform; +using namespace Windows::Media::MediaProperties; /************************************************************ * * - * Size * + * Rate * * * ***********************************************************/ - -unsigned int -Video::Size::width() -{ - return m_Width; -} - -unsigned int -Video::Size::height() -{ - return m_Height; -} - -void -Video::Size::setWidth(unsigned int width) +Rate::Rate() { - m_Width = width; + m_validFormats = ref new Vector<String^>(); } -void -Video::Size::setHeight(unsigned int height) -{ - m_Height = height; -} - -/************************************************************ - * * - * Rate * - * * - ***********************************************************/ - String^ Rate::name() { @@ -74,56 +49,46 @@ Rate::value() return m_value; } -void -Rate::setName(String^ name) -{ - m_name = name; -} - -void -Rate::setValue(unsigned int value) +String^ +Rate::format() { - m_value = value; + return m_currentFormat; } -/************************************************************ - * * - * Channel * - * * - ***********************************************************/ -Channel::Channel() +Vector<String^>^ +Rate::formatList() { - m_validResolutions = ref new Vector<Resolution^>(); + return m_validFormats; } -String^ -Channel::name() +IMediaEncodingProperties^ +Rate::getMediaEncodingProperties() { - return m_name; + return m_encodingProperties; } -Resolution^ -Channel::currentResolution() +void +Rate::setName(String^ name) { - return m_currentResolution; + m_name = name; } void -Channel::setName(String^ name) +Rate::setValue(unsigned int value) { - m_name = name; + m_value = value; } void -Channel::setCurrentResolution(Resolution^ currentResolution) +Rate::setFormat(String^ format) { - m_currentResolution = currentResolution; + m_currentFormat = format; } -Vector<Resolution^>^ -Channel::resolutionList() +void +Rate::setMediaEncodingProperties(IMediaEncodingProperties^ props) { - return m_validResolutions; + m_encodingProperties = props; } /************************************************************ @@ -132,20 +97,33 @@ Channel::resolutionList() * * ***********************************************************/ -Resolution::Resolution() +Resolution::Resolution(IMediaEncodingProperties^ encodingProperties): + m_encodingProperties(encodingProperties) { - m_size = ref new Size(); m_validRates = ref new Vector<Rate^>(); + VideoEncodingProperties^ vidprops = static_cast<VideoEncodingProperties^>(encodingProperties); + m_width = vidprops->Width; + m_height = vidprops->Height; } -Resolution::Resolution(Video::Size^ size): - m_size(size) -{ } +String^ +Resolution::getFriendlyName() +{ + std::wstringstream ss; + ss << m_width << "x" << m_height; + return ref new String(ss.str().c_str()); +} + +IMediaEncodingProperties^ +Resolution::getMediaEncodingProperties() +{ + return m_encodingProperties; +} String^ Resolution::name() { - return size()->width().ToString() + "x" + size()->height().ToString(); + return m_width.ToString() + "x" + m_height.ToString(); } Rate^ @@ -160,28 +138,34 @@ Resolution::rateList() return m_validRates; } -Video::Size^ -Resolution::size() +unsigned int +Resolution::width() { - return m_size; + return m_width; } -String^ -Resolution::format() +unsigned int +Resolution::height() { - return m_format; + return m_height; } void -Resolution::setWidth(int width) +Resolution::setWidth(unsigned int width) { - m_size->setWidth(width); + m_width = width; } void -Resolution::setHeight(int height) +Resolution::setHeight(unsigned int height) { - m_size->setHeight(height); + m_height = height; +} + +String^ +Resolution::format() +{ + return m_format; } void @@ -197,10 +181,15 @@ Resolution::setActiveRate(Rate^ rate) return false; m_currentRate = rate; - // set camera device rate here return true; } +void +Resolution::setMediaEncodingProperties(IMediaEncodingProperties^ props) +{ + m_encodingProperties = props; +} + /************************************************************ * * * Device * @@ -210,7 +199,7 @@ Resolution::setActiveRate(Rate^ rate) Device::Device(String^ id) { m_deviceId = id; - m_channels = ref new Vector<Channel^>(); + m_validResolutions = ref new Vector<Resolution^>(); } String^ @@ -219,31 +208,22 @@ Device::id() return m_deviceId; } -Vector<Channel^>^ -Device::channelList() -{ - return m_channels; -} - String^ Device::name() { return m_name; } -Channel^ -Device::channel() +Resolution^ +Device::currentResolution() { - return m_currentChannel; + return m_currentResolution; } -bool -Device::setCurrentChannel(Channel^ channel) +Vector<Resolution^>^ +Device::resolutionList() { - if (m_currentChannel == channel) - return false; - m_currentChannel = channel; - return true; + return m_validResolutions; } void @@ -252,6 +232,12 @@ Device::setName(String^ name) m_name = name; } +void +Device::setCurrentResolution(Resolution^ currentResolution) +{ + m_currentResolution = currentResolution; +} + void Device::save() { @@ -260,22 +246,22 @@ Device::save() bool Device::isActive() { - return false; - //return Video::DeviceModel::instance().activeDevice() == this; + return Video::VideoManager::instance->captureManager()->activeDevice == this; } void Device::SetDeviceProperties(String^ format, int width, int height, int rate) { - auto rl = m_currentChannel->resolutionList(); - for (auto res : rl) { - if (res->format() == format && - res->size()->width() == width && - res->size()->height() == height && - res->activeRate()->value() == rate) + for (auto resolution_ : m_validResolutions) { + if ( resolution_->width() == width && + resolution_->height() == height ) { - m_currentChannel->setCurrentResolution(res); - RingDebug::instance->WriteLine("SetDeviceProperties"); + setCurrentResolution(resolution_); + for (auto rate_ : resolution_->rateList()) { + if (rate_->value() == rate && + (format->IsEmpty()? true : rate_->format() == format)) + currentResolution()->setActiveRate(rate_); + } return; } } diff --git a/Video.h b/Video.h index ad544d5ae1aadf0d9559791f96cd169dbc548cb0..ef1db3a61f5130284219d726a57ca57a9aaf42b0 100644 --- a/Video.h +++ b/Video.h @@ -20,6 +20,7 @@ #pragma once using namespace Platform; +using namespace Windows::Media::MediaProperties; namespace RingClientUWP { @@ -31,87 +32,61 @@ ref class Rate; ref class Resolution; ref class Device; -public ref class Size sealed -{ -internal: - unsigned int width (); - unsigned int height (); - - void setWidth ( unsigned int width ); - void setHeight ( unsigned int height ); - -public: - Size() { }; - Size(unsigned int w, unsigned int h): - m_Width(w), - m_Height(h) { }; - Size(Size^ rhs): - m_Width(rhs->m_Width), - m_Height(rhs->m_Height) { }; - -private: - unsigned int m_Width; - unsigned int m_Height; - -}; - public ref class Rate sealed { internal: - String^ name (); - unsigned int value (); + String^ name (); + unsigned int value (); + Vector<String^>^ formatList (); + String^ format (); + IMediaEncodingProperties^ getMediaEncodingProperties (); - void setName ( String^ name ); - void setValue ( unsigned int value ); + void setName ( String^ name ); + void setValue ( unsigned int value ); + void setFormat ( String^ format ); -private: - String^ m_name; - unsigned int m_value; - -}; - -public ref class Channel sealed -{ -internal: - String^ name (); - Resolution^ currentResolution (); - Vector<Resolution^>^ resolutionList (); - - void setName ( String^ name ); - void setCurrentResolution ( Resolution^ currentResolution); + void setMediaEncodingProperties (IMediaEncodingProperties^ props); public: - Channel(); + Rate(); private: + IMediaEncodingProperties^ m_encodingProperties; String^ m_name; - Resolution^ m_currentResolution; - Vector<Resolution^>^ m_validResolutions; + unsigned int m_value; + String^ m_currentFormat; + Vector<String^>^ m_validFormats; }; public ref class Resolution sealed { internal: - String^ name (); - Rate^ activeRate (); - Vector<Rate^>^ rateList (); - Size^ size (); - String^ format (); - - bool setActiveRate ( Rate^ rate ); - void setWidth ( int width ); - void setHeight ( int height ); - void setFormat ( String^ format ); + String^ name (); + Rate^ activeRate (); + Vector<Rate^>^ rateList (); + unsigned int width (); + unsigned int height (); + String^ format (); + String^ getFriendlyName (); + IMediaEncodingProperties^ getMediaEncodingProperties (); + + bool setActiveRate ( Rate^ rate ); + void setWidth ( unsigned int width ); + void setHeight ( unsigned int height ); + void setFormat ( String^ format ); + + void setMediaEncodingProperties (IMediaEncodingProperties^ props); public: - Resolution(); - Resolution(Size^ size); + Resolution(IMediaEncodingProperties^ encodingProperties); private: + IMediaEncodingProperties^ m_encodingProperties; Rate^ m_currentRate; Vector<Rate^>^ m_validRates; - Size^ m_size; + unsigned int m_width; + unsigned int m_height; String^ m_format; }; @@ -123,18 +98,16 @@ internal: public: constexpr static const char* RATE = "rate" ; constexpr static const char* NAME = "name" ; - constexpr static const char* CHANNEL = "channel"; constexpr static const char* SIZE = "size" ; }; - Vector<Channel^>^ channelList (); - String^ id (); - String^ name (); - Channel^ channel (); + String^ id (); + String^ name (); + Resolution^ currentResolution (); + Vector<Resolution^>^ resolutionList (); - bool setCurrentChannel ( Channel^ channel ); - void setName ( String^ name ); - + void setName ( String^ name ); + void setCurrentResolution ( Resolution^ currentResolution ); public: Device(String^ id); @@ -144,11 +117,11 @@ public: bool isActive (); private: - String^ m_deviceId ; - String^ m_name ; - Channel^ m_currentChannel ; - Vector<Channel^>^ m_channels ; - bool m_requireSave ; + String^ m_deviceId ; + String^ m_name ; + bool m_requireSave ; + Resolution^ m_currentResolution ; + Vector<Resolution^>^ m_validResolutions ; }; diff --git a/VideoCaptureManager.cpp b/VideoCaptureManager.cpp index 5f7d8742f655ac5016312ff1c8e7751a87ecda8a..3c50e26160a218921e7234c76f884f4d0b37f8f3 100644 --- a/VideoCaptureManager.cpp +++ b/VideoCaptureManager.cpp @@ -19,12 +19,15 @@ #include "pch.h" #include "VideoCaptureManager.h" +#include "SmartPanel.xaml.h" #include <MemoryBuffer.h> // IMemoryBufferByteAccess using namespace RingClientUWP; +using namespace RingClientUWP::Views; using namespace Video; +using namespace Windows::UI::Core; using namespace Windows::Graphics::Display; using namespace Windows::Graphics::Imaging; using namespace Windows::UI::Xaml::Media::Imaging; @@ -32,10 +35,18 @@ using namespace Windows::Media; using namespace Windows::Media::MediaProperties; using namespace Windows::Media::Capture; +std::map<std::string, int> pixel_formats = { + {"NV12", 0}, + {"MJPG", 1}, + {"RGB24",2}, + {"YUV2", 3} +}; + VideoCaptureManager::VideoCaptureManager(): mediaCapture(nullptr) , isInitialized(false) , isPreviewing_(false) + , isSettingsPreviewing_(false) , isChangingCamera(false) , isRendering(false) , externalCamera(false) @@ -60,13 +71,13 @@ VideoCaptureManager::getSettings(String^ device) void VideoCaptureManager::MediaCapture_Failed(Capture::MediaCapture^, Capture::MediaCaptureFailedEventArgs^ errorEventArgs) { - RingDebug::instance->WriteLine("MediaCapture_Failed"); std::wstringstream ss; ss << "MediaCapture_Failed: 0x" << errorEventArgs->Code << ": " << errorEventArgs->Message->Data(); RingDebug::instance->WriteLine(ref new String(ss.str().c_str())); if (captureTaskTokenSource) captureTaskTokenSource->cancel(); + CleanupCameraAsync(); } @@ -84,7 +95,6 @@ VideoCaptureManager::CleanupCameraAsync() auto stopPreviewTask = create_task(StopPreviewAsync()); taskList.push_back(stopPreviewTask); } - isInitialized = false; } @@ -99,51 +109,30 @@ VideoCaptureManager::CleanupCameraAsync() }); } -task<void> -VideoCaptureManager::EnumerateWebcamsAsync() -{ - devInfoCollection = nullptr; - - deviceList->Clear(); - - return create_task(DeviceInformation::FindAllAsync(DeviceClass::VideoCapture)) - .then([this](task<DeviceInformationCollection^> findTask) - { - try { - devInfoCollection = findTask.get(); - if (devInfoCollection == nullptr || devInfoCollection->Size == 0) { - RingDebug::instance->WriteLine("No WebCams found."); - } - else { - for (unsigned int i = 0; i < devInfoCollection->Size; i++) { - AddVideoDevice(i); - } - RingDebug::instance->WriteLine("Enumerating Webcams completed successfully."); - } - } - catch (Platform::Exception^ e) { - WriteException(e); - } - }); -} task<void> -VideoCaptureManager::StartPreviewAsync() +VideoCaptureManager::StartPreviewAsync(bool isSettingsPreview) { - RingDebug::instance->RingDebug::instance->WriteLine("StartPreviewAsync"); + WriteLine("StartPreviewAsync"); displayRequest->RequestActive(); - auto sink = getSink(); + Windows::UI::Xaml::Controls::CaptureElement^ sink; + if (isSettingsPreview) + sink = getSettingsPreviewSink(); + else + sink = getSink(); sink->Source = mediaCapture.Get(); return create_task(mediaCapture->StartPreviewAsync()) - .then([this](task<void> previewTask) + .then([=](task<void> previewTask) { try { previewTask.get(); isPreviewing = true; + if (isSettingsPreview) + isSettingsPreviewing = true; startPreviewing(); - RingDebug::instance->WriteLine("StartPreviewAsync DONE"); + WriteLine("StartPreviewAsync DONE"); } catch (Exception ^e) { WriteException(e); @@ -181,7 +170,7 @@ VideoCaptureManager::StopPreviewAsync() } task<void> -VideoCaptureManager::InitializeCameraAsync() +VideoCaptureManager::InitializeCameraAsync(bool isSettingsPreview) { RingDebug::instance->WriteLine("InitializeCameraAsync"); @@ -190,26 +179,21 @@ VideoCaptureManager::InitializeCameraAsync() mediaCapture = ref new MediaCapture(); - auto devInfo = devInfoCollection->GetAt(0); //preferences - video capture device - mediaCaptureFailedEventToken = mediaCapture->Failed += - ref new Capture::MediaCaptureFailedEventHandler(this, &VideoCaptureManager::MediaCapture_Failed); - - if (devInfo == nullptr) - return create_task([]() {}); + ref new Capture::MediaCaptureFailedEventHandler(this, &VideoCaptureManager::MediaCapture_Failed); auto settings = ref new MediaCaptureInitializationSettings(); - settings->VideoDeviceId = devInfo->Id; + settings->VideoDeviceId = activeDevice->id(); return create_task(mediaCapture->InitializeAsync(settings)) - .then([this](task<void> initTask) + .then([=](task<void> initTask) { try { initTask.get(); SetCaptureSettings(); isInitialized = true; RingDebug::instance->WriteLine("InitializeCameraAsync DONE"); - return StartPreviewAsync(); + return StartPreviewAsync(isSettingsPreview); } catch (Exception ^e) { WriteException(e); @@ -218,8 +202,51 @@ VideoCaptureManager::InitializeCameraAsync() }); } -void -VideoCaptureManager::AddVideoDevice(uint8_t index) +task<void> +VideoCaptureManager::EnumerateWebcamsAsync() +{ + devInfoCollection = nullptr; + + deviceList->Clear(); + + // TODO: device monitor + //auto watcher = DeviceInformation::CreateWatcher(); + + return create_task(DeviceInformation::FindAllAsync(DeviceClass::VideoCapture)) + .then([this](task<DeviceInformationCollection^> findTask) + { + try { + devInfoCollection = findTask.get(); + if (devInfoCollection == nullptr || devInfoCollection->Size == 0) { + WriteLine("No WebCams found."); + } + else { + std::vector<task<void>> taskList; + for (unsigned int i = 0; i < devInfoCollection->Size; i++) { + taskList.push_back(AddVideoDeviceAsync(i)); + } + when_all(taskList.begin(), taskList.end()) + .then([this](task<void> previousTasks) + { + try { + previousTasks.get(); + RingD::instance->startDaemon(); + } + catch (Exception^ e) { + ERR_("One doesn't simply start Ring daemon..."); + WriteException(e); + } + }); + } + } + catch (Platform::Exception^ e) { + WriteException(e); + } + }); +} + +task<void> +VideoCaptureManager::AddVideoDeviceAsync(uint8_t index) { RingDebug::instance->WriteLine("GetDeviceCaps " + index.ToString()); Platform::Agile<Windows::Media::Capture::MediaCapture^> mc; @@ -228,47 +255,81 @@ VideoCaptureManager::AddVideoDevice(uint8_t index) auto devInfo = devInfoCollection->GetAt(index); if (devInfo == nullptr) - return; + return concurrency::task_from_result(); auto settings = ref new MediaCaptureInitializationSettings(); settings->VideoDeviceId = devInfo->Id; - create_task(mc->InitializeAsync(settings)) + return create_task(mc->InitializeAsync(settings)) .then([=](task<void> initTask) { try { initTask.get(); auto allprops = mc->VideoDeviceController->GetAvailableMediaStreamProperties(MediaStreamType::VideoPreview); Video::Device^ device = ref new Device(devInfo->Id); - Video::Channel^ channel = ref new Channel(); for (auto props : allprops) { MediaProperties::VideoEncodingProperties^ vidprops = static_cast<VideoEncodingProperties^>(props); - int width = vidprops->Width; - int height = vidprops->Height; - Video::Resolution^ resolution = ref new Resolution(ref new Size(width,height)); + // Create resolution + Video::Resolution^ resolution = ref new Resolution(props); + // Get pixel-format + String^ format = vidprops->Subtype; + // Create rate Video::Rate^ rate = ref new Rate(); unsigned int frame_rate = 0; if (vidprops->FrameRate->Denominator != 0) frame_rate = vidprops->FrameRate->Numerator / vidprops->FrameRate->Denominator; rate->setValue(frame_rate); - rate->setName(rate->value().ToString() + "fps"); + rate->setName(rate->value().ToString() + " FPS"); + rate->setFormat(format); + // Try to find a resolution with the same dimensions in this device's resolution list + std::vector<int>::size_type resolution_index; + Video::Resolution^ matchingResolution; + for(resolution_index = 0; resolution_index != device->resolutionList()->Size; resolution_index++) { + matchingResolution = device->resolutionList()->GetAt(resolution_index); + if (matchingResolution->width() == resolution->width() && + matchingResolution->height() == resolution->height()) + break; + } + if (resolution_index < device->resolutionList()->Size) { + // Resolution found, check if rate is already in this resolution's ratelist, + // If so, pick the best format (prefer NV12 -> MJPG -> YUV2 -> RGB24 -> scrap the rest), + // otherwise append to resolution's ratelist, and continue looping + std::vector<int>::size_type rate_index; + for(rate_index = 0; rate_index != matchingResolution->rateList()->Size; rate_index++) { + auto matchingRate = matchingResolution->rateList()->GetAt(rate_index); + if (matchingRate->value() == rate->value()) + break; + } + if (rate_index < matchingResolution->rateList()->Size) { + // Rate found, pick best pixel-format + if (pixel_formats[Utils::toString(format)] < + pixel_formats[Utils::toString(matchingResolution->activeRate()->format())]) { + matchingResolution->activeRate()->setFormat(format); + // set props again + matchingResolution->activeRate()->setMediaEncodingProperties(props); + } + continue; + } + // Rate NOT found + device->resolutionList()->GetAt(resolution_index)->rateList()->Append(rate); + continue; + } + // Resolution NOT found, append rate to this resolution's ratelist and append resolution + // to device's resolutionlist + rate->setFormat(format); + rate->setMediaEncodingProperties(props); + resolution->rateList()->Append(rate); resolution->setActiveRate(rate); - String^ format = vidprops->Subtype; resolution->setFormat(format); - channel->resolutionList()->Append(resolution); - RingDebug::instance->WriteLine(devInfo->Name + " " - + width.ToString() + "x" + height.ToString() - + " " + frame_rate.ToString() + "FPS" + " " + format); + device->resolutionList()->Append(resolution); } - device->channelList()->Append(channel); - device->setCurrentChannel(device->channelList()->GetAt(0)); auto location = devInfo->EnclosureLocation; if (location != nullptr) { if (location->Panel == Windows::Devices::Enumeration::Panel::Front) { - device->setName(devInfo->Name + "-Front"); + device->setName(devInfo->Name + " - Front"); } else if (location->Panel == Windows::Devices::Enumeration::Panel::Back) { - device->setName(devInfo->Name + "-Back"); //ignore + device->setName(devInfo->Name + " - Back"); // TODO: ignore these back panel cameras..? } else { device->setName(devInfo->Name); @@ -277,9 +338,15 @@ VideoCaptureManager::AddVideoDevice(uint8_t index) else { device->setName(devInfo->Name); } + for (auto res : device->resolutionList()) { + for (auto rate : res->rateList()) { + WriteLine(device->name() + " " + res->width().ToString() + "x" + res->height().ToString() + + " " + rate->value().ToString() + "FPS " + rate->format()); + } + } this->deviceList->Append(device); this->activeDevice = deviceList->GetAt(0); - RingDebug::instance->WriteLine("GetDeviceCaps DONE"); + WriteLine("GetDeviceCaps DONE"); DRing::addVideoDevice(Utils::toString(device->name())); } catch (Platform::Exception^ e) { @@ -312,20 +379,35 @@ void VideoCaptureManager::CopyFrame(Object^ sender, Object^ e) { if (!isRendering && isPreviewing) { - try { - create_task(VideoCaptureManager::CopyFrameAsync()); - } - catch(Platform::COMException^ e) { - RingDebug::instance->WriteLine(e->ToString()); - } + create_task(VideoCaptureManager::CopyFrameAsync()) + .then([=](task<void> copyTask) + { + try { + copyTask.get(); + } + catch (Exception^ e) { + WriteException(e); + isRendering = false; + StopPreviewAsync(); + videoFrameCopyInvoker->Stop(); + if (captureTaskTokenSource) + captureTaskTokenSource->cancel(); + CleanupCameraAsync(); + throw ref new Exception(e->HResult, e->Message); + } + }); } } task<void> VideoCaptureManager::CopyFrameAsync() { - unsigned int videoFrameWidth = activeDevice->channel()->currentResolution()->size()->width(); - unsigned int videoFrameHeight = activeDevice->channel()->currentResolution()->size()->height(); + unsigned int videoFrameWidth = activeDevice->currentResolution()->width(); + unsigned int videoFrameHeight = activeDevice->currentResolution()->height(); + + auto allprops = mediaCapture->VideoDeviceController->GetAvailableMediaStreamProperties(MediaStreamType::VideoPreview); + MediaProperties::VideoEncodingProperties^ vidprops = static_cast<VideoEncodingProperties^>(allprops->GetAt(0)); + String^ format = vidprops->Subtype; // for now, only bgra auto videoFrame = ref new VideoFrame(BitmapPixelFormat::Bgra8, videoFrameWidth, videoFrameHeight); @@ -379,35 +461,53 @@ VideoCaptureManager::CopyFrameAsync() } catch (Exception^ e) { - RingDebug::instance->WriteLine("failed to copy frame to daemon's buffer"); + WriteException(e); + throw ref new Exception(e->HResult, e->Message); } - }).then([=](task<void> previousTask) { + }).then([=](task<void> renderCaptureToBufferTask) { try { - previousTask.get(); + renderCaptureToBufferTask.get(); isRendering = false; } catch (Platform::Exception^ e) { - RingDebug::instance->WriteLine( "Caught exception from previous task.\n" ); + WriteException(e); } }); } catch(Exception^ e) { WriteException(e); - throw std::exception(); + throw ref new Exception(e->HResult, e->Message); } } void VideoCaptureManager::SetCaptureSettings() { - auto vp = ref new VideoEncodingProperties; - auto res = activeDevice->channel()->currentResolution(); - vp->Width = res->size()->width(); - vp->Height = res->size()->height(); - vp->FrameRate->Numerator = res->activeRate()->value(); - vp->FrameRate->Denominator = 1; - vp->Subtype = res->format(); - auto encodingProperties = static_cast<IMediaEncodingProperties^>(vp); + WriteLine("SetCaptureSettings"); + auto res = activeDevice->currentResolution(); + auto vidprops = ref new VideoEncodingProperties; + vidprops->Width = res->width(); + vidprops->Height = res->height(); + vidprops->FrameRate->Numerator = res->activeRate()->value(); + vidprops->FrameRate->Denominator = 1; + vidprops->Subtype = res->activeRate()->format(); + auto encodingProperties = static_cast<IMediaEncodingProperties^>(vidprops); create_task(mediaCapture->VideoDeviceController->SetMediaStreamPropertiesAsync( - MediaStreamType::VideoPreview, encodingProperties)); + MediaStreamType::VideoPreview, encodingProperties)) + .then([=](task<void> setpropsTask){ + try { + setpropsTask.get(); + std::string deviceName = Utils::toString(activeDevice->name()); + std::map<std::string, std::string> settings = DRing::getSettings(deviceName); + settings["name"] = Utils::toString(activeDevice->name()); + settings["rate"] = Utils::toString(res->activeRate()->value().ToString()); + settings["size"] = Utils::toString(res->getFriendlyName()); + DRing::applySettings(deviceName, settings); + DRing::setDefaultDevice(deviceName); + WriteLine("SetCaptureSettings DONE"); + } + catch (Exception^ e) { + WriteException(e); + } + }); } \ No newline at end of file diff --git a/VideoCaptureManager.h b/VideoCaptureManager.h index 74ad03d78277e8ff1b552a02a50b0d549cf60146..32bc635a869d502b2558ebc71446bd35ef41e388 100644 --- a/VideoCaptureManager.h +++ b/VideoCaptureManager.h @@ -23,6 +23,7 @@ using namespace Windows::ApplicationModel::Core; using namespace Windows::Devices::Enumeration; using namespace Windows::Media::Capture; using namespace Windows::Foundation; +using namespace Windows::UI::Xaml::Controls; using namespace Concurrency; namespace RingClientUWP @@ -30,7 +31,9 @@ namespace RingClientUWP delegate void StartPreviewing(); delegate void StopPreviewing(); -delegate Windows::UI::Xaml::Controls::CaptureElement^ GetSink(); +delegate CaptureElement^ GetSink(); +delegate CaptureElement^ GetSettingsPreviewSink(); +delegate void CaptureEnumerationComplete(); namespace Video { @@ -43,12 +46,16 @@ internal: bool get() { return isPreviewing_; } void set(bool value) { isPreviewing_ = value; } } + property bool isSettingsPreviewing + { + bool get() { return isSettingsPreviewing_; } + void set(bool value) { isSettingsPreviewing_ = value; } + } Map<String^,String^>^ getSettings(String^ device); VideoCaptureManager(); - Windows::Graphics::Display::DisplayInformation^ displayInformation; Windows::Graphics::Display::DisplayOrientations displayOrientation; Windows::System::Display::DisplayRequest^ displayRequest; @@ -68,21 +75,20 @@ internal: Platform::Agile<Windows::Media::Capture::MediaCapture^> mediaCapture; - task<void> InitializeCameraAsync(); - task<void> StartPreviewAsync(); + task<void> InitializeCameraAsync(bool isSettingsPreview); + task<void> StartPreviewAsync(bool isSettingsPreview); task<void> StopPreviewAsync(); task<void> EnumerateWebcamsAsync(); task<void> CleanupCameraAsync(); // event tokens EventRegistrationToken mediaCaptureFailedEventToken; - EventRegistrationToken displayInformationEventToken; EventRegistrationToken visibilityChangedEventToken; cancellation_token_source* captureTaskTokenSource; void MediaCapture_Failed(MediaCapture ^currentCaptureObject, MediaCaptureFailedEventArgs^ errorEventArgs); - void AddVideoDevice(uint8_t index); + task<void> AddVideoDeviceAsync(uint8_t index); void SetCaptureSettings(); DispatcherTimer^ videoFrameCopyInvoker; @@ -93,9 +99,12 @@ internal: event StartPreviewing^ startPreviewing; event StopPreviewing^ stopPreviewing; event GetSink^ getSink; + event GetSettingsPreviewSink^ getSettingsPreviewSink; + event CaptureEnumerationComplete^ captureEnumerationComplete; private: bool isPreviewing_; + bool isSettingsPreviewing_; }; diff --git a/VideoPage.xaml b/VideoPage.xaml index 79fdecc0ce74692172e1467986fd38132c8d6206..a1cf59f3fefe5512a51f081e4dd6addfc2e09a8d 100644 --- a/VideoPage.xaml +++ b/VideoPage.xaml @@ -1,4 +1,22 @@ -<Page +<!-- ********************************************************************** +* Copyright (C) 2016 by Savoir-faire Linux * +* Author: Jäger Nicolas<nicolas.jager@savoirfairelinux.com> * +* Author: Traczyk Andreas<andreas.traczyk@savoirfairelinux.com> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see <http://www.gnu.org/licenses/> . * +*********************************************************************** --> +<Page x:Class="RingClientUWP.Views.VideoPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" diff --git a/VideoPage.xaml.cpp b/VideoPage.xaml.cpp index d4be80c6b5a7ccb23efe89dd8d98b8a5a1c5dc02..959ae62d43f3e0bbe9f9d040e990d031a4857394 100644 --- a/VideoPage.xaml.cpp +++ b/VideoPage.xaml.cpp @@ -54,9 +54,6 @@ VideoPage::VideoPage() { InitializeComponent(); - VideoManager::instance->captureManager()->displayInformation = DisplayInformation::GetForCurrentView(); - VideoManager::instance->captureManager()->EnumerateWebcamsAsync(); - Page::NavigationCacheMode = Navigation::NavigationCacheMode::Required; VideoManager::instance->rendererManager()->writeVideoFrame += @@ -77,7 +74,7 @@ VideoPage::VideoPage() previousTask.get(); } catch (Platform::Exception^ e) { - RingDebug::instance->WriteLine( "Caught exception from previous task.\n" ); + RingDebug::instance->WriteLine( "Caught exception from WriteFrameAsSoftwareBitmapAsync task.\n" ); } }); } diff --git a/VideoPage.xaml.h b/VideoPage.xaml.h index 48c82d71ea01079ada3d167b6a556cba547edab0..b3048d87e91c5dd49548681cbe3a0f081689e4eb 100644 --- a/VideoPage.xaml.h +++ b/VideoPage.xaml.h @@ -1,4 +1,3 @@ -#pragma once /************************************************************************** * Copyright (C) 2016 by Savoir-faire Linux * * Author: J�ger Nicolas <nicolas.jager@savoirfairelinux.com> * @@ -17,6 +16,8 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see <http://www.gnu.org/licenses/>. * **************************************************************************/ +#pragma once + #include "VideoPage.g.h" #include "MessageTextPage.xaml.h" diff --git a/WelcomePage.xaml b/WelcomePage.xaml index d8431df5809f52c71b17335dd8c17c641674175d..b52a07ff15d34ab7eb0145b3c1ea47443f7841c1 100644 --- a/WelcomePage.xaml +++ b/WelcomePage.xaml @@ -1,6 +1,7 @@ <!-- ********************************************************************** * Copyright (C) 2016 by Savoir-faire Linux * * Author: Jäger Nicolas<nicolas.jager@savoirfairelinux.com> * +* Author: Traczyk Andreas<andreas.traczyk@savoirfairelinux.com> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * diff --git a/WelcomePage.xaml.cpp b/WelcomePage.xaml.cpp index f61275d15b3555ce08c99dd80839da8bfd17c410..b87451be05781be10f618b18ea19e2e5fa8d1b1b 100644 --- a/WelcomePage.xaml.cpp +++ b/WelcomePage.xaml.cpp @@ -1,6 +1,7 @@ /************************************************************************** * Copyright (C) 2016 by Savoir-faire Linux * * Author: J�ger Nicolas <nicolas.jager@savoirfairelinux.com> * +* Author: Traczyk Andreas <andreas.traczyk@savoirfairelinux.com> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * diff --git a/WelcomePage.xaml.h b/WelcomePage.xaml.h index cd8a716476f357ac81aa8361e78c76372cab0c52..0e0c34f3a5164f37b5a1e718b25db4d78d769b24 100644 --- a/WelcomePage.xaml.h +++ b/WelcomePage.xaml.h @@ -1,4 +1,3 @@ -#pragma once /************************************************************************** * Copyright (C) 2016 by Savoir-faire Linux * * Author: J�ger Nicolas <nicolas.jager@savoirfairelinux.com> * @@ -17,6 +16,8 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see <http://www.gnu.org/licenses/>. * **************************************************************************/ +#pragma once + #include "WelcomePage.g.h" namespace RingClientUWP diff --git a/_language-fr.appx b/_language-fr.appx index 49546c5da3f6d3be79b44c40b259a8206fd7b285..cbd51770704c55fe85fb8207f5bce4990458eb4d 100644 Binary files a/_language-fr.appx and b/_language-fr.appx differ diff --git a/_scale-100.appx b/_scale-100.appx index 2ddc1d5eff625a5c24c37d149cad23525f3c2d45..622a8ba31e824c812aa8194d773b918e17dc8af6 100644 Binary files a/_scale-100.appx and b/_scale-100.appx differ diff --git a/_scale-125.appx b/_scale-125.appx index 4696faa956d0bbfa2a9ea9acf8f212676ae44d47..45387f054f1bb902622e2d6903841b883c6ef60b 100644 Binary files a/_scale-125.appx and b/_scale-125.appx differ diff --git a/_scale-150.appx b/_scale-150.appx index 04caab6f5e3fc98964adb0732257c1a7bff852a7..0ffbcee757962fa987c0c1f112058b9fbd1c632b 100644 Binary files a/_scale-150.appx and b/_scale-150.appx differ diff --git a/_scale-400.appx b/_scale-400.appx index 0b27623d07c12e86c82149aea9b233e170c6bc3f..37070f6429f51611a18c005fcf7414b22b2c2bc5 100644 Binary files a/_scale-400.appx and b/_scale-400.appx differ diff --git a/ring-client-uwp.vcxproj b/ring-client-uwp.vcxproj index 720d25da9a5a0ec583c3d05c5094bdcaa9e1fdc5..8b532db1452429f4890991cb70b149bf7c07dfe9 100644 --- a/ring-client-uwp.vcxproj +++ b/ring-client-uwp.vcxproj @@ -189,6 +189,9 @@ <ClInclude Include="MainPage.xaml.h"> <DependentUpon>MainPage.xaml</DependentUpon> </ClInclude> + <ClInclude Include="PreviewPage.xaml.h"> + <DependentUpon>PreviewPage.xaml</DependentUpon> + </ClInclude> <ClInclude Include="RingConsolePanel.xaml.h"> <DependentUpon>RingConsolePanel.xaml</DependentUpon> </ClInclude> @@ -227,6 +230,7 @@ <SubType>Designer</SubType> </Page> <Page Include="MessageTextPage.xaml" /> + <Page Include="PreviewPage.xaml" /> <Page Include="RingConsolePanel.xaml" /> <Page Include="SmartPanel.xaml" /> <Page Include="Styles.xaml" /> @@ -318,6 +322,9 @@ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> </ClCompile> + <ClCompile Include="PreviewPage.xaml.cpp"> + <DependentUpon>PreviewPage.xaml</DependentUpon> + </ClCompile> <ClCompile Include="RingConsolePanel.xaml.cpp"> <DependentUpon>RingConsolePanel.xaml</DependentUpon> </ClCompile> diff --git a/ring-client-uwp.vcxproj.filters b/ring-client-uwp.vcxproj.filters index 42897f4659019824c413f939e048b57bb2dd0338..562fb4691856caabdd20afded278af9cd26fc1b8 100644 --- a/ring-client-uwp.vcxproj.filters +++ b/ring-client-uwp.vcxproj.filters @@ -263,6 +263,9 @@ <Page Include="WelcomePage.xaml"> <Filter>Views</Filter> </Page> + <Page Include="PreviewPage.xaml"> + <Filter>Views</Filter> + </Page> </ItemGroup> <ItemGroup> <Filter Include="Assets">