video: add video

- adds incoming video
- adds webcam preview
- adds device enumeration
- adds daemon video signal handlers
- adds outgoing video

Tuleap: #1200
Change-Id: Ife5f6acc2ee400665e096e44e2111e03cab0299a
diff --git a/VideoPage.xaml.cpp b/VideoPage.xaml.cpp
index 112741c..ee44f93 100644
--- a/VideoPage.xaml.cpp
+++ b/VideoPage.xaml.cpp
@@ -20,8 +20,11 @@
 

 #include "VideoPage.xaml.h"

 

+#include <MemoryBuffer.h>   // IMemoryBufferByteAccess

+

 using namespace RingClientUWP::Views;

 using namespace ViewModel;

+using namespace Video;

 

 using namespace Concurrency;

 using namespace Platform;

@@ -39,19 +42,98 @@
 using namespace Windows::ApplicationModel::Core;

 using namespace Windows::UI::Core;

 

+using namespace Windows::Graphics::Display;

+using namespace Windows::Graphics::Imaging;

+using namespace Windows::Media;

+using namespace Windows::UI::Xaml::Media::Imaging;

+using namespace Windows::Media::Capture;

+using namespace Windows::Devices::Sensors;

+

 VideoPage::VideoPage()

 {

     InitializeComponent();

+

+    VideoManager::instance->captureManager()->displayInformation = DisplayInformation::GetForCurrentView();

+    VideoManager::instance->captureManager()->EnumerateWebcamsAsync();

+

+    Page::NavigationCacheMode = Navigation::NavigationCacheMode::Required;

+

+    VideoManager::instance->rendererManager()->writeVideoFrame +=

+        ref new WriteVideoFrame([this](String^ id, uint8_t* buf, int width, int height)

+    {

+        CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::Normal,

+        ref new DispatchedHandler([=]() {

+            try {

+                if (!VideoManager::instance->rendererManager()->renderers->Size)

+                    return;

+                VideoManager::instance->rendererManager()->renderer(id)->isRendering = true;

+                create_task(WriteFrameAsSoftwareBitmapAsync(id, buf, width, height))

+                    .then([=](task<void> previousTask) {

+                    try {

+                        previousTask.get();

+                    }

+                    catch (Platform::Exception^ e) {

+                        WriteLine( "Caught exception from previous task.\n" );

+                    }

+                });

+            }

+            catch(Platform::COMException^ e) {

+                WriteLine(e->ToString());

+            }

+        }));

+    });

+

+    VideoManager::instance->captureManager()->startPreviewing +=

+        ref new StartPreviewing([this]()

+    {

+        PreviewImage->Visibility = Windows::UI::Xaml::Visibility::Visible;

+        PreviewImage->FlowDirection = VideoManager::instance->captureManager()->mirroringPreview ?

+            Windows::UI::Xaml::FlowDirection::RightToLeft :

+            Windows::UI::Xaml::FlowDirection::LeftToRight;

+    });

+

+    VideoManager::instance->captureManager()->stopPreviewing +=

+        ref new StopPreviewing([this]()

+    {

+        PreviewImage->Source = nullptr;

+        PreviewImage->Visibility = Windows::UI::Xaml::Visibility::Collapsed;

+    });

+

+    VideoManager::instance->captureManager()->getSink +=

+        ref new GetSink([this]()

+    {

+        return PreviewImage;

+    });

+

+    VideoManager::instance->rendererManager()->clearRenderTarget +=

+        ref new ClearRenderTarget([this]()

+    {

+        IncomingVideoImage->Source = nullptr;

+    });

+

+    RingD::instance->incomingAccountMessage +=

+        ref new IncomingAccountMessage([&](String^ accountId, String^ from, String^ payload)

+    {

+        scrollDown();

+    });

+

+    RingD::instance->stateChange +=

+        ref new StateChange([&](String^ callId, CallStatus state, int code)

+    {

+        if (state == CallStatus::ENDED) {

+            Video::VideoManager::instance->rendererManager()->raiseClearRenderTarget();

+        }

+    });

+

+    RingD::instance->incomingAccountMessage += ref new IncomingAccountMessage([&](String^ accountId,

+    String^ from, String^ payload) {

+        scrollDown();

+    });

 }

 

 void

 RingClientUWP::Views::VideoPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e)

 {

-    RingD::instance->incomingAccountMessage += ref new IncomingAccountMessage([&](String^ accountId,

-    String^ from, String^ payload) {

-        scrollDown();

-    });

-

     updatePageContent();

 }

 

@@ -202,4 +284,54 @@
 void RingClientUWP::Views::VideoPage::btnAny_exited(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)

 {

     barFading_ = true;

-}
\ No newline at end of file
+}

+

+task<void>

+VideoPage::WriteFrameAsSoftwareBitmapAsync(String^ id, uint8_t* buf, int width, int height)

+{

+    auto vframe = ref new VideoFrame(BitmapPixelFormat::Bgra8, width, height);

+    auto frame = vframe->SoftwareBitmap;

+

+    const int BYTES_PER_PIXEL = 4;

+

+    BitmapBuffer^ buffer = frame->LockBuffer(BitmapBufferAccessMode::ReadWrite);

+    IMemoryBufferReference^ reference = buffer->CreateReference();

+

+    Microsoft::WRL::ComPtr<IMemoryBufferByteAccess> byteAccess;

+    if (SUCCEEDED(reinterpret_cast<IUnknown*>(reference)->QueryInterface(IID_PPV_ARGS(&byteAccess))))

+    {

+        byte* data;

+        unsigned capacity;

+        byteAccess->GetBuffer(&data, &capacity);

+

+        auto desc = buffer->GetPlaneDescription(0);

+

+        for (int row = 0; row < desc.Height; row++)

+        {

+            for (int col = 0; col < desc.Width; col++)

+            {

+                auto currPixel = desc.StartIndex + desc.Stride * row + BYTES_PER_PIXEL * col;

+

+                data[currPixel + 0] = buf[currPixel + 0];

+                data[currPixel + 1] = buf[currPixel + 1];

+                data[currPixel + 2] = buf[currPixel + 2];

+            }

+        }

+    }

+    delete reference;

+    delete buffer;

+

+    VideoManager::instance->rendererManager()->renderer(id)->isRendering = false;

+

+    auto sbSource = ref new Media::Imaging::SoftwareBitmapSource();

+    return create_task(sbSource->SetBitmapAsync(frame))

+        .then([this, sbSource]()

+    {

+        try {

+            IncomingVideoImage->Source = sbSource;

+        }

+        catch (Exception^ e) {

+            WriteException(e);

+        }

+    });

+}