Live
To run the application you need to install the Silverlight 4 runtime. At this time there are only beta developer runtimes for Windows and Mac available.
I recommend the non-embedded version of the application.
The Webcam capturing could be started and stopped with the "Start Capture" Button. If you press it for the first time you need to give your permission for the capturing. This application uses the default Silverlight capture devices. You can specify the video and audio devices that are used by default with the Silverlight Configuration. Just press the right mouse button over the application, click "Silverlight" in the context menu and select the new "Webcam / Mic" tab to set them.
Press the "Save Snapshot" Button to take a snapshot and save it to a JPEG file on your harddisk.
The threshold of the edge detection can be changed using the Slider and the "Bypass" Checkbox allows you to disable the shader.
How it works
The base Silverlight 4 webcam usage code was covered in my last blog post.
The new eventhandler for the "Save Snapshot" button:
private void BtnSnapshot_Click(object sender, RoutedEventArgs e)
{
if (saveFileDlg.ShowDialog().Value)
{
using (Stream dstStream = saveFileDlg.OpenFile())
{
SaveSnapshot(dstStream);
}
}
}The code is pretty obvious: A SaveFileDialog is shown and if the user enters a file name and hits OK, a stream to the file will be opened and passed to the SaveSnapshot method. There's only one think to keep in mind when using the SaveFileDialog.ShowDialog() method, it can only be called from user-initiated code like an event handler, otherwise a SecurityException is thrown.The SaveSnapshot method including comments:
// Render Rectangle manually into WriteableBitmap
WriteableBitmap bmp = new WriteableBitmap(ViewportHost, null);
// Init buffer in FluxJpeg format
int w = bmp.PixelWidth;
int h = bmp.PixelHeight;
int[] p = bmp.Pixels;
byte[][,] pixelsForJpeg = new byte[3][,]; // RGB colors
pixelsForJpeg[0] = new byte[w, h];
pixelsForJpeg[1] = new byte[w, h];
pixelsForJpeg[2] = new byte[w, h];
// Copy WriteableBitmap data into buffer for FluxJpeg
int i = 0;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
int color = p[i++];
pixelsForJpeg[0][x, y] = (byte)(color >> 16); // R
pixelsForJpeg[1][x, y] = (byte)(color >> 8); // G
pixelsForJpeg[2][x, y] = (byte)(color); // B
}
}
//Encode Image as JPEG
var colModel = new ColorModel { colorspace = ColorSpace.RGB };
var jpegImage = new FluxJpeg.Core.Image(colModel, pixelsForJpeg);
var encoder = new JpegEncoder(jpegImage, 95, dstStream);
encoder.Encode();The Rectangle's surrounding Grid "ViewportHost" is rendered into a WriteableBitmap and the WriteableBitmap's Pixels are copied into another buffer with a different format. The rendered image is then written as a JPEG encoded stream using the open source FJCore library which is distributed under the MIT License. I've found some code at Stackoverflow on how to use the library in combination with the WriteableBitmap, but I modified / shortened it. See my post on how to Convert, Encode And Decode Silverlight WriteableBitmap Data if you are interested in more WriteableBitmap conversion and JPEG encoding code.
Why not CaptureSource.AsyncCaptureImage?
You might be wondering why I haven't used the Silverlight 4 webcam API's built-in AsyncCaptureImage snapshot method of the CaptureSource class.
- The AsyncCaptureImage method grabs a frame directly from the device and therefore any effects like the edge detection pixel shader won't be visible in the rendered image.
- It only works if the CaptureSource was started, so the user can't save the visible image when the capturing was stopped. See the MSDN for details.
Where to go from here
The code in the SaveSnapshot method could be optimized and also multithreaded. The rendering should run in it's own thread. The encoding in a separate thread too and the network transport also. This approach would utilize modern quad core CPUs. After that it might be a good starting point for a video chat / conferencing application that continuously renders JPEGs and transports them between Silverlight clients. This technique is similar to the M-JPEG video format that also uses separately compressed JPEGs.
To make this idea usable for a Silverlight video chat, only the rendering, the JPEG encoder and the transfer method need to be fast enough for real time streaming.
Please leave a comment what you think about it.
Source code
Download the Visual Studio 2010 solution here.











13 Kommentare:
Great post - I am waiting to build a video-chat app using SL4 (with the two clients connected directly with each other for better performance)
Will have to wait for now, I suppose!
Thanks Sriram.
I don't have any experience with Silverlight's networking APIs and I don't know if SL4 OOB supports a peer to peer communication, but it should be possible using a server, although this is really hard work. Especially the audio and video sync is not that easy.
Hi Rene,
Great post - A beginner question: Any idea how to convert the writeablebitmap to a byte array in order to save the snapshot on a sql server with wcf ria ? Do I need to convert it to jpeg ?
Thanks for your help.
@marcb: Please see my blog post about it :)
"Convert, Encode And Decode Silverlight WriteableBitmap Data"
http://kodierer.blogspot.com/2009/11/convert-encode-and-decode-silverlight.html
Perfect Rene, many thanks.
there's a bug in your demo, if you change the videohost width/height to 400px/300px or any value as long as width is different to height.
then try to save a snapshot.
data in pixelsForJpeg not correct. :)
I cannot compile your Solution, here is the error from Visual Studio 2010:
Error 1 The "ValidateXaml" task failed unexpectedly.
System.IO.FileLoadException: Could not load file or assembly 'file:///C:\Users\VG\Downloads\EdgeCamShots\EdgeCamShots\3rdParty\FJCore\FJ.Core.dll' or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)
File name: 'file:///C:\Users\VG\Downloads\EdgeCamShots\EdgeCamShots\3rdParty\FJCore\FJ.Core.dll' ---> System.NotSupportedException: An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more information.
@Victor
Interesting and thanks for letting me know. Any ideas?
They only provide the source of the FJCore lib at
http://code.google.com/p/fjcore/
So I compiled my own version of FJCore. Although I was able to compile it on two completely different machines, you might want to try it with your own FJCore build.
This demo gives the same E_FAIL HRESULT whilst attempting to initialize the webcam when running on Snow Leopard and SL4 beta.
@Victor - The reason you get that error is usually because the downloaded file needs to be unblocked first. Go to the dll in windows explorer, right click it and unblock it. This has happened to me a few times.
The error has happened to me as well when I download a zip file.. had to do the same things as John Papa to the zip file to resolve the issue.
Thanks for the helpful information John!
@roger
You are right, there was an obvious bug in my JPEG encoding code. I fixed it. :)
Post a Comment