I'm working on a video recording app that supports the VideoStabilization effect, but when I start recording, I receive the following through the MediaCapture.Failed event almost instantly:
The sample allocator is currently empty, due to outstanding requests.
(0xC00D4A3E)
It only happens when I use the recommended configuration from the effect, though. If I don't call SetUpVideoStabilizationRecommendationAsync, it works fine.
Here is how I'm setting it up:
private MediaEncodingProfile _encodingProfile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto);
private async Task CreateVideoStabilizationEffectAsync()
{
var definition = new VideoStabilizationEffectDefinition();
_videoStabilizationEffect = (VideoStabilizationEffect)await _mediaCapture.AddVideoEffectAsync(definition, MediaStreamType.VideoRecord);
_videoStabilizationEffect.Enabled = true;
await SetUpVideoStabilizationRecommendationAsync();
}
private async Task SetUpVideoStabilizationRecommendationAsync()
{
var properties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoRecord) as VideoEncodingProperties;
var recommendation = _videoStabilizationEffect.GetRecommendedStreamConfiguration(_mediaCapture.VideoDeviceController, properties);
if (recommendation.InputProperties != null)
{
await _mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoRecord, recommendation.InputProperties);
}
if (recommendation.OutputProperties != null)
{
_encodingProfile.Video = recommendation.OutputProperties;
}
}
private async Task StartRecordingAsync()
{
var videoFile = await KnownFolders.PicturesLibrary.CreateFileAsync("StableVideo.mp4", CreationCollisionOption.GenerateUniqueName);
await _mediaCapture.StartRecordToStorageFileAsync(_encodingProfile, videoFile);
}
The desiredProperties parameter of the GetRecommendedStreamConfiguration method needs to get MediaEncodingProfile that will be used when calling your choice of MediaCapture.StartRecordTo* (i.e. the "output properties") to see what your desired VideoEncodingProperties are.
The error is being triggered because the VideoEncodingProperties from the VideoDeviceController (i.e. the "input properties") are being passed instead. If you think about it, an instance of the the VideoDeviceController is already being passed in as a parameter to the method, so the effect can already access the information in that properties var; it wouldn't make much sense to have to pass those in separately at the same time. Instead, what it needs is information about the other endpoint. Does that make sense? At least that's how I try to rationalize it.
The official SDK sample for VideoStabilization on the Microsoft github repo shows how to do this correctly:
/// <summary>
/// Configures the pipeline to use the optimal resolutions for VS based on the settings currently in use
/// </summary>
/// <returns></returns>
private async Task SetUpVideoStabilizationRecommendationAsync()
{
Debug.WriteLine("Setting up VS recommendation...");
// Get the recommendation from the effect based on our current input and output configuration
var recommendation = _videoStabilizationEffect.GetRecommendedStreamConfiguration(_mediaCapture.VideoDeviceController, _encodingProfile.Video);
// Handle the recommendation for the input into the effect, which can contain a larger resolution than currently configured, so cropping is minimized
if (recommendation.InputProperties != null)
{
// Back up the current input properties from before VS was activated
_inputPropertiesBackup = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoRecord) as VideoEncodingProperties;
// Set the recommendation from the effect (a resolution higher than the current one to allow for cropping) on the input
await _mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoRecord, recommendation.InputProperties);
Debug.WriteLine("VS recommendation for the MediaStreamProperties (input) has been applied");
}
// Handle the recommendations for the output from the effect
if (recommendation.OutputProperties != null)
{
// Back up the current output properties from before VS was activated
_outputPropertiesBackup = _encodingProfile.Video;
// Apply the recommended encoding profile for the output, which will result in a video with the same dimensions as configured
// before VideoStabilization was added if an appropriate padded capture resolution was available. Otherwise, it will be slightly
// smaller (due to cropping). This prevents upscaling back to the original size, which can result in a loss of quality
_encodingProfile.Video = recommendation.OutputProperties;
Debug.WriteLine("VS recommendation for the MediaEncodingProfile (output) has been applied");
}
}
Related
I have been trying to programmatically flip the NLog async switch on and off for a long time. I am getting nowhere. I have code that appears to do what I want, but after making the changes, I get no logging at all. Here's what I've found so far:
When the NLog config file contains async=true, then at runtime, I will have two targets (LogManager.Configuration.AllTargets) for each target in the config file. One is the original target, renamed with "_wrapped" at the end. The other is the original (now renamed) target wrapped in an AsyncTargetWrapper, and given the name of my original target.
Before doing anything (to turn on or off async), I first clear all of the LoggingRules.
To turn off async, I get the wrapped target from the AsyncTargetWrapper, rename it to the original name, remove the wrapper target (which seems to remove the original too), then re-add the original target (which has now been given its original name again). Then I create a LoggingRule and set the appropriate logging levels. Finally, I call LogManager.ReconfigExistingLoggers(). At this point, something is broken, as I will get no logging whatsoever.
I follow a similar process when turning async on, except this time, I rename the original target, create an AsyncTargetWrapper for it, then add the new wrapper target. Again, I create a LoggingRule for the new target and set the logging levels. At this point, something is broken, as I will get no logging whatsoever.
The code is below:
LogManager.Configuration.LoggingRules.Clear();
if (async) // turning async ON
{
foreach (var target in LogManager.Configuration.AllTargets)
{
if (!(target is AsyncTargetWrapper))
{
string targetName = target.Name;
// rename the synchronous target to indicate that it is going to be wrapped by another target (an asynchronous wrapper)
target.Name = $"{target.Name}_wrapped";
// create an asynchronous target wrpper and give it the name of the synchronous target's original (non-wrapped) name
AsyncTargetWrapper asyncTarget = new AsyncTargetWrapper(target) { Name = targetName };
LogManager.Configuration.AddTarget(asyncTarget);
LoggingRule asyncRule = new LoggingRule("*", asyncTarget);
if (_minLevel != null && _maxLevel != null)
{
asyncRule.EnableLoggingForLevels(_minLevel, _maxLevel);
}
else
{
foreach (var level in LogLevel.AllLevels.Except(new List<LogLevel>() { LogLevel.Off }))
{
if (Logger.IsEnabled(level)) asyncRule.EnableLoggingForLevel(level);
}
}
}
}
}
else // turning async OFF
{
foreach (var target in LogManager.Configuration.AllTargets)
{
if (target is AsyncTargetWrapper)
{
// get the wrapped (synchronous) target from the wrapper
Target syncTarget = ((AsyncTargetWrapper)target).WrappedTarget;
// rename the synchronous target (remove "_wrapped" from the end of its name)
syncTarget.Name = target.Name.Replace("_wrapped", "");
// remove the wrapper target
LogManager.Configuration.RemoveTarget(target.Name);
LogManager.Configuration.AddTarget(syncTarget);
// create a rule for the wrapped (synchronous) target
LoggingRule syncRule = new LoggingRule("*", syncTarget);
// set the logging level for the synchronous target
if (_minLevel != null && _maxLevel != null)
{
syncRule.EnableLoggingForLevels(_minLevel, _maxLevel);
}
else
{
foreach (var level in LogLevel.AllLevels.Except(new List<LogLevel>() { LogLevel.Off }))
{
if (Logger.IsEnabled(level)) syncRule.EnableLoggingForLevel(level);
}
}
}
}
}
LogManager.Configuration.Reload();
LogManager.ReconfigExistingLoggers();
I don't think those last two lines are needed, but I tried them because nothing else was working.
There must be a correct way to flip the async flag, but I don't know what it is. Can anyone tell me how to do it?
I found the problem. I had a working implementation in the past, so this was all very confusing for me. There were problems with my old implementation, so I decided to refactor it, but made a mistake in the new implementation. I left out a critical line of code! New logging rules must be added to the configuration after they are created:
LogManager.Configuration.LoggingRules.Add(syncRule);
Now it works exactly as I want it to. Whew!
I'm writing a console app to do some data migration between a legacy system and a new version. Each record has associated images stored on one web server and I'm downloading/altering/uploading each image to Azure (and also recording some data about each image in a database).
Here's a rough outline, in code:
public void MigrateData()
{
var records = GetRecords();
foreach (var record in records)
{
// ...
MigrateImages(record.Id, record.ImageCount);
}
}
public void MigrateImages(int recordId, int imageCount)
{
for (int i = 1; i <= imageCount; i++)
{
var legacyImageData = DownloadImage("the image url");
if (legacyImageData != null && legacyImageData.Length > 0)
{
// discard because we don't need the image id here, but it's used in other workflows
var _ = InsertImage(recordId, legacyImageData);
}
}
}
// This method can be used elsewhere, so the return of int is necessary and cannot be changed
public int InsertImage(int recordId, byte[] imageData)
{
var urls = UploadImage(imageData).Result;
return // method call to save image and return image ID
}
public async Task<(Uri LargeUri, Uri ThumbnailUri)> UploadImage(byte[] imageData)
{
byte[] largeData = ResizeImageToLarge(imageData);
byte[] thumbnailData = ResizeImageToThumbnail(imageData);
var largeUpload = largeBlob.UploadFromByteArrayAsync(largeImage, 0, largeImage.Length);
var thumbUpload = thumbsBlob.UploadFromByteArrayAsync(thumbImage, 0, thumbImage.Length);
await Task.WhenAll(largeUpload, thumbUpload);
var largeUrl = "";// logic to build url
var thumbUrl = "";// logic to build url
return (largeUrl, thumbUrl);
}
I'm using async/await for UploadImage() to allow parallel uploads for the large and thumbnail images, saving time.
My question is (if it's possible/makes sense) how can I utilize async/await for MigrateImages() to do parallel image uploads in order to reduce the overall time it takes for the task to complete? Does the fact that I'm already using async/await within UploadImage() hinder my goal?
It might be obvious due to my question, but await/async is still something I can't fully wrap my head around about how to correct utilize and/or implement it.
The async/await technology is intended for facilitating asynchrony, not concurrency. In your case you want to speed-up the images-uploading process by uploading multiple images concurrently. It is not important if you are wasting some thread-pool threads by blocking them, and you have no UI thread that needs to remain unblocked. You are making a tool that you are going to use once or twice and that's it. So I suggest that you spare yourself the trouble of trying to understand why your app is not behaving the way you expect, and avoid async/await altogether. Stick with the simple and familiar synchronous programming model for this assignment.
Combining synchronous and asynchronous code is dangerous, and even more so if your experience and understanding of async/await is limited. There are many intricacies in this technology. Using the Task.Result property in particular is a red flag. When you become more proficient with async/await code, you are going to treat any use of Result like a an unlocked grenade, ready to explode in your face at any time, and make you look like a fool. When used in apps with synchronization context (Windows Forms, ASP.NET) it can introduce deadlocks so easily that it's not even funny.
Here is how you can achieve the desired concurrency, without having to deal with the complexities of asynchrony:
public (Uri LargeUri, Uri ThumbnailUri) UploadImage(byte[] imageData)
{
byte[] largeData = ResizeImageToLarge(imageData);
byte[] thumbnailData = ResizeImageToThumbnail(imageData);
var largeUpload = largeBlob.UploadFromByteArrayAsync(
largeImage, 0, largeImage.Length);
var thumbUpload = thumbsBlob.UploadFromByteArrayAsync(
thumbImage, 0, thumbImage.Length);
Task.WaitAll(largeUpload, thumbUpload);
var largeUrl = "";// logic to build url
var thumbUrl = "";// logic to build url
return (largeUrl, thumbUrl);
}
I just replaced await Task.WhenAny with Task.WaitAll, and removed the wrapping Task from the method's return value.
When retrieving the VideoProperties of a freshly created MediaCapture file, I am finding that frequently the properties are not set, i.e. all zero. To illustrate I've updated the CameraStarterKit sample in the Windows universal samples zip:
private async Task StopRecordingAsync()
{
Debug.WriteLine("Stopping recording...");
_isRecording = false;
await _mediaCapture.StopRecordAsync();
var videoproperties = await videoFile.Properties.GetVideoPropertiesAsync();
// This Assert will fail most of the time but not always
System.Diagnostics.Debug.Assert(videoproperties.Duration != TimeSpan.Zero);
Debug.WriteLine("Stopped recording!");
}
I'm not receiving any errors, just bad data. Viewing the file properties in File Explorer always shows the expected result. What am I doing wrong?
currently i am working with Awsomnium 1.7 in the C# environment.
I'm just using the Core and trying to define custom post parameters.
Now, i googled a lot and i even posted at the awsomnium forums, but there was no answer.
I understand the concept, but the recent changes just dropped the suggested mechanic and examples.
What i found:
http://support.awesomium.com/kb/general-use/how-do-i-send-form-values-post-data
The problem with this is, that the WebView Class does not contain "OnResourceRequest" Event anymore.
So far, i have implemented the IResourceInterceptor and have the "OnRequest"-Function overwritten
public ResourceResponse OnRequest(ResourceRequest request)
is the signature, but i have no chance to reach in there in order to add request headers.
Anyone here any idea? I tried to look in the documentation, but i didn't find anything on that.....
You need to attach your IResourceInterceptor to WebCore, not WebView. Here's a working example:
Resource interceptor:
public class CustomResourceInterceptor : ResourceInterceptor
{
protected override ResourceResponse OnRequest(ResourceRequest request)
{
request.Method = "POST";
var bytes = "Appending some text to the request";
request.AppendUploadBytes(bytes, (uint) bytes.Length);
request.AppendExtraHeader("custom-header", "this is a custom header");
return null;
}
}
Main application:
public MainWindow()
{
WebCore.Started += WebCoreOnStarted;
InitializeComponent();
}
private void WebCoreOnStarted(object sender, CoreStartEventArgs coreStartEventArgs)
{
var interceptor = new CustomResourceInterceptor();
WebCore.ResourceInterceptor = interceptor;
//webView is a WebControl on my UI, but you should be able to create your own WebView off WebCore
webView.Source = new Uri("http://www.google.com");
}
HotN's answer above is good; in fact, it's what I based my answer on. However, I spent a week searching for this information and putting together something that will work. (The answer above has a couple of issues which, at the very least, make it unworkable with v1.7 of Awesomium.) What I was looking for was something that would work right out of the box.
And here is that solution. It needs improvement, but it suits my needs at the moment. I hope this helps someone else.
// CRI.CustomResourceInterceptor
//
// Author: Garison E Piatt
// Contact: {removed}
// Created: 11/17/14
// Version: 1.0.0
//
// Apparently, when Awesomium was first created, the programmers did not understand that someone would
// eventually want to post data from the application. So they made it incredibly difficult to upload
// POST parameters to the remote web site. We have to jump through hoops to get that done.
//
// This module provides that hoop-jumping in a simple-to-understand fashion. We hope. It overrides
// the current resource interceptor (if any), replacing both the OnRequest and OnFilterNavigation
// methods (we aren't using the latter yet).
//
// It also provides settable parameters. Once this module is attached to the WebCore, it is *always*
// attached; therefore, we can simply change the parameters before posting to the web site.
//
// File uploads are currently unhandled, and, once handled, will probably only upload one file. We
// will deal with that issue later.
//
// To incoroprate this into your application, follow these steps:
// 1. Add this file to your project. You know how to do that.
// 2. Edit your MainWindow.cs file.
// a. At the top, add:
// using CRI;
// b. inside the main class declaration, near the top, add:
// private CustomResourceInterceptor cri;
// c. In the MainWindow method, add:
// WebCore.Started += OnWebCoreOnStarted;
// cri = new CustomResourceInterceptor();
// and (set *before* you set the Source value for the Web Control):
// cri.Enabled = true;
// cri.Parameters = String.Format("login={0}&password={1}", login, pw);
// (Choose your own parameters, but format them like a GET query.)
// d. Add the following method:
// private void OnWebCoreOnStarted(object sender, CoreStartEventArgs coreStartEventArgs) {
// WebCore.ResourceInterceptor = cri;
// }
// 3. Compile your application. It should work.
using System;
using System.Runtime.InteropServices;
using System.Text;
using Awesomium.Core;
using Awesomium.Windows.Controls;
namespace CRI {
//* CustomResourceInterceptor
// This object replaces the standard Resource Interceptor (if any; we still don't know) with something
// that allows posting data to the remote web site. It overrides both the OnRequest and OnFilterNavigation
// methods. Public variables allow for run-time configuration.
public class CustomResourceInterceptor : IResourceInterceptor {
// Since the default interceptor remains overridden for the remainder of the session, we need to disable
// the methods herein unless we are actually using them. Note that both methods are disabled by default.
public bool RequestEnabled = false;
public bool FilterEnabled = false;
// These are the parameters we send to the remote site. They are empty by default; another safeguard
// against sending POST data unnecessarily. Currently, both values allow for only one string. POST
// variables can be combined (by the caller) into one string, but this limits us to only one file
// upload at a time. Someday, we will have to fix that. And make it backward-compatible.
public String Parameters = null;
public String FilePath = null;
/** OnRequest
** This ovverrides the default OnRequest method of the standard resource interceptor. It receives
** the resource request object as a parameter.
**
** It first checks whether or not it is enabled, and returns NULL if not. Next it sees if any
** parameters are defined. If so, it converst them to a byte stream and appends them to the request.
** Currently, files are not handled, but we hope to add that someday.
*/
public ResourceResponse OnRequest(ResourceRequest request) {
// We do nothing at all if we aren't enabled. This is a stopgap that prevents us from sending
// POST data with every request.
if (RequestEnabled == false) return null;
// If the Parameters are defined, convert them to a byte stream and append them to the request.
if (Parameters != null) {
var str = Encoding.Default.GetBytes(Parameters);
var bytes = Encoding.UTF8.GetString(str);
request.AppendUploadBytes(bytes, (uint)bytes.Length);
}
// If either the parameters or file path are defined, this is a POST request. Someday, we'll
// figure out how to get Awesomium to understand Multipart Form data.
if (Parameters != null || FilePath != null) {
request.Method = "POST";
request.AppendExtraHeader("Content-Type", "application/x-www-form-urlencoded"); //"multipart/form-data");
}
// Once the data has been appended to the page request, we need to disable this process. Otherwise,
// it will keep adding the data to every request, including those that come from the web site.
RequestEnabled = false;
Parameters = null;
FilePath = null;
return null;
}
/** OnFilterNavigation
** Not currently used, but needed to keep VisualStudio happy.
*/
public bool OnFilterNavigation(NavigationRequest request) {
return false;
}
}
}
An elegant / performant way to "Touch" a file in (update ModifiedTime) WinRT?
I have some code which needs to delete files that are older than 30 days. This works well, but in some cases, I need to update the time on the file to reset the 30 day window, and prevent deletion. On the basicProperties list, the ModifiedTime is read-only, so I need to find another way to update it...
Method 1: Rename twice
// Ugly, and may have side-effects depending on what's using the file
// Sometimes gives access denied...
public static async Task TouchFileAsync(this StorageFile file)
{
var name = file.Name;
await file.RenameAsync("~" + name).AsTask().ContinueWith(
async (task) => { await file.RenameAsync(name); }
);
}
Method 2: Modify a file property
// Sometimes works, but currently throwing an ArgumentException for
// me, and I have no idea why. Also tried many other properties:
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb760658(v=vs.85).aspx
public static async Task TouchFileAsync(this StorageFile file)
{
var prop = new KeyValuePair<string, object>("System.Comment", DateTime.Now.Ticks.ToString());
await file.Properties.SavePropertiesAsync(new[] { prop });
}
Method 3: Use a Win32 API via P/Invoke?
Not sure if this would work on ARM devices?
Pass certification?
Be performant?
Is there a best way to do this? Code sample?
Anyone got any other ideas? I'm a bit stuck :-)
Many thanks,
Jon
I just had a need for this and here is my solution.
usage
await storageFileToTouch.TouchAsync();
code
public static class StorageFileExtensions
{
/// <summary>
/// Touches a file to update the DateModified property.
/// </summary>
public static async Task TouchAsync(this StorageFile file)
{
using (var touch = await file.OpenTransactedWriteAsync())
{
await touch.CommitAsync();
}
}
}
Assuming you're planning on combing a list of files that exist locally on an RT machine, and not somewhere in that cloud (otherwise we woudln't have to worry about the WinRT doc mod process), You could easily use the Application Data Container provided to each app to store very thin data (key value pairs fit very well).
In this way you would store a future delete date for each file that needed to be persisted, so that the next time it was raised for deletion, before the deletion process occurs, the app checks the App Storage Data. Then you wont need to worry about the permissions of the files you're iterating over, when you're only trying to make sure they don't get deleted from your process.
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
// Create a setting in a container
Windows.Storage.ApplicationDataContainer container =
localSettings.CreateContainer("FilesToPersist", Windows.Storage.ApplicationDataCreateDisposition.Always);
StorageFile file = fileYouWantToPersist;
if (localSettings.Containers.ContainsKey("FilesToPersist"))
{
localSettings.Containers["FilesToPersist"].Values[file.FolderRelativeId] = DateTime.Now.AddDays(30);
}
// Read data from a setting in a container
bool hasContainer = localSettings.Containers.ContainsKey("FilesToPersist");
bool hasSetting = false;
if (hasContainer)
{
hasSetting = localSettings.Containers["FilesToPersist"].Values.ContainsKey(file.FolderRelativeId);
if(hasSettings)
{
string dt = localSettings.Containers["FilesToPersist"].Values[file.FolderRelativeId];
if(Convert.ToDateTime(dt) < DateTime.Now)
{
//Delete the file
}
}
}
Resources:
http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.applicationdata.aspx
http://lunarfrog.com/blog/2011/10/10/winrt-storage-accesscache/