Silverlight之摄像头麦克风使用

摘要:silverlight从4.0开始支持摄像头和麦克风,可以说这一个功能的加入弥补了silverlight媒体方面的不足,毕竟flash很早就具有这一功能。从下面的内容你可以看到在silverlight中使用camera和microphone是如此的简单。

主要内容:

1.视频捕获

2.视频截图

3.音频捕获

一、视频捕获

silverlight的视频捕获只需要简单几步即可做到:获得VideoCaptureDevice对象,声明CaptureSource对象,将VideoCaptureDevice赋值给CaptureSource的VideoCaptureDevice属性,然后将CaptureSource交给VideoBrush即可。有了VideoBrush以后如何操作就随你了。

CaptureSource提供了操作视频和音频的很多方法(后面关于音频的捕获同样需要使用它),关于视频捕获的启动、停止等操作均是由CaptureSource来控制的。而视频捕获在启动操作之前建议对设备访问权限进行判读,如果不允许访问可以通过CaptureDeviceConfiguration.RequestDeviceAccess()方法对用户发出请求。下面还是来看代码吧(Camera是对摄像头操作的简单封装,代码中还包括后面会用到的内容,不过都有注释,其他内容下面也会说到):

+ View Code

来看一下运行效果:

为了更清楚地体验silverlight 视频和音频捕获效果我简单的做了一个界面,当然它包含的内容不仅仅是上面提到的,有些内容是下面要讨论的,不过不用担心我将整个界面的说明以图片形式展示出来:

在界面左上方是视频设备列表,这个是展开的效果:

要想得到所有可用设备只需要使用CaptureDeviceConfiguration.GetAvailableVideoCaptureDevices()便可以获得所有可用视频设备(音频设备与之类似)。

注意:上面在调用CaptureDeviceConfiguration.RequestDeviceAccess()进行视频请求时会弹出下面的界面,如果你不想每次都请求只要勾选"Remember my answer"。如果不小心勾选了并且点击了"No"怎么办呢?可以通过右键"Silverlight"在下图列表中删除即可。

 

二、视频截图

很多视频软件都有截图功能,并且添加很多特效将其保存到本地,例如dell官方有一款Dell Webcam Central就可以使用摄像头拍照、制作大头贴、录制视频等。下面是这款应用的截图,看起来还是很有意思:

这里不妨试一下silverlight的截图功能。在silverlight中要实现截图功能一般有两种方式:调用CaptureSource的CaptureImageAsync()方法和利用WriteableBitmap对象直接取得视频所在容器的截图。二者不同的地方在于第一种方式截取的是原始视频图像,而第二种方式截取的则是容器内所有可见内容。换句话说即使你在视频容器中放入一个TextBlock,截图时也会将TextBlock中的文本截取下来,而第一种方式则不会。

在体验截图效果之前在这里插入一个小主题,在上面的截图中你或许已经注意到UI的左下方有一组"特效选项",没错,这里将带您一起实现一个类似于Dell Webcam Central的特效功能,由于此功能刚好可以说明两种截图的不同效果因此先来看一下如何实现视频(图片)特效。实现视频特效功能方法比较简单,就是设置控件的Effect属性,但是关键是要实现这些效果要花费一番功夫。不过不用担心,这里有一个强大的特效库http://wpffx.codeplex.com/,而且是完全开源的,里面除了提供silverlight特效组件还有用于wpf的特效。有了它要制作特效简直易如反掌,这里简单列举几类:

using System;

using System.Collections.Generic;

using System.Windows.Media;

using System.Windows.Media.Effects;

using ShaderEffectLibrary;

 

namespace Cmj.MyWeb.MySilverlight.SilverlightEffect

{

    public class Shadercs

    {

        /// <summary>

        /// 反色特效

        /// </summary>

        public static InvertColorEffect InvertColor()

        {

            InvertColorEffect ice= new InvertColorEffect();

            return ice;

        }

 

        /// <summary>

        /// 黑白特效

        /// </summary>

        public static MonochromeEffect Monochrome(Color filterColor)

        {

            MonochromeEffect me= new MonochromeEffect();

            me.FilterColor = filterColor;

            return me;

        }

 

        /// <summary>

        /// 漩涡特效

        /// </summary>

        public static SwirlEffect Swirl(double swirlStrength)

        {

            SwirlEffect we = new SwirlEffect();

            we.SwirlStrength = swirlStrength;

            return we;

        }

 

        /// <summary>

        /// 色调

        /// </summary>

        public static ColorToneEffect ColorTone(double tone,Color lightColor)

        {

            ColorToneEffect cte = new ColorToneEffect();

            cte.Toned=tone;

            cte.LightColor = lightColor;

            return cte;

        }

 

        /// <summary>

        /// 修饰

        /// </summary>

        public static EmbossedEffect Embossed(double amount,double width)

        {

            EmbossedEffect ee= new EmbossedEffect();

            ee.Amount = amount;

            ee.Width = width;

            return ee;

        }

    }

}

有了它只要在点击"特效选项"中的每个Image时设置不同的Effect即可,当然这里要保留原视频,因此第一个Image组件只需要使用原有Effect即可。下面是几种效果下的截图,当然wpffx还有很多的特效,这里不再赘余(为了方便截图,下面直接使用虚拟摄像头,本人不再献丑了O(∩_∩)O~)。

 

效果不错,回到之前的截图问题,下面是两种截图方式截图比较(两中截图均是在添加了Invert特效后),可以清晰的看出对于采用第一种方式的截图根本没有添加特效,而第二种方式截图却完整保留了特效,这也正验证了上面关于两种方式截图的区别。

 

截图相关代码在上面Camera类中。

截图功能已经实现了,但是还无法保存到本地。文件的保存事实上最重要的就是encode问题,无论是采用第一种方式还是第二种方式来截图,最终得到的都是WriteableBitmap,而这个对象是不存在任何save方法的,因此需要自己实现编码问题。不过这里你仍然不需要担心,已经有朋友对此进行封装并且开源,这就是ImageTools,地址:http://imagetools.codeplex.com/,其中有对于png编码操作(当然还有其他开源类库,这里采用ImageTools)。有了这个开源控件,只需要对WriteableBitmap进行简单扩展即可:

using System.IO;

using System.Windows.Controls;

using System.Windows.Media.Imaging;

using ImageTools;

using ImageTools.IO.Png;

 

namespace Cmj.MyWeb.MySilverlight.SilverlightImage

{

    public static class BitmapExtensions

    {

        public static void SaveToPNG(this WriteableBitmap bitmap)//扩展WriteableBitmap类添加SaveToPNG方法

        {

            if (bitmap != null)

            {

                SaveFileDialog sfd = new SaveFileDialog

                                        {

                                             Filter = "PNG Files (*.png)|*.png|All Files (*.*)|*.*",

                                             DefaultExt = ".png",

                                             FilterIndex = 1

                                        };

                if ((bool)sfd.ShowDialog())

                {

                    var img = bitmap.ToImage();

                    var encoder = new PngEncoder();

                    using (Stream stream = sfd.OpenFile())

                    {

                        encoder.Encode(img, stream);

                        stream.Close();

                    }

                }

            }

        }

 

    }

}

扩展之后WriteableBitmap就拥有了SaveToPNG方法。在点击保存时只需要将截图区域中Image控件的Source属性转化为WriteableBitmap对象调用SaveToPNG

方法(在此之前注意先将扩展方法所在命名空间引入)。

使用效果:

 

三、音频捕获

有了上面视频捕获的操作,音频捕获就变得更简单了,不同之处仅仅就是VideoCaptureDevice变成了AudioCaptureDevice而已,只不过这次设置的是CaptureSource的AudioCaptureDevice属性而已。

同视频捕获不同,音频操作没办法直接看到捕获效果,因此不妨将音频保存到本地。想要保存音频第一个问题就是如何得到音频数据?此刻就需要请出AudioSink类,它公开了音频设备捕获方法,只需要实现并且覆盖这些方法就可接收音频信息,当然这其中就包括录音数据。AudioSink公开了四个虚拟回调方法,供调用者重写,下面是msdn对于AudioSink重写的说明:

为了使用捕获图形中的原始音频,必须重写 OnSamples 并在将 sampleData 转换为目标格式的音频接收器中提供实现。然后可以使用从 BitsPerSample、Channels 和 SamplesPerSecond 获取的数字将位流转换为 PCM 音频格式。转换逻辑可能使用一系列嵌套循环和适当的数据类型(如 8 位的 Byte 和 32 位的 Int32)获取离散示例。

如果提供了 OnSamples 的有意义重写,应该同时提供有意义的 OnFormatChange 重写。在第一次连接设备与实际捕获这两个时刻之间,此接收器的 AudioFormat 信息可能会改变。处理 OnFormatChange 是了解运行时上的真实格式信息的唯一方法。

可以在更改通知时使用 OnCaptureStarted 和 OnCaptureStopped,以向用户指示当前正在捕获音频或捕获已停止。还可以将通知用作标记,以在捕获仍在进行时阻止应用程序的某些其他操作。

前一段简而言之就是在OnSamples中获取原始音频,而后一段是什么意思呢?不妨看看下面msdn对于OnFormatChange的备注:

接收器的 AudioFormat 可以不同于在 AudioCaptureDevice.DesiredFormat 中请求的格式。

当在接收器中启动音频捕获时,针对该接收器的第一次捕获,系统将至少调用一次 OnFormatChange。因此,可以将 OnFormatChange 用作音频格式的最终确定,而不使用 DesiredFormat 或 SupportedFormats。

AudioCaptureDevice 属性旨确定应用程序逻辑是否可以根据其格式支持特定设备的捕获和示例转换。(如果 GetAvailableAudioCaptureDevices 方法报告在当前默认设备不受支持的情况下可能起作用的替代方案,您还可以提示用户更改其配置 UI 中的设备。)

来自音频设备(包括获取捕获的初始格式)的格式更改通常也会与您的 OnSamples 实现相关。

从这段描述中可以得知音频的最终格式可以由OnFormatChange来确定。

接下来就来继承并重写AudioSink吧(Microphone是对麦克风操作的封装):

 

using System;

using System.Collections.ObjectModel;

using System.Windows.Media;

using System.Windows.Controls;

using System.IO;

 

namespace Cmj.MyWeb.MySilverlight.SilverlightMeida

{

    public class Microphone:AudioSink

    {

        public MemoryStream Stream{get;private set;}

        public AudioFormat Format { get; private set; }

 

        private AudioCaptureDevice _acDevice = null;

        private CaptureSource _cSource = null;

        public Microphone()

        {

            _cSource = new CaptureSource();

            _cSource.Stop();

            _acDevice = GetAudioDevice();

            _cSource.AudioCaptureDevice = _acDevice;

            this.CaptureSource = _cSource;

        }

 

        /// <summary>

        /// 设置当前音频设备

        /// </summary>

        /// <param name="acDevice"></param>

        public void SetAudioCaptureDevice(AudioCaptureDevice acDevice)

        {

            _acDevice = acDevice;

            _cSource.AudioCaptureDevice = _acDevice;

        }

 

        /// <summary>

        /// 取得所有可用音频捕获设备

        /// </summary>

        /// <returns></returns>

        public ReadOnlyCollection<AudioCaptureDevice> GetAvailableDevice()

        {

            return CaptureDeviceConfiguration.GetAvailableAudioCaptureDevices();

        }

 

        /// <summary>

        /// 获得默认音频设备

        /// </summary>

        /// <returns></returns>

        public AudioCaptureDevice GetAudioDevice()

        {

            return CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice();

        }

 

        /// <summary>

        /// 开始音频捕获

        /// </summary>

        public void StartCapture()

        {

            if (CaptureDeviceConfiguration.AllowedDeviceAccess || CaptureDeviceConfiguration.RequestDeviceAccess())

            {

                _cSource.Start();

            }

        }

 

        /// <summary>

        /// 停止音频捕获

        /// </summary>

        public void StopCapture()

        {

            _cSource.Stop();

        }

        /***************************************下面是对应AudioSink方法的覆盖****************************************/

        /// <summary>

        /// 开始捕获

        /// </summary>

        protected override void OnCaptureStarted()

        {

            Stream = new MemoryStream();

        }

 

        /// <summary>

        /// 停止捕获

        /// </summary>

        protected override void OnCaptureStopped()

        {

             

        }

 

        /// <summary>

        /// 音频格式更改

        /// 第一次捕获时会调用一次,此后便确定格式

        /// </summary>

        /// <param name="audioFormat"></param>

        protected override void OnFormatChange(AudioFormat audioFormat)

        {

            Format = audioFormat;

        }

 

        /// <summary>

        /// 捕获完音频

        /// </summary>

        /// <param name="sampleTimeInHundredNanoseconds"></param>

        /// <param name="sampleDurationInHundredNanoseconds"></param>

        /// <param name="sampleData"></param>

        protected override void OnSamples(long sampleTimeInHundredNanoseconds, long sampleDurationInHundredNanoseconds, byte[] sampleData)

        {

            Stream.Write(sampleData, 0, sampleData.Length);

        }

 

        /***************************************下面是音频保存相关操作****************************************/

        /// <summary>

        /// 添加WAV header

        /// </summary>

        /// <param name="audioByteLength"></param>

        /// <param name="audioFormat"></param>

        /// <returns></returns>

        public void SavePcmToWav(Stream rawData, Stream output, AudioFormat audioFormat)

        {

            if (audioFormat.WaveFormat != WaveFormatType.Pcm)

                throw new ArgumentException("Only PCM coding is supported.");

 

            BinaryWriter bwOutput = new BinaryWriter(output);

 

            bwOutput.Write("RIFF".ToCharArray());

            bwOutput.Write((uint)(rawData.Length + 36));

            bwOutput.Write("WAVE".ToCharArray());

            bwOutput.Write("fmt ".ToCharArray());

            bwOutput.Write((uint)0x10);

            bwOutput.Write((ushort)0x01);

            bwOutput.Write((ushort)audioFormat.Channels);

            bwOutput.Write((uint)audioFormat.SamplesPerSecond);

            bwOutput.Write((uint)(audioFormat.BitsPerSample * audioFormat.SamplesPerSecond * audioFormat.Channels / 8));

            bwOutput.Write((ushort)(audioFormat.BitsPerSample * audioFormat.Channels / 8));

            bwOutput.Write((ushort)audioFormat.BitsPerSample);

            bwOutput.Write("data".ToCharArray());

            bwOutput.Write((uint)rawData.Length);

            long originalRawDataStreamPosition = rawData.Position;

            rawData.Seek(0, SeekOrigin.Begin);

            byte[] buffer = new byte[4096];

            int read;     

            while ((read = rawData.Read(buffer, 0, 4096)) > 0)

            {

                bwOutput.Write(buffer, 0, read);

            }

            rawData.Seek(originalRawDataStreamPosition, SeekOrigin.Begin);

        }

 

        /// <summary>

        /// 音频保存

        /// </summary>

        public void SaveToWav()

        {

            SaveFileDialog sfDialog = new SaveFileDialog();

            sfDialog.Filter = "音频(*.wav)|*.wav";

            if (sfDialog.ShowDialog()==true)

            {

                using (Stream stream = sfDialog.OpenFile())

                {

                    SavePcmToWav(this.Stream, stream, this.Format);

                }

            }

 

        }

    }

}

将原始音频存放到MemoryStream中,然后在OnFormatChange中获得音频格式。这样一来音频捕获时将会执行OnSamples将其写入MemoryStream中,由于AudioCaptureDevice可以捕获得到PCM数据,接下来只要给捕获的音频数据加上WAV头就可以了。

相对视频编码来说音频编码会简单许多,而且从占用空间大小上音频也会小很多(这也是将其临时存储到MemoryStream中的原因),上面Microphone.cs中有关于音频头添加和保持的具体代码,关于WAV更多编码信息可以访问http://technology.niagarac.on.ca/courses/ctec1631/WavFileFormat.html(目前好像只有pcm的wave编码)。

添加过WAV头之后接下来只需要将MemoryStream中的内容编码并写入本地即可,具体代码见上面Microphone中SaveToWav方法。

下面是演示效果:

 

好了,今天主要跟大家一起学习了silverlight中视频和音频捕获基本内容,但是或许有人会问音频可以保存了、截图也可以保存了,那么视频呢?如何使用silverlight录制视频呢?没错,这是接下来要跟大家一起学习的内容,下一篇博客我会跟大家一起学习如何使用silverlight进行视频录制。

本文源代码下载

时间: 2024-09-04 22:07:40

Silverlight之摄像头麦克风使用的相关文章

Silverlight 4 Beta之操作摄像头/麦克风

Silverlight4Beta带来了万众期待的新特性:对摄像头/麦克风的支持. 本篇文章将通过一个操作摄像头的实例来演示这个新特性,我们的实例主要实现以下功能 显示设备名 开始/停止捕获视频 实时截取图像 由于麦克风的使用和摄像头大同小异,并且也无法直观的表现所以在这里就不赘述了. 老规矩,我们先造个简单UI出来,XAML如下 <UserControl.Resources> <Style TargetType="TextBlock"> <Setter P

如何使用台电摄像头麦克风

由于现在的摄像头都会自带一个内置或外置麦克风,使得用户在视频的同时也可以进行语言聊天.不过你会发现,当你用摄像头的麦克风时,需要把麦克风移到离你嘴边2厘米的距离说话,对方才能听得清楚,或是根本不能用麦克风说话,以为摄像头自带的麦克风是坏的.其实这是电脑系统设置的问题,而一般摄像头自带的麦克风基本是没有问题的,都能够在15厘米左右的距离就可以对着麦克风说话,而对方也能听得很清晰,以下是笔者在家里使用台电特效王T700的经历,给大家说明一下怎么设置麦克风,4步解决麦克风不能用的情况. 特效王T700

QQ 6.2版邀请体验:多人通话支持摄像头麦克风随意换

QQ 6.2版邀请体验:多人通话支持摄像头麦克风随意换7月28日消息,腾讯体验中心今日开启了QQ 6.2版的邀请体验.体验日期:2014年7月28日-2014年8月7日.QQ 6.2版本"我的收藏"支持分类查看,新增音视频通话等状态标识,收藏网页助手支持收藏的内容可编辑:多人通话过程中支持摄像头.麦克风随意换等新功能.QQ 6.2版本新功能:1."我的收藏"支持分类查看,内容更清晰.查找更便捷:2.主面板新增音视频通话等状态标识,加入畅聊触手可及:3.收藏网页助手支

EasyDarwin+ffmpeg进行PC(摄像头+麦克风)流媒体直播服务

上一回我们描述了用EasyDarwin+ffmpeg进行摄像机直播的过程:ffmpeg推送,EasyDarwin转发,vlc播放 实现整个RTSP直播 我们再进行一个方面的描述,那就是pc摄像头+麦克风进行主播过程: 第一步>列出我们本机的设备:ffmpeg -list_devices true -f dshow -i dummy   第二步>ffmpeg编码推送到EasyDarwin:ffmpeg -f dshow -i video="Integrated Camera"

Silverlight实用窍门系列:41.Silverlight中调用麦克风模拟录音机设备,存储为WAV音频【附带实例源码】

   在Silverlight 4中支持了麦克风设置的调用,在本节中我们将调用麦克风设备,然后进行录音,并且将录制的声音存取为Wav音频文件.         第一步.首先我们从AudioSink类派生一个音频接收器类:WavAudioSink.其代码如下所示: public class WavAudioSink:AudioSink { // 设置需要记录的内存流 private MemoryStream _stream; // 设置当前的音频格式 private AudioFormat _fo

Flash/Flex学习笔记(27):摄像头/麦克风的视频/音量指示器

在一些实时视频或视频分享应用中,需要动态显示麦克风的音量大小,或者检测视频是不是正在播放,这里演示一种简单的音量指示器1.先写一个指示器类 其实就是一个根据百分比来填充的矩形 package { import flash.display.Sprite; //音量指示器(by 菩提树下的杨过 http://yjmyzz.cnblogs.com/) public class Indicator extends Sprite { private var _w:uint; private var _h:

Silverlight中摄像头的运用—part2

Silverlight 4 中摄像头的运用-part1 将跟踪颜色视作输入  好了,我们能够跟踪到这个颜色了,那这么做的意义是什么呢?实际上,我们可以根据它的位置来移动东西.接下来的例子中,创建的一个球会跟随这个颜色一起移动.你可以用来作出很诡异的对象跟随画面移动的效果.  关键代码: ===================================         //part2         Ellipse ball = new Ellipse();         Composit

流媒体技术学习笔记之(十四)FFmpeg进行笔记本摄像头+麦克风实现流媒体直播服务

FFmpeg推送视频流,Nginx RTMP模块转发,VLC播放器播放,实现整个RTMP直播 查看本机电脑的设备 ffmpeg -list_devices true -f dshow -i dummy 红色标记表示视频设备和麦克风设备 看到乱码了吧!来这里查看哦   FFmpeg编码推送到RTMP服务器 ffmpeg -f dshow -i video="Lenovo EasyCamera":audio="麦克风 (Realtek High Definition Audio)

Silverlight之区域图表

摘要:目前有很多图表插件可以在开发中供开发者选择,包括Silverlight本身就内置了很多图表插件,但是多数图表插件并没有提供地理区域图表功能.例如想看一下北京十八个区县人口分布情况,当然你可以使用饼图.柱状图等,但是如果可以直接看到类似于地图的分布状况不是更加直观吗? 主要内容: 1.图表功能 2.图表设计 3.使用效果 一.图表功能 图表有以下功能特点 区域可定制化,今天是北京市明天可能还需要用到上海市,因此这个区域不能是硬编码的而是可以自由定制的. 使用灵活,插件使用者可以自用设置图表的