October 2009 Entries

Near complete wiring harness

near complete wiring harness

Remember, this use to look like this:

Final wiring for v3 drunktender hardware

Quick mounting to show off look / feel

mounted on acrylic

I have to say, I’m rather happy how these turned out.  This will be mounted between the two legs protected by yet another sheet of acrylic for PDC.  I’m using hex head screws also to mount these since they just look so dang nice.  I had TAP plastics in Seattle do the holes and bends for me.  Chances are there is a plastic place near you that can do this for you.  With the bends and holes, it cost about $40 and they did two of them for me in an hour on a Saturday.  The blue plastic is just protective as I still need to make a few extra holes in them.

Back plane and inductor PCBs soldered up

Small and reduces the rat’s nest of wiring by creating these little puppies.  Everything has a little LED on it to show when it is on too.

back plane PCB
load induction suppression PCB load induction suppression PCB

drinktendr progress

If there is one thing I love, it is getting stuff done.  Here are all the parts for Drinktendr v3.5 and the new PCBs I had created to help aid in wire management.  One is just pure wire management (bottom left) and the other is to help aid in the load induction (bottom right).  The load induction suppressor PCB is designed to directly hook into the valve.

PDC bits  Drinktendr PCBs
Drinktendr PCBs Drinktendr PCBs

Getting a database to attach from a My Document folder

Ever want to mount a database from your My Document Folder?  I do … far too often.  In this instance, my SQL Server database MDF file is in: C:\Users\crutkas\Documents\Visual Studio 2008\Projects\Bartender. 

Going into SQL Server Management Studio, and attaching a database will give you this issue.  As you can see, I can’t go in and use anything there.

image 

So what the issue is I have to give SQL Server’s user that is running under access to that folder!  You can do this a few different ways.  Give access to the SQL Server user to read the entire directory tree for your user (c:\Users\[user]\) or just that folder.

So how do you get the user that SQL Server is running under?  Use Computer Management to figure that out and go to the Services module.

image

image

For me, I’m running it under “Network Service”

Now I go back into either c:\Users\[user]\ or the target folder and get that user access.

image

If you did just the target folder, you’ll have to put in the entire file path in the file name of the Attach Database prompt, if you did the root, you now should be able to see everything via the folder tree!

Final tweaks to improve Thumbnailing and now on CodePlex!

In the prior two posts, I talked about creating and fixing the logic for creating video thumbnails / screenshots but it had a few flaws in it.

The first was I didn’t realize I had to close the MediaPlayer object’s stream, this caused memory to balloon upward after multiple plays.  Another problem was opening the same file for the same screenshots could cause issues in the long run at the same time.  To solve these issues, one was an easy fix, the other required a bit of threading knowledge.

To correct this, I’ll first have a Dictionary object to pass in multiple TimeSpans so I can do multiple captures without having the file open multiple times.  I also created used a Mutex that is tied to the video’s Uri.  This will lock the thread until the other processing has been completed.  This will help reduce the memory load footprint in a threaded environment.  You still can get in trouble by opening up some very large video files in a threaded environment

To get access to the tester app, head over to codeplex and source code can be found there too!

public static void CaptureScreen(Uri source, 
	Dictionary<TimeSpan, object> captureList, 
	double scale, CaptureWorkerDelegate finalWorkerPrimary, 
	CaptureWorkerDelegate finalWorkerThumbnail)
{
	var mutexLock = new Mutex(false, source.GetHashCode().ToString());
	mutexLock.WaitOne();
	
	var player = new MediaPlayer { Volume = 0, ScrubbingEnabled = true };

	player.Open(source);
	player.Pause();
	foreach (var pair in captureList)
	{
		var timeSpan = pair.Key;
		var state = pair.Value;

		player.Position = timeSpan;
		Thread.Sleep(1000);

		var width = player.NaturalVideoWidth;
		var height = player.NaturalVideoHeight;

		var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
		var dv = new DrawingVisual();

		using (DrawingContext dc = dv.RenderOpen())
			dc.DrawVideo(player, new Rect(0, 0, width, height));

		rtb.Render(dv);
		var frame = BitmapFrame.Create(rtb).GetCurrentValueAsFrozen();
		if (finalWorkerPrimary != null)
			finalWorkerPrimary(frame as BitmapFrame, state);

		if (scale > 0 && finalWorkerThumbnail != null)
		{
			var thumbnailFrame =
				BitmapFrame.Create(new TransformedBitmap(frame as BitmapSource, new ScaleTransform(scale, scale))).
					GetCurrentValueAsFrozen();
			var encoder = new JpegBitmapEncoder();
			encoder.Frames.Add(thumbnailFrame as BitmapFrame);

			finalWorkerThumbnail(thumbnailFrame as BitmapFrame, state);
		}
	}
	player.Close();
	mutexLock.ReleaseMutex();
}

Improving Thumbnailing Code

From the prior post about getting thumbnails from a video in .Net, it was just prototype code and wasn’t properly abstracted.  Now it is time to fix it.  We’ll create a class called VideoScreenShot.  This class will function in both an asynchronous and synchronous mode.  This still could be improved by queuing up work but this is a nice refactoring.

To get access to the tester app, head over to codeplex and source code can be found there too!

public delegate void CaptureWorkerDelegate(BitmapFrame frame, object state);
public static void CaptureScreenAsync(Uri source, TimeSpan timeSpan, object state, 
	CaptureWorkerDelegate finalWorkerPrimary)
{
	CaptureScreenAsync(source, timeSpan, -1, 
		state, finalWorkerPrimary, null);
}

public static void CaptureScreenAsync(Uri source, TimeSpan timeSpan, double scale, object state, 
	CaptureWorkerDelegate finalWorkerPrimary, CaptureWorkerDelegate finalWorkerThumbnail)
{
	ThreadPool.QueueUserWorkItem(
		delegate { CaptureScreen(source, timeSpan, scale, 
			state, finalWorkerPrimary, finalWorkerThumbnail); });
}

public static void CaptureScreen(Uri source, TimeSpan timeSpan, object state, 
	CaptureWorkerDelegate finalWorkerPrimary)
{
	CaptureScreen(source, timeSpan, -1, state, 
		finalWorkerPrimary, null);
}

public static void CaptureScreen(Uri source, TimeSpan timeSpan, double scale, object state, 
	CaptureWorkerDelegate finalWorkerPrimary, CaptureWorkerDelegate finalWorkerThumbnail)
{
	var player = new MediaPlayer { Volume = 0, ScrubbingEnabled = true };

	player.Open(source);
	player.Pause();
	player.Position = timeSpan;
	Thread.Sleep(1000);

	var width = player.NaturalVideoWidth;
	var height = player.NaturalVideoHeight;

	var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
	var dv = new DrawingVisual();

	using (DrawingContext dc = dv.RenderOpen())
		dc.DrawVideo(player, new Rect(0, 0, width, height));

	rtb.Render(dv);
	var frame = BitmapFrame.Create(rtb).GetCurrentValueAsFrozen();
	if (finalWorkerPrimary != null)
		finalWorkerPrimary(frame as BitmapFrame, state);

	if (scale > 0 && finalWorkerThumbnail != null)
	{
		var thumbnailFrame = BitmapFrame.Create(
			new TransformedBitmap(frame as BitmapSource, 
				new ScaleTransform(scale, scale))).GetCurrentValueAsFrozen();
		var encoder = new JpegBitmapEncoder();
		encoder.Frames.Add(thumbnailFrame as BitmapFrame);

		finalWorkerThumbnail(thumbnailFrame as BitmapFrame, state);
	}
	
	player.Close();
}

In the form’s code behind, here is my code.  This is just a sample of what can be used.

private delegate void setImageDelegate(string controlName, BitmapFrame frame);
private void setImage(string controlName, BitmapFrame frame)
{
	var control = FindName(controlName);
	if (control != null)
		((Image)control).Source = frame;
}

private void makeThumbnails(BitmapFrame frame, object state)
{
	Dispatcher.Invoke(new setImageDelegate(setImage), (string)state, frame);
}

private void makeJpeg(BitmapFrame frame, object state)
{
	var encoder = new JpegBitmapEncoder();
	encoder.Frames.Add(frame);

	string filename = (string)state + ".jpg";
	using (var fs = new FileStream(filename, FileMode.Create))
		encoder.Save(fs);
}

private void Button_Click(object sender, RoutedEventArgs e)
{
	var source = video.Source;

	VideoScreenShot.CaptureScreenAsync(source, TimeSpan.FromSeconds(10), .1, 
		"image0", makeJpeg, makeThumbnails);
	VideoScreenShot.CaptureScreenAsync(source, TimeSpan.FromSeconds(43) + TimeSpan.FromMilliseconds(760), 
		"image1", makeThumbnails);
}

Now it isn’t so much cleaner?

Getting Thumbnails in WPF … on a non-UI thread

imageOn my internal application for Channel 9, I have to create thumbnails at certain time codes in a movie.  After a giant headache attempting to track down the proper way of doing this, I figured out how to do this even if for a few hours I thought 127 seconds was the same at 1 minute and 27 seconds.  That caused some testing headaches as my test clip is 1:40 long.

In this example I’ll write out the code for a threaded video thumbnail creating tool in WPF (XAML) and c#.

To get access to the tester app, head over to codeplex and source code can be found there too!

First, here is my XAML for my control.  Basic but shows the point.

<StackPanel>
	<Button Click="Button_Click">
		Capture
	</Button>
	<Border BorderThickness="2" BorderBrush="Cyan">
			<MediaElement Name="video" 
				Source="media\theOffice.wmv" 
				LoadedBehavior="Pause" 
				ScrubbingEnabled="True" 
				Visibility="Collapsed"/>
		</Border>
	<StackPanel Orientation="Horizontal">
		<Border BorderThickness="2" BorderBrush="Red">
			<Image Name="image0" />
		</Border>
		<Border BorderThickness="2" BorderBrush="Green">
			<Image Name="image1" />
		</Border>
	</StackPanel>
	<StackPanel Orientation="Horizontal">
		<Border BorderThickness="2" BorderBrush="Blue">
			<Image Name="image2" />
		</Border>
		<Border BorderThickness="2" BorderBrush="Yellow">
			<Image Name="image3" />
		</Border>
	</StackPanel>
</StackPanel>

The Click event on the button will be where we get the width, height and source of the video.  I’m creating 4 images to show it off the fact it is threaded.

private void Button_Click(object sender, RoutedEventArgs e)
{
	var source = video.Source;

	setScreenCapture(image0.Name, TimeSpan.FromSeconds(10), source);
	setScreenCapture(image1.Name, TimeSpan.FromSeconds(43) + TimeSpan.FromMilliseconds(760), source);
	setScreenCapture(image2.Name, TimeSpan.FromSeconds(60), source);
	setScreenCapture(image3.Name, TimeSpan.FromSeconds(80), source);
}

 

 

We’ll be using a ThreadPool to accomplish the processing due to a need for a Thread.Sleep in the function following this one.  This will make things a bit more complicated but not locking the UI is worth the extra pain.

private void setScreenCapture(string controlName, 
	TimeSpan timeSpan, Uri source)
{
	ThreadPool.QueueUserWorkItem(delegate
	{ 
		setScreenCaptureWorker(controlName, 
			timeSpan, source); 
	});
}

Now that we have our queuing function done, on to the worker. 

 

private void setScreenCaptureWorker(string controlName, 
	TimeSpan timeSpan, Uri source)
{ /* code */ }

 

We’ll be using a MediaPlayer, RenderTargetBitmap, DrawingVisual, and DrawingContext to accomplish the thumbnail creation.

var player = new MediaPlayer {Volume = 0, ScrubbingEnabled = true};

player.Open(source);
player.Pause();
player.Position = timeSpan;
Thread.Sleep(1000);

var width = player.NaturalVideoWidth;
var height = player.NaturalVideoHeight;

var rtb = new RenderTargetBitmap(
	width, height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();

using(DrawingContext dc = dv.RenderOpen())
	dc.DrawVideo(player, new Rect(0, 0, width, height));

rtb.Render(dv);
var frame = BitmapFrame.Create(rtb).GetCurrentValueAsFrozen();
// you now have a thumbnail frame!

The MediaPlayer needs a moment to get stuff going.  Adding in the Thread.Sleep allows this to get synced up.  The key thing to remember here is we are no longer on the UI thread.  The user interface won’t lock now.  But due to that, we have elements on non-UI threads.  The RenderTargetBitmap and even the BitmapFrame created are both tied to the current thread.  Passing these back to the UI will cause thread context issues.  Using a Freezable object solves the context issue.

In a perfect world, I’d have a different function do this and pass it in as a delegate but this was example code.  Due to the sample codeness of it, I’m creating the image in the function and then calling the dispatcher.  You’ll want to replace this code with whatever your goal is, however this code does both tasks I could see someone using this for.

 

var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(frame as BitmapFrame);

string filename = controlName + ".jpg";
using (var fs = new FileStream(filename, FileMode.Create))
	encoder.Save(fs);

Dispatcher.Invoke(
	new setImageDelegate(setImage), controlName, frame); 

 

And now to update the UI elements.  Since we used Dispatcher.Invoke to call the delegate, we can do this since that forces us back on the User Interface Thread!

 

private delegate void setImageDelegate(string controlName, BitmapFrame frame);
private void setImage(string controlName, BitmapFrame frame)
{
	var control = FindName(controlName);
	if (control != null)
		((Image)control).Source = frame;
}

Enjoy!