//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
//
// AdvancedCapture.xaml.cpp
// Implementation of the AdvancedCapture class
//
#include "pch.h"
#include "AdvancedCapture.xaml.h"
using namespace SDKSample::MediaCapture;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Navigation;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Platform;
using namespace Windows::UI;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::Storage;
using namespace Windows::Media::MediaProperties;
using namespace Windows::Storage::Streams;
using namespace Windows::System;
using namespace Windows::UI::Xaml::Media::Imaging;
using namespace Windows::Devices::Enumeration;
ref class ReencodeState sealed
{
public:
ReencodeState()
{
}
virtual ~ReencodeState()
{
if (InputStream != nullptr)
{
delete InputStream;
}
if (OutputStream != nullptr)
{
delete OutputStream;
}
}
internal:
Windows::Storage::Streams::IRandomAccessStream ^InputStream;
Windows::Storage::Streams::IRandomAccessStream ^OutputStream;
Windows::Storage::StorageFile ^PhotoStorage;
Windows::Graphics::Imaging::BitmapDecoder ^Decoder;
Windows::Graphics::Imaging::BitmapEncoder ^Encoder;
};
AdvancedCapture::AdvancedCapture()
{
InitializeComponent();
ScenarioInit();
}
///
/// Invoked when this page is about to be displayed in a Frame.
///
/// Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.
void AdvancedCapture::OnNavigatedTo(NavigationEventArgs^ e)
{
// A pointer back to the main page. This is needed if you want to call methods in MainPage such
// as NotifyUser()
rootPage = MainPage::Current;
m_orientationChangedEventToken = Windows::Graphics::Display::DisplayProperties::OrientationChanged += ref new Windows::Graphics::Display::DisplayPropertiesEventHandler(this, &AdvancedCapture::DisplayProperties_OrientationChanged);
}
void AdvancedCapture::OnNavigatedFrom(NavigationEventArgs^ e)
{
Windows::Media::MediaControl::SoundLevelChanged -= m_eventRegistrationToken;
Windows::Graphics::Display::DisplayProperties::OrientationChanged -= m_orientationChangedEventToken;
}
void AdvancedCapture::ScenarioInit()
{
rootPage = MainPage::Current;
btnStartDevice2->IsEnabled = true;
btnStartPreview2->IsEnabled = false;
m_bRecording = false;
m_bPreviewing = false;
m_bEffectAdded = false;
previewElement2->Source = nullptr;
ShowStatusMessage("");
EffectTypeCombo->IsEnabled = false;
previewCanvas2->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
EnumerateWebcamsAsync();
m_bSuspended = false;
}
void AdvancedCapture::ScenarioReset()
{
previewCanvas2->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
ScenarioInit();
}
void AdvancedCapture::Failed(Windows::Media::Capture::MediaCapture ^currentCaptureObject, Windows::Media::Capture::MediaCaptureFailedEventArgs^ currentFailure)
{
String ^message = "Fatal error" + currentFailure->Message;
create_task(Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High,
ref new Windows::UI::Core::DispatchedHandler([this, message]()
{
ShowStatusMessage(message);
})));
}
void AdvancedCapture::btnStartDevice_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
try
{
EnableButton(false, "StartDevice");
ShowStatusMessage("Starting device");
auto mediaCapture = ref new Windows::Media::Capture::MediaCapture();
m_mediaCaptureMgr = mediaCapture;
auto settings = ref new Windows::Media::Capture::MediaCaptureInitializationSettings();
auto chosenDevInfo = m_devInfoCollection->GetAt(EnumedDeviceList2->SelectedIndex);
settings->VideoDeviceId = chosenDevInfo->Id;
if (chosenDevInfo->EnclosureLocation != nullptr && chosenDevInfo->EnclosureLocation->Panel == Windows::Devices::Enumeration::Panel::Back)
{
m_bRotateVideoOnOrientationChange = true;
m_bReversePreviewRotation = false;
}
else if (chosenDevInfo->EnclosureLocation != nullptr && chosenDevInfo->EnclosureLocation->Panel == Windows::Devices::Enumeration::Panel::Front)
{
m_bRotateVideoOnOrientationChange = true;
m_bReversePreviewRotation = true;
}
else
{
m_bRotateVideoOnOrientationChange = false;
}
create_task(mediaCapture->InitializeAsync(settings)).then([this](task initTask)
{
try
{
initTask.get();
auto mediaCapture = m_mediaCaptureMgr.Get();
DisplayProperties_OrientationChanged(nullptr);
EnableButton(true, "StartPreview");
EnableButton(true, "StartStopRecord");
EnableButton(true, "TakePhoto");
ShowStatusMessage("Device initialized successful");
EffectTypeCombo->IsEnabled = true;
mediaCapture->Failed += ref new Windows::Media::Capture::MediaCaptureFailedEventHandler(this, &AdvancedCapture::Failed);
}
catch (Exception ^ e)
{
ShowExceptionMessage(e);
}
});
}
catch (Platform::Exception^ e)
{
ShowExceptionMessage(e);
}
}
void AdvancedCapture::btnStartPreview_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
m_bPreviewing = false;
try
{
ShowStatusMessage("Starting preview");
EnableButton(false, "StartPreview");
auto mediaCapture = m_mediaCaptureMgr.Get();
previewCanvas2->Visibility = Windows::UI::Xaml::Visibility::Visible;
previewElement2->Source = mediaCapture;
create_task(mediaCapture->StartPreviewAsync()).then([this](task previewTask)
{
try
{
previewTask.get();
m_bPreviewing = true;
ShowStatusMessage("Start preview successful");
}
catch (Exception ^e)
{
ShowExceptionMessage(e);
}
});
}
catch (Platform::Exception^ e)
{
m_bPreviewing = false;
previewElement2->Source = nullptr;
EnableButton(true, "StartPreview");
ShowExceptionMessage(e);
}
}
void AdvancedCapture::lstEnumedDevices_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e)
{
if ( m_bPreviewing )
{
create_task(m_mediaCaptureMgr->StopPreviewAsync()).then([this](task previewTask)
{
try
{
previewTask.get();
m_bPreviewing = false;
}
catch (Exception ^e)
{
ShowExceptionMessage(e);
}
});
}
btnStartDevice2->IsEnabled = true;
btnStartPreview2->IsEnabled = false;
m_bRecording = false;
previewElement2->Source = nullptr;
EffectTypeCombo->IsEnabled = false;
m_bEffectAdded = false;
m_bEffectAddedToRecord = false;
m_bEffectAddedToPhoto = false;
ShowStatusMessage("");
}
void AdvancedCapture::EnumerateWebcamsAsync()
{
try
{
ShowStatusMessage("Enumerating Webcams...");
m_devInfoCollection = nullptr;
EnumedDeviceList2->Items->Clear();
task(DeviceInformation::FindAllAsync(DeviceClass::VideoCapture)).then([this](task findTask)
{
try
{
m_devInfoCollection = findTask.get();
if (m_devInfoCollection == nullptr || m_devInfoCollection->Size == 0)
{
ShowStatusMessage("No WebCams found.");
}
else
{
for(unsigned int i = 0; i < m_devInfoCollection->Size; i++)
{
auto devInfo = m_devInfoCollection->GetAt(i);
EnumedDeviceList2->Items->Append(devInfo->Name);
}
EnumedDeviceList2->SelectedIndex = 0;
ShowStatusMessage("Enumerating Webcams completed successfully.");
btnStartDevice2->IsEnabled = true;
}
}
catch (Exception ^e)
{
ShowExceptionMessage(e);
}
});
}
catch (Platform::Exception^ e)
{
ShowExceptionMessage(e);
}
}
void AdvancedCapture::AddEffectToImageStream()
{
auto mediaCapture = m_mediaCaptureMgr.Get();
Windows::Media::Capture::VideoDeviceCharacteristic charecteristic = mediaCapture->MediaCaptureSettings->VideoDeviceCharacteristic;
if((charecteristic != Windows::Media::Capture::VideoDeviceCharacteristic::AllStreamsIdentical) &&
(charecteristic != Windows::Media::Capture::VideoDeviceCharacteristic::PreviewPhotoStreamsIdentical) &&
(charecteristic != Windows::Media::Capture::VideoDeviceCharacteristic::RecordPhotoStreamsIdentical))
{
Windows::Media::MediaProperties::IMediaEncodingProperties ^props = mediaCapture->VideoDeviceController->GetMediaStreamProperties(Windows::Media::Capture::MediaStreamType::Photo);
if(props->Type->Equals("Image"))
{
//Switch to a video media type instead since we can't add an effect to an image media type
Windows::Foundation::Collections::IVectorView^ supportedPropsList = mediaCapture->VideoDeviceController->GetAvailableMediaStreamProperties(Windows::Media::Capture::MediaStreamType::Photo);
{
unsigned int i = 0;
while (i < supportedPropsList->Size)
{
Windows::Media::MediaProperties::IMediaEncodingProperties^ props = supportedPropsList->GetAt(i);
String^ s = props->Type;
if(props->Type->Equals("Video"))
{
task(mediaCapture->VideoDeviceController->SetMediaStreamPropertiesAsync(Windows::Media::Capture::MediaStreamType::Photo,props)).then([this](task changeTypeTask)
{
try
{
changeTypeTask.get();
ShowStatusMessage("Change type on photo stream successful");
//Now add the effect on the image pin
task(m_mediaCaptureMgr->AddEffectAsync(Windows::Media::Capture::MediaStreamType::Photo,"OcvTransform.OcvImageManipulations", nullptr)).then([this](task effectTask3)
{
try
{
effectTask3.get();
m_bEffectAddedToPhoto = true;
ShowStatusMessage("Adding effect to photo stream successful");
EffectTypeCombo->IsEnabled = true;
}
catch(Exception ^e)
{
ShowExceptionMessage(e);
EffectTypeCombo->IsEnabled = true;
}
});
}
catch(Exception ^e)
{
ShowExceptionMessage(e);
EffectTypeCombo->IsEnabled = true;
}
});
break;
}
i++;
}
}
}
else
{
//Add the effect to the image pin if the type is already "Video"
task(mediaCapture->AddEffectAsync(Windows::Media::Capture::MediaStreamType::Photo,"OcvTransform.OcvImageManipulations", nullptr)).then([this](task effectTask3)
{
try
{
effectTask3.get();
m_bEffectAddedToPhoto = true;
ShowStatusMessage("Adding effect to photo stream successful");
EffectTypeCombo->IsEnabled = true;
}
catch(Exception ^e)
{
ShowExceptionMessage(e);
EffectTypeCombo->IsEnabled = true;
}
});
}
}
}
void AdvancedCapture::ShowStatusMessage(Platform::String^ text)
{
rootPage->NotifyUser(text, NotifyType::StatusMessage);
}
void AdvancedCapture::ShowExceptionMessage(Platform::Exception^ ex)
{
rootPage->NotifyUser(ex->Message, NotifyType::ErrorMessage);
}
void AdvancedCapture::EnableButton(bool enabled, String^ name)
{
if (name->Equals("StartDevice"))
{
btnStartDevice2->IsEnabled = enabled;
}
else if (name->Equals("StartPreview"))
{
btnStartPreview2->IsEnabled = enabled;
}
}
task AdvancedCapture::ReencodePhotoAsync(
Windows::Storage::StorageFile ^tempStorageFile,
Windows::Storage::FileProperties::PhotoOrientation photoRotation)
{
ReencodeState ^state = ref new ReencodeState();
return create_task(tempStorageFile->OpenAsync(Windows::Storage::FileAccessMode::Read)).then([state](Windows::Storage::Streams::IRandomAccessStream ^stream)
{
state->InputStream = stream;
return Windows::Graphics::Imaging::BitmapDecoder::CreateAsync(state->InputStream);
}).then([state](Windows::Graphics::Imaging::BitmapDecoder ^decoder)
{
state->Decoder = decoder;
return Windows::Storage::KnownFolders::PicturesLibrary->CreateFileAsync(PHOTO_FILE_NAME, Windows::Storage::CreationCollisionOption::GenerateUniqueName);
}).then([state](Windows::Storage::StorageFile ^storageFile)
{
state->PhotoStorage = storageFile;
return state->PhotoStorage->OpenAsync(Windows::Storage::FileAccessMode::ReadWrite);
}).then([state](Windows::Storage::Streams::IRandomAccessStream ^stream)
{
state->OutputStream = stream;
state->OutputStream->Size = 0;
return Windows::Graphics::Imaging::BitmapEncoder::CreateForTranscodingAsync(state->OutputStream, state->Decoder);
}).then([state, photoRotation](Windows::Graphics::Imaging::BitmapEncoder ^encoder)
{
state->Encoder = encoder;
auto properties = ref new Windows::Graphics::Imaging::BitmapPropertySet();
properties->Insert("System.Photo.Orientation",
ref new Windows::Graphics::Imaging::BitmapTypedValue((unsigned short)photoRotation, Windows::Foundation::PropertyType::UInt16));
return create_task(state->Encoder->BitmapProperties->SetPropertiesAsync(properties));
}).then([state]()
{
return state->Encoder->FlushAsync();
}).then([tempStorageFile, state](task previousTask)
{
auto result = state->PhotoStorage;
delete state;
tempStorageFile->DeleteAsync(Windows::Storage::StorageDeleteOption::PermanentDelete);
previousTask.get();
return result;
});
}
Windows::Storage::FileProperties::PhotoOrientation AdvancedCapture::GetCurrentPhotoRotation()
{
bool counterclockwiseRotation = m_bReversePreviewRotation;
if (m_bRotateVideoOnOrientationChange)
{
return PhotoRotationLookup(Windows::Graphics::Display::DisplayProperties::CurrentOrientation, counterclockwiseRotation);
}
else
{
return Windows::Storage::FileProperties::PhotoOrientation::Normal;
}
}
void AdvancedCapture::PrepareForVideoRecording()
{
Windows::Media::Capture::MediaCapture ^mediaCapture = m_mediaCaptureMgr.Get();
if (mediaCapture == nullptr)
{
return;
}
bool counterclockwiseRotation = m_bReversePreviewRotation;
if (m_bRotateVideoOnOrientationChange)
{
mediaCapture->SetRecordRotation(VideoRotationLookup(Windows::Graphics::Display::DisplayProperties::CurrentOrientation, counterclockwiseRotation));
}
else
{
mediaCapture->SetRecordRotation(Windows::Media::Capture::VideoRotation::None);
}
}
void AdvancedCapture::DisplayProperties_OrientationChanged(Platform::Object^ sender)
{
Windows::Media::Capture::MediaCapture ^mediaCapture = m_mediaCaptureMgr.Get();
if (mediaCapture == nullptr)
{
return;
}
bool previewMirroring = mediaCapture->GetPreviewMirroring();
bool counterclockwiseRotation = (previewMirroring && !m_bReversePreviewRotation) ||
(!previewMirroring && m_bReversePreviewRotation);
if (m_bRotateVideoOnOrientationChange)
{
mediaCapture->SetPreviewRotation(VideoRotationLookup(Windows::Graphics::Display::DisplayProperties::CurrentOrientation, counterclockwiseRotation));
}
else
{
mediaCapture->SetPreviewRotation(Windows::Media::Capture::VideoRotation::None);
}
}
Windows::Storage::FileProperties::PhotoOrientation AdvancedCapture::PhotoRotationLookup(
Windows::Graphics::Display::DisplayOrientations displayOrientation, bool counterclockwise)
{
switch (displayOrientation)
{
case Windows::Graphics::Display::DisplayOrientations::Landscape:
return Windows::Storage::FileProperties::PhotoOrientation::Normal;
case Windows::Graphics::Display::DisplayOrientations::Portrait:
return (counterclockwise) ? Windows::Storage::FileProperties::PhotoOrientation::Rotate270:
Windows::Storage::FileProperties::PhotoOrientation::Rotate90;
case Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped:
return Windows::Storage::FileProperties::PhotoOrientation::Rotate180;
case Windows::Graphics::Display::DisplayOrientations::PortraitFlipped:
return (counterclockwise) ? Windows::Storage::FileProperties::PhotoOrientation::Rotate90 :
Windows::Storage::FileProperties::PhotoOrientation::Rotate270;
default:
return Windows::Storage::FileProperties::PhotoOrientation::Unspecified;
}
}
Windows::Media::Capture::VideoRotation AdvancedCapture::VideoRotationLookup(
Windows::Graphics::Display::DisplayOrientations displayOrientation, bool counterclockwise)
{
switch (displayOrientation)
{
case Windows::Graphics::Display::DisplayOrientations::Landscape:
return Windows::Media::Capture::VideoRotation::None;
case Windows::Graphics::Display::DisplayOrientations::Portrait:
return (counterclockwise) ? Windows::Media::Capture::VideoRotation::Clockwise270Degrees :
Windows::Media::Capture::VideoRotation::Clockwise90Degrees;
case Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped:
return Windows::Media::Capture::VideoRotation::Clockwise180Degrees;
case Windows::Graphics::Display::DisplayOrientations::PortraitFlipped:
return (counterclockwise) ? Windows::Media::Capture::VideoRotation::Clockwise90Degrees:
Windows::Media::Capture::VideoRotation::Clockwise270Degrees ;
default:
return Windows::Media::Capture::VideoRotation::None;
}
}
void SDKSample::MediaCapture::AdvancedCapture::Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
try
{
create_task(m_mediaCaptureMgr->ClearEffectsAsync(Windows::Media::Capture::MediaStreamType::VideoPreview)).then([this](task cleanTask)
{
m_bEffectAdded = true;
int index = EffectTypeCombo->SelectedIndex;
PropertySet^ props = ref new PropertySet();
props->Insert(L"{698649BE-8EAE-4551-A4CB-3EC98FBD3D86}", index);
create_task(m_mediaCaptureMgr->AddEffectAsync(Windows::Media::Capture::MediaStreamType::VideoPreview,"OcvTransform.OcvImageManipulations", props)).then([this](task effectTask)
{
try
{
effectTask.get();
auto mediaCapture = m_mediaCaptureMgr.Get();
Windows::Media::Capture::VideoDeviceCharacteristic charecteristic = mediaCapture->MediaCaptureSettings->VideoDeviceCharacteristic;
ShowStatusMessage("Add effect successful to preview stream successful");
if((charecteristic != Windows::Media::Capture::VideoDeviceCharacteristic::AllStreamsIdentical) &&
(charecteristic != Windows::Media::Capture::VideoDeviceCharacteristic::PreviewRecordStreamsIdentical))
{
Windows::Media::MediaProperties::IMediaEncodingProperties ^props = mediaCapture->VideoDeviceController->GetMediaStreamProperties(Windows::Media::Capture::MediaStreamType::VideoRecord);
Windows::Media::MediaProperties::VideoEncodingProperties ^videoEncodingProperties = static_cast(props);
if(!videoEncodingProperties->Subtype->Equals("H264")) //Can't add an effect to an H264 stream
{
task(mediaCapture->AddEffectAsync(Windows::Media::Capture::MediaStreamType::VideoRecord,"OcvTransform.OcvImageManipulations", nullptr)).then([this](task effectTask2)
{
try
{
effectTask2.get();
ShowStatusMessage("Add effect successful to record stream successful");
m_bEffectAddedToRecord = true;
AddEffectToImageStream();
EffectTypeCombo->IsEnabled = true;
}
catch(Exception ^e)
{
ShowExceptionMessage(e);
EffectTypeCombo->IsEnabled = true;
}
});
}
else
{
AddEffectToImageStream();
EffectTypeCombo->IsEnabled = true;
}
}
else
{
AddEffectToImageStream();
EffectTypeCombo->IsEnabled = true;
}
}
catch (Exception ^e)
{
ShowExceptionMessage(e);
EffectTypeCombo->IsEnabled = true;
}
});
});
}
catch (Platform::Exception ^e)
{
ShowExceptionMessage(e);
EffectTypeCombo->IsEnabled = true;
}
}