Recording video in C# and WPF from mediaElement - c#

My application applies custom HLSL shader effects to a mediaElement using decorators. How can I record and save the modified video using the application itself in realtime?

I have been using the RenderTargetBitmap object to render image sequences of animations like this:
First you call:
myStoryboard.CurrentTimeInvalidated += new EventHandler(onCurrentTimeInvalidated );
where myStoryboard is the Storyboard driving the animation and then you have the following method:
void onCurrentTimeInvalidated (object sender, EventArgs e)
{
prefix = "";
if (counter < 10)
{
prefix = "000";
}
else if (counter < 100)
{
prefix = "00";
}
else if (counter < 1000)
{
prefix = "0";
}
Size size = new Size(MainCanvas.ActualWidth, MainCanvas.ActualHeight);
MainCanvas.Measure(size);
MainCanvas.Arrange(new Rect(size));
RenderTargetBitmap bmp = new RenderTargetBitmap(imgWidth, imgHeight, 96d, 96d, PixelFormats.Default);
bmp.Render(MainCanvas);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.QualityLevel = 90;
encoder.Frames.Add(BitmapFrame.Create(bmp));
string file = basePath + prefix + counter.ToString() + "_testpic.jpg";
using (Stream stm = File.Create(file))
{
encoder.Save(stm);
}
counter++;
}
I am not sure how well this will work with the MediaElement but it may be worth a try. For this to work on the MediaElement though you need to drive the MediaElement from a MediaTimeline and call the onCurrentTimeInvalidated method from it's CurrentTimeInvalidated event.

Related

How to change rtsp streaming size in c#?

I am using rtsp streaming in c# using below code
However, the code below plays back the original size of the video.
I want to resize the video
How can I increase the size of my video?
video = new VideoCapture();
video.Open(rtspUrl);
using (Mat image = new Mat())
{
while (true)
{
if (!video.Read(image))
{
Cv2.WaitKey();
}
if (!image.Empty())
{
Bitmap bmp = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(image);
pbStream.Image = bmp;
}
if (Cv2.WaitKey(1) >= 0)
break;
if (isRun == false)
{
break;
}
}
}
video = null;

WPF AForge.net resize web cam image to custom size

Hey all I am trying to figure out a way of customizing the size of the webcam image inside an image control.
Currently this is the code that draws the image:
private void video_NewFrame(object sender, Video.NewFrameEventArgs eventArgs)
{
try
{
BitmapImage bi;
ResizeBicubic filter = new ResizeBicubic(400, 300);
using (var bitmap = (Bitmap)eventArgs.Frame.Clone())
{
bi = bitmap.ToBitmapImage();
}
bi.Freeze(); // avoid cross thread operations and prevents leaks
Dispatcher.BeginInvoke(new ThreadStart(delegate { videoPlayer.Source = bi; }));
}
catch (Exception exc)
{
MessageBox.Show("Error on _videoSource_NewFrame:\n" + exc.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
StopCamera();
}
}
As you see I am trying to use ResizeBicubic filter = new ResizeBicubic(400, 300); to resize it but I can not seem to find anywhere to put the filter.Apply().
If I do something like this:
filter.Apply(bi);
It gives me the error of:
Argument 1: cannot convert from 'System.Windows.Media.Imaging.BitmapImage' to 'System.Drawing.Bitmap'
I also have code that finds the largest video res that particular web cam can do:
private void StartCamera()
{
if (CurrentDevice != null)
{
_videoSource = new VideoCaptureDevice(CurrentDevice.MonikerString);
if (_videoSource.VideoCapabilities.Length > 0)
{
string highestSolution = "0;0";
//Search for the highest resolution
for (int i = 0; i < _videoSource.VideoCapabilities.Length; i++)
{
if (_videoSource.VideoCapabilities[i].FrameSize.Width > Convert.ToInt32(highestSolution.Split(';')[0]))
highestSolution = _videoSource.VideoCapabilities[i].FrameSize.Width.ToString() + ";" + i.ToString();
}
_videoSource.VideoResolution = _videoSource.VideoCapabilities[Convert.ToInt32(highestSolution.Split(';')[1])];
}
_videoSource.NewFrame += video_NewFrame;
_videoSource.Start();
}
}
And that code does work to set the image to the largest size on the screen but like I said, I want to customize that size to whatever the actual image control is sized to.
Any help would be great!

win2D uwp won't save a highlight stroke in an inkcanvas

I'm desperately trying to save a highlight stroke as a png. I'm using win2d for UWP to make this work.
It works well for strokes with 100% opacity, but when I set DrawAsHighlighter = true;, the saved png is empty, fully transparent.
Here's my code :
private void SetHighLight()
{
InkDrawingAttributes attributes = new InkDrawingAttributes();
attributes.DrawAsHighlighter = true;
attributes.PenTip = PenTipShape.Rectangle;
attributes.Size = new Size(4, 10);
attributes.Color = currentColor;
SetAttribute(attributes);
}
private void GetCanvasRender(out CanvasRenderTarget renderTarget)
{
CanvasDevice device = CanvasDevice.GetSharedDevice();
renderTarget = new CanvasRenderTarget(device, (int)ink.ActualWidth, (int)ink.ActualHeight, 96);
using (var ds = renderTarget.CreateDrawingSession())
{
ds.Clear(Colors.Transparent); //I already tried to delete this but it doesn't change anything
ds.DrawInk(ink.InkPresenter.StrokeContainer.GetStrokes());
}
}
private async void SavePicture()
{
CanvasRenderTarget renderTarget;
Image img = new Image();
GetCanvasRender(out renderTarget);
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile noteFile = await storageFolder.CreateFileAsync(i.ToString() + ".png", CreationCollisionOption.ReplaceExisting);
using (var fileStream = await noteFile.OpenAsync(FileAccessMode.ReadWrite))
await renderTarget.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);
img.Source = new BitmapImage(new Uri(storageFolder.Path + "/" + i++ + ".png"));
img.VerticalAlignment = VerticalAlignment.Stretch;
ContainerCanvas.Children.Add(img);
Canvas.SetTop(img, ScrollViewerContainer.VerticalOffset);
Canvas.SetZIndex(img, 5);
}
I've read it might be because of the highlight not being present in the visual tree but I'm really not sure about it.
By the way when I try to change the opacity of a color(attributes.Color = Color.FromArgb(128, 255, 0, 0)), the inkcanvas doesn't apply the alpha, why ? Am I missing something ?
You can't save DrawAsHighlighter ink to a bitmap format like .png - that's just fundamentally not a meaningful operation to be attempting.
Regular non-highlighter ink is drawn using standard alpha blending, so it is reasonable to write just these ink shapes into a bitmap format. You can later blend that bitmap over some other background image, and get the same result as if the ink had been drawn directly over that background.
For highlighter ink, however, the "blend over background" is a more complex operation, not just standard sourceover blending. So there is no such thing as a bitmap image that contains just this ink - you also have to provide a background in order for the appropriate blend to be carried out.
You have three options here:
Don't use highligher ink mode.
Instead of saving out your ink as a bitmap image, save the original ink stroke data, and use inking APIs to later blend these strokes directly to their final location.
Include the background as well as the ink strokes in the same bitmap.
Try clearing the background of the canvas with:
ds.Clear(Colors.White);
The highlighter isn't visible on transparent backgrounds as it seems to multiply its value with the background color.
I finally figured out how to make it work.
I simply added a new layer before calling DrawInk and gave it an opacity, and got rid of the attributes.DrawAsHighlighter = true;. Instead, I've made 1 inkCanvas with 0.5 opacity specially for the highlighter, looking like you're using a highlighter.
Here's the code :
private void SetHighLight()
{
InkDrawingAttributes attributes = new InkDrawingAttributes();
attributes.PenTip = PenTipShape.Rectangle;
attributes.Size = new Size(4, 10);
attributes.Color = currentColor;
SetAttribute(attributes);
}
private void GetCanvasRender(out CanvasRenderTarget renderTarget, float opacity)
{
CanvasDevice device = CanvasDevice.GetSharedDevice();
renderTarget = new CanvasRenderTarget(device, (int)ink.ActualWidth, (int)ink.ActualHeight, 96);
using (var ds = renderTarget.CreateDrawingSession())
{
ds.Clear(Colors.Transparent);
using (ds.CreateLayer(opacity))
{
ds.DrawInk(ink.InkPresenter.StrokeContainer.GetStrokes());
}
}
}
private async void SavePicture(float opacity)
{
CanvasRenderTarget renderTarget;
Image img = new Image();
GetCanvasRender(out renderTarget, opacity);
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile noteFile = await storageFolder.CreateFileAsync(i.ToString() + ".png", CreationCollisionOption.ReplaceExisting);
using (var fileStream = await noteFile.OpenAsync(FileAccessMode.ReadWrite))
await renderTarget.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);
img.Source = new BitmapImage(new Uri(storageFolder.Path + "/" + i++ + ".png"));
img.VerticalAlignment = VerticalAlignment.Stretch;
ContainerCanvas.Children.Add(img);
Canvas.SetTop(img, ScrollViewerContainer.VerticalOffset);
Canvas.SetZIndex(img, 5);
}

How to capture entire page screenshot with Selenium and C#

I am using the following method to take screenshots and noticed that all of the screenshot images are not capturing the full window but instead cropping the window. For example I will get a .jpg where I cannot see the entire webpage that is visible on the screen. I suspect that this happens when certain elements are not visible in the DOM and therefore not included in the screenshot. Is this expected behavior? If this is expected, is there a way to program the driver to take a full screen capture with the Selenium 32 bit Internet Explorer Driver consistently? Here is the method I am calling to take the screenshots.
public static void TakeScreenshot(IWebDriver driver, string saveLocation)
{
ITakesScreenshot ssdriver = driver as ITakesScreenshot;
Screenshot screenshot = ssdriver.GetScreenshot();
screenshot.SaveAsFile(saveLocation, ImageFormat.png);
}
Indeed, Richard's answer will work to take a screenshot of the entire current desktop area - if that is what you are after, it will work. If you are actually after having a specific application (e.g. Internet Explorer) that is not 'maximized' while using Selenium, then you might need to take a different approach. Consider forcing the application to be maximized and maybe even have it get focus before taking the screenshot using Richard's method above - or use the Selenium's ITakesScreenshot interface ...
In order to use the System.Windows.Forms namespace in a Console application, you will need to "Add Reference..." to the project. In the Solution Explorer, right-click on "References" and select "Add Reference..."; scroll to System.Windows.Forms, check it and click Okay. After doing that, you will be able to type "using System.Windows.Forms;" at the top of your class file.
This is what I use for capturing the entire screen:
Rectangle bounds = Screen.GetBounds(Point.Empty);
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
}
bitmap.Save(saveLocation, System.Drawing.Imaging.ImageFormat.Png);
}
the resolution:
Detect if the web page can be display on current browser
If current window screenshot is enough then use screenshot of current browser window
If the current page include scroll bar, then take screen shot of each window by scroll the page accordingly, and cut the screenshot to combine all screenshot together
Put code for whole page screenshot:
using System.Linq;
using System.Threading;
using OpenQA.Selenium;
using DotRez.Utilities.WebDriver;
using System.Drawing;
using System.Drawing.Imaging;
// decide take full screenshot and take current window screenshot according to the height
public static void TakeSnapshot()
{
IJavaScriptExecutor js = Driver as IJavaScriptExecutor;
var fileName = ScenarioContext.Current.ScenarioInfo.Title.ToIdentifier() + DateTime.Now.ToString("HH_mm_ss") + "JS" + ".png";
var fileLocation = Path.Combine(Configuration.SCREEN_SHOT_LOCATION, fileName);
Image finalImage;
// get the full page height and current browser height
string getCurrentBrowserSizeJS =
#"
window.browserHeight = (window.innerHeight || document.body.clientHeight);
window.headerHeight= document.getElementById('site-header').clientHeight;;
window.fullPageHeight = document.body.scrollHeight;
";
js.ExecuteScript(getCurrentBrowserSizeJS);
// * This is async operation. So we have to wait until it is done.
string getSizeHeightJS = #"return window.browserHeight;";
int contentHeight = 0;
while (contentHeight == 0)
{
contentHeight = Convert.ToInt32(js.ExecuteScript(getSizeHeightJS));
if (contentHeight == 0) System.Threading.Thread.Sleep(10);
}
string getContentHeightJS = #"return window.headerHeight;";
int siteHeaderHeight = 0;
while (siteHeaderHeight == 0)
{
siteHeaderHeight = Convert.ToInt32(js.ExecuteScript(getContentHeightJS));
if (siteHeaderHeight == 0) System.Threading.Thread.Sleep(10);
}
string getFullPageHeightJS = #"return window.fullPageHeight";
int fullPageHeight = 0;
while (fullPageHeight == 0)
{
fullPageHeight = Convert.ToInt32(js.ExecuteScript(getFullPageHeightJS));
if (fullPageHeight == 0) System.Threading.Thread.Sleep(10);
}
if (contentHeight == fullPageHeight)
{
TakeSnapshotCurrentPage();
}
else
{
int scollEachHeight = contentHeight - siteHeaderHeight;
int shadowAndBorder = 3;
int scollCount = 0;
int existsIf = (fullPageHeight - siteHeaderHeight) % scollEachHeight;
bool cutIf = true;
if (existsIf == 0)
{
scollCount = (fullPageHeight - siteHeaderHeight) / scollEachHeight;
cutIf = false;
}
else
{
scollCount = (fullPageHeight - siteHeaderHeight) / scollEachHeight + 1;
cutIf = true;
}
// back to top start screenshot
string scollToTopJS = "window.scrollTo(0, 0)";
js.ExecuteScript(scollToTopJS);
Byte[] imageBaseContent = ((ITakesScreenshot)Driver).GetScreenshot().AsByteArray;
Image imageBase;
using (var ms = new MemoryStream(imageBaseContent))
{
imageBase = Image.FromStream(ms);
}
finalImage = imageBase;
string scrollBar = #"window.scrollBy(0, window.browserHeight-window.headerHeight);";
for (int count = 1; count < scollCount; count++)
{
js.ExecuteScript(scrollBar);
Thread.Sleep(500);
Byte[] imageContentAdd = ((ITakesScreenshot)Driver).GetScreenshot().AsByteArray;
Image imageAdd;
using (var msAdd = new MemoryStream(imageContentAdd))
{
imageAdd = Image.FromStream(msAdd);
}
imageAdd.Save(fileLocation, ImageFormat.Png);
Bitmap source = new Bitmap(imageAdd);
int a = imageAdd.Width;
int b = imageAdd.Height - siteHeaderHeight;
PixelFormat c = source.PixelFormat;
// cut the last screen shot if last screesshot override with sencond last one
if ((count == (scollCount - 1)) && cutIf)
{
Bitmap imageAddLastCut =
source.Clone(new System.Drawing.Rectangle(0, contentHeight - existsIf, imageAdd.Width, existsIf), source.PixelFormat);
finalImage = combineImages(finalImage, imageAddLastCut);
source.Dispose();
imageAddLastCut.Dispose();
}
//cut the site header from screenshot
else
{
Bitmap imageAddCutHeader =
source.Clone(new System.Drawing.Rectangle(0, (siteHeaderHeight + shadowAndBorder), imageAdd.Width, (imageAdd.Height - siteHeaderHeight - shadowAndBorder)), source.PixelFormat);
finalImage = combineImages(finalImage, imageAddCutHeader);
source.Dispose();
imageAddCutHeader.Dispose();
}
imageAdd.Dispose();
}
finalImage.Save(fileLocation, ImageFormat.Png);
imageBase.Dispose();
finalImage.Dispose();
}
}
//combine two pictures
public static Bitmap combineImages(Image image1, Image image2)
{
Bitmap bitmap = new Bitmap(image1.Width, image1.Height + image2.Height);
using (Graphics g = Graphics.FromImage(bitmap))
{
g.DrawImage(image1, 0, 0);
g.DrawImage(image2, 0, image1.Height);
}
return bitmap;
}
// take current window screenshot
private static void TakeSnapshotCurrentPage()
{
try
{
var screenshot = ((ITakesScreenshot)Driver).GetScreenshot();
var urlStr = Driver.Url;
var fileName = ScenarioContext.Current.ScenarioInfo.Title.ToIdentifier() + DateTime.Now.ToString("HH_mm_ss") + ".png";
var fileLocation = Path.Combine(Configuration.SCREEN_SHOT_LOCATION, fileName);
if (!Directory.Exists(Configuration.SCREEN_SHOT_LOCATION))
{
Directory.CreateDirectory(Configuration.SCREEN_SHOT_LOCATION);
}
screenshot.SaveAsFile(fileLocation, ScreenshotImageFormat.Png);
Console.WriteLine(urlStr);
Console.WriteLine("SCREENSHOT[{0}]SCREENSHOT", Path.Combine("Screenshots", fileName));
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}

NGif Animator Background Problems

I've been playing around with NGif Animator for resizing animated gifs and it does resize but parts in many animated gifs I've tried get erased. I looked through the comments on that page and didn't see anyone else mention it.
To eliminate resizing as the cause I simply loop through the frames and save each one. Each frame is a System.Drawing.Image. Transparency is set to none (Color.Empty).
This is my test method currently:
GifDecoder gifDecoder = new GifDecoder();
MemoryStream memoryStream = new MemoryStream();
new BinaryWriter((Stream)memoryStream).Write(imageToResize); // byte array
memoryStream.Position = 0L;
gifDecoder.Read((Stream)memoryStream);
memoryStream.Dispose();
string filename = Guid.NewGuid().ToString().Replace("-", String.Empty) + ".gif";
string output = path + #"\" + filename;
AnimatedGifEncoder animatedGifEncoder = new AnimatedGifEncoder();
animatedGifEncoder.Start(output);
animatedGifEncoder.SetRepeat(gifDecoder.GetLoopCount());
animatedGifEncoder.SetQuality(10); // They say 20 is max quality will get, I've tried higher. Makes it a little bit better but black areas instead of gray. 10 is their default.
animatedGifEncoder.SetTransparent(Color.Empty); // This is default either way
int frameCount = gifDecoder.GetFrameCount();
int num = 0;
Image frame;
Image image = null;
for (int index = frameCount; num < index; ++num)
{
frame = gifDecoder.GetFrame(num);
animatedGifEncoder.SetDelay(gifDecoder.GetDelay(num));
string fname = #"C:\Development\images\frame_" + num.ToString() + ".gif";
if (File.Exists(fname)) { File.Delete(fname); }
frame.Save(fname);
animatedGifEncoder.AddFrame(image);
}
animatedGifEncoder.Finish();
Here's an example of what's happening:
The background is gone and it's gray.
It's supposed to look like:
Anyone have experience with NGif and know what would cause this? The first frame is always fine. It's the others after that have a problem so I'm guessing something isn't being reset from frame to frame (or re-read). I've been adding more things to their reset frame method but so far it hasn't helped. That now looks like:
protected void ResetFrame()
{
lastDispose = dispose;
lastRect = new Rectangle(ix, iy, iw, ih);
lastImage = image;
lastBgColor = bgColor;
delay = 0;
transparency = false; // I don't want transparency
lct = null;
act = null;
transIndex = -1;
}
There is actually a bug in their code, a byte array not being reset. Check the comments on their page for a solution

Categories

Resources