天天看点

Silverlight之视频录制

摘要:在前两篇silverlight的文章中跟大家一块学习了silverlight的基础知识、silverlight摄像头麦克风的相关操作以及截图、声音录制等,在文章后面也简单的说明了为什么没有视频录制,今天就和大家一块看一下上一节中最后的一个问题:如何使用silverlight进行视频录制。

主要内容:

1.nesl项目简介

2.使用nesl实现视频录制

3.注意

在silverlight 中如何录制视频?相信这个问题有不少朋友都搜索过,但是好像目前还没有见到很好的答案,究其原因其实就是视频编码问题。当然也有朋友提到直接进行截图,只要每秒截取足够多的图片,然后依次播放就可以形成视频。但是我看到国外一个朋友使用此方法进行了几十秒的视频录制,其文件大小就达到了百兆级别,而且还进行了优化。因此这种方式要实现视频录制就目前而言还不是很合适。那么到底有没有好的方法呢?答案是有,但有限制,那就是借助于nesl。

native extensions for silverlight(简称nesl)是由微软silverlight团队进行开发,其目的主要为了增强silverlight out-of-browser离线应用的功能。大家都知道虽然silverlight 4的oob应用支持信任人权限提升功能,允许silverlight的oob应用对com组件的访问,但对绝大多数windows api仍旧无法调用,而nesl的出现正是为了解决这个问题。在最新的nesl 2.0中包含了大量有用的功能,而这其中就包括今天要说的视频编码部分。在nesl中有一个类库microsoft.silverlight.windows.localencode.dll主要负责本地视频和音频编码,这里就是用此类库来解决上面提到的视频录制问题。

在microsoft.silverlight.windows.localencode.dll中一个核心类就是encodesession,它负责音频和视频的编码输出工作。使用encodesession进行视频录制大概分为下面两步:

1.准备输入输出信息

在这个过程中需要定义videinputformatinfo、audioinputformatinfo、videooutputformatinfo、audiooutputformatinfo和outputcontainerinfo,然后调用encodesession.prepare()方法。

2.捕获视频输出

当输入输出信息准备好之后接下来就是调用encodesession.start()方法进行视频编码输出。当然为了接收音频和视频数据必须准备两个sink类,分别继承于audiosink和videosink,在这两个sink中指定capturesource,并且在对应的onsample中调用encodesession的wirtevideosample()和wirteaudiosample()接收并编码数据(关于audiosink在前面的文章中已经说过,videosink与之类似)。

知道了encodesession的使用方法后下面就将其操作进行简单封装,localcamera.cs是本例中的核心类:

<code>using</code> <code>system;</code>

<code>using</code> <code>system.collections.objectmodel;</code>

<code>using</code> <code>system.io;</code>

<code>using</code> <code>system.windows;</code>

<code>using</code> <code>system.windows.threading;</code>

<code>using</code> <code>system.windows.media;</code>

<code>using</code> <code>system.windows.controls;</code>

<code>using</code> <code>system.windows.shapes;</code>

<code>using</code> <code>microsoft.silverlight.windows.localencode;</code>

<code>namespace</code> <code>cmj.myweb.mysilverlight.silverlightmeida</code>

<code>{</code>

<code>    </code><code>/// &lt;summary&gt;</code>

<code>    </code><code>/// 编码状态</code>

<code>    </code><code>/// &lt;/summary&gt;</code>

<code>    </code><code>public</code> <code>enum</code> <code>encodesessionstate</code>

<code>    </code><code>{</code>

<code>        </code><code>start,</code>

<code>        </code><code>pause,</code>

<code>        </code><code>stop</code>

<code>    </code><code>}</code>

<code>    </code><code>/// 本地视频对象</code>

<code>    </code><code>public</code> <code>class</code> <code>localcamera</code>

<code>        </code><code>private</code> <code>string</code> <code>_savefullpath =</code><code>""</code><code>;</code>

<code>        </code><code>private</code> <code>uint</code> <code>_videowidth = 640;</code>

<code>        </code><code>private</code> <code>uint</code> <code>_videoheight = 480;</code>

<code>        </code><code>private</code> <code>videosinkextensions _videosink =</code><code>null</code><code>;</code>

<code>        </code><code>private</code> <code>audiosinkextensions _audiosink=</code><code>null</code><code>;</code>

<code>        </code><code>private</code> <code>encodesession _encodesession =</code><code>null</code><code>;</code>

<code>        </code><code>private</code> <code>usercontrol _page =</code><code>null</code><code>;</code>

<code>        </code><code>private</code> <code>capturesource _csource =</code><code>null</code><code>;</code>

<code>        </code><code>public</code> <code>localcamera(usercontrol page,videoformat videoformat,audioformat audioformat)</code>

<code>        </code><code>{</code>

<code>            </code><code>//this._savefullpath = savefullpath;</code>

<code>            </code><code>this</code><code>._videowidth = (</code><code>uint</code><code>)videoformat.pixelwidth;</code>

<code>            </code><code>this</code><code>._videoheight = (</code><code>uint</code><code>)videoformat.pixelheight;</code>

<code>            </code><code>this</code><code>._page = page;</code>

<code>            </code><code>this</code><code>.sessionstate = encodesessionstate.stop;</code>

<code>            </code><code>//this._encodesession = new encodesession();</code>

<code>            </code><code>_csource =</code><code>new</code> <code>capturesource();</code>

<code>            </code><code>this</code><code>.videodevice = defaultvideodevice;</code>

<code>            </code><code>this</code><code>.videodevice.desiredformat = videoformat;</code>

<code>            </code><code>this</code><code>.audiodevice = defaultaudiodevice;</code>

<code>            </code><code>this</code><code>.audiodevice.desiredformat = audioformat;</code>

<code>            </code><code>_csource.videocapturedevice =</code><code>this</code><code>.videodevice;</code>

<code>            </code><code>_csource.audiocapturedevice =</code><code>this</code><code>.audiodevice;</code>

<code>            </code><code>audioinputformatinfo =</code><code>new</code> <code>audioinputformatinfo() { sourcecompressiontype = formatconstants.audioformat_pcm };</code>

<code>            </code><code>videoinputformatinfo =</code><code>new</code> <code>videoinputformatinfo() { sourcecompressiontype = formatconstants.videoformat_argb32 };</code>

<code>            </code><code>audiooutputformatinfo =</code><code>new</code> <code>audiooutputformatinfo() { targetcompressiontype = formatconstants.audioformat_aac };</code>

<code>            </code><code>videooutputformatinfo =</code><code>new</code> <code>videooutputformatinfo() { targetcompressiontype = formatconstants.videoformat_h264 };</code>

<code>            </code><code>outputcontainerinfo =</code><code>new</code> <code>outputcontainerinfo() { containertype = formatconstants.transcodecontainertype_mpeg4 };</code>

<code>        </code><code>}</code>

<code>        </code><code>public</code> <code>localcamera(usercontrol page,videocapturedevice videocapturedevice,audiocapturedevice audiocapturedevice, videoformat videoformat, audioformat audioformat)</code>

<code>            </code><code>this</code><code>.videodevice = videocapturedevice;</code>

<code>            </code><code>this</code><code>.audiodevice = audiocapturedevice;</code>

<code>        </code><code>public</code> <code>encodesessionstate sessionstate</code>

<code>            </code><code>get</code><code>;</code>

<code>            </code><code>set</code><code>;</code>

<code>        </code><code>public</code> <code>encodesession session</code>

<code>            </code><code>get</code>

<code>            </code><code>{</code>

<code>                </code><code>return</code> <code>_encodesession;</code>

<code>            </code><code>}</code>

<code>            </code><code>set</code>

<code>                </code><code>_encodesession = value;</code>

<code>        </code><code>/// &lt;summary&gt;</code>

<code>        </code><code>/// 编码对象所在用户控件对象</code>

<code>        </code><code>/// &lt;/summary&gt;</code>

<code>        </code><code>public</code> <code>usercontrol ownpage</code>

<code>                </code><code>return</code> <code>_page;</code>

<code>                </code><code>_page = value;</code>

<code>        </code><code>/// 捕获源</code>

<code>        </code><code>public</code> <code>capturesource source</code>

<code>                </code><code>return</code> <code>_csource;</code>

<code>        </code><code>/// 操作音频对象</code>

<code>        </code><code>public</code> <code>audiosinkextensions audiosink</code>

<code>                </code><code>return</code> <code>_audiosink;</code>

<code>        </code><code>public</code> <code>static</code> <code>videocapturedevice defaultvideodevice</code>

<code>                </code><code>return</code> <code>capturedeviceconfiguration.getdefaultvideocapturedevice();</code>

<code>        </code> 

<code>        </code><code>public</code> <code>static</code> <code>readonlycollection&lt;videocapturedevice&gt; availablevideodevice</code>

<code>                </code><code>return</code> <code>capturedeviceconfiguration.getavailablevideocapturedevices();</code>

<code>        </code><code>public</code> <code>videocapturedevice videodevice</code>

<code>        </code><code>public</code> <code>static</code> <code>audiocapturedevice defaultaudiodevice</code>

<code>                </code><code>return</code> <code>capturedeviceconfiguration.getdefaultaudiocapturedevice();</code>

<code>        </code><code>public</code> <code>static</code> <code>readonlycollection&lt;audiocapturedevice&gt; availableaudiodevice</code>

<code>                </code><code>return</code> <code>capturedeviceconfiguration.getavailableaudiocapturedevices();</code>

<code>        </code><code>public</code> <code>audiocapturedevice audiodevice</code>

<code>        </code><code>private</code> <code>object lockobj =</code><code>new</code> <code>object</code><code>();</code>

<code>        </code><code>internal</code> <code>videoinputformatinfo videoinputformatinfo;</code>

<code>        </code><code>internal</code> <code>audioinputformatinfo audioinputformatinfo;</code>

<code>        </code><code>internal</code> <code>videooutputformatinfo videooutputformatinfo;</code>

<code>        </code><code>internal</code> <code>audiooutputformatinfo audiooutputformatinfo;</code>

<code>        </code><code>internal</code> <code>outputcontainerinfo outputcontainerinfo;</code>

<code>        </code><code>/// 视频录制</code>

<code>        </code><code>public</code> <code>void</code> <code>startrecord()</code>

<code>            </code><code>lock</code> <code>(lockobj)</code>

<code>                </code><code>if</code> <code>(</code><code>this</code><code>.sessionstate == encodesessionstate.stop)</code>

<code>                </code><code>{</code>

<code>                    </code><code>_videosink =</code><code>new</code> <code>videosinkextensions(</code><code>this</code><code>);</code>

<code>                    </code><code>_audiosink =</code><code>new</code> <code>audiosinkextensions(</code><code>this</code><code>);</code>

<code>                    </code><code>//_audiosink.volumnchange += new audiosinkextensions.volumnchangehanlder(_audiosink_volumnchange);</code>

<code>                    </code><code>if</code> <code>(_encodesession ==</code><code>null</code><code>)</code>

<code>                    </code><code>{</code>

<code>                        </code><code>_encodesession =</code><code>new</code> <code>encodesession();</code>

<code>                    </code><code>}</code>

<code>                    </code><code>prepareformatinfo(_csource.videocapturedevice.desiredformat, _csource.audiocapturedevice.desiredformat);</code>

<code>                    </code><code>_encodesession.prepare(videoinputformatinfo, audioinputformatinfo, videooutputformatinfo, audiooutputformatinfo, outputcontainerinfo);</code>

<code>                    </code><code>_encodesession.start(</code><code>false</code><code>, 200);</code>

<code>                    </code><code>this</code><code>.sessionstate = encodesessionstate.start;</code>

<code>                </code><code>}</code>

<code>        </code><code>/// 音量大小指示</code>

<code>        </code><code>/// &lt;param name="sender"&gt;&lt;/param&gt;</code>

<code>        </code><code>/// &lt;param name="e"&gt;&lt;/param&gt;</code>

<code>        </code><code>//void _audiosink_volumnchange(object sender, volumnchangeargs e)</code>

<code>        </code><code>//{</code>

<code>        </code><code>//    this.ownpage.dispatcher.begininvoke(new action(() =&gt;</code>

<code>        </code><code>//    {</code>

<code>        </code><code>//        (</code>

<code>        </code><code>//            this.ownpage.tag as progressbar).value = e.volumn;</code>

<code>        </code><code>//    }));</code>

<code>        </code><code>//}</code>

<code>        </code><code>/// 暂停录制</code>

<code>        </code><code>public</code> <code>void</code> <code>pauserecord()</code>

<code>                </code><code>this</code><code>.sessionstate = encodesessionstate.pause;</code>

<code>                </code><code>_encodesession.pause();</code>

<code>        </code><code>/// 停止录制</code>

<code>        </code><code>public</code> <code>void</code> <code>stoprecord()</code>

<code>                </code><code>this</code><code>.sessionstate = encodesessionstate.stop;</code>

<code>                </code><code>_encodesession.shutdown();</code>

<code>                </code><code>_videosink =</code><code>null</code><code>;</code>

<code>                </code><code>_audiosink =</code><code>null</code><code>;</code>

<code>        </code><code>/// 准备编码信息</code>

<code>        </code><code>/// &lt;param name="videoformat"&gt;&lt;/param&gt;</code>

<code>        </code><code>/// &lt;param name="audioformat"&gt;&lt;/param&gt;</code>

<code>        </code><code>private</code> <code>void</code> <code>prepareformatinfo(videoformat videoformat, audioformat audioformat)</code>

<code>            </code><code>uint</code> <code>frameraterationumerator = 0;</code>

<code>            </code><code>uint</code> <code>frameraterationdenominator = 0;</code>

<code>            </code><code>formatconstants.frameratetoratio((</code><code>float</code><code>)math.round(videoformat.framespersecond, 2),</code><code>ref</code> <code>frameraterationumerator,</code><code>ref</code> <code>frameraterationdenominator);</code>

<code>            </code><code>videoinputformatinfo.frameraterationumerator = frameraterationumerator;</code>

<code>            </code><code>videoinputformatinfo.framerateratiodenominator = frameraterationdenominator;</code>

<code>            </code><code>videoinputformatinfo.framewidthinpixels = _videowidth;</code>

<code>            </code><code>videoinputformatinfo.frameheightinpixels = _videoheight ;</code>

<code>            </code><code>videoinputformatinfo.stride = (</code><code>int</code><code>)_videowidth*-4;</code>

<code>            </code><code>videooutputformatinfo.frameraterationumerator = frameraterationumerator;</code>

<code>            </code><code>videooutputformatinfo.framerateratiodenominator = frameraterationdenominator;</code>

<code>            </code><code>videooutputformatinfo.framewidthinpixels = videooutputformatinfo.framewidthinpixels == 0 ? (</code><code>uint</code><code>)videoformat.pixelwidth : videooutputformatinfo.framewidthinpixels;</code>

<code>            </code><code>videooutputformatinfo.frameheightinpixels = videooutputformatinfo.frameheightinpixels == 0 ? (</code><code>uint</code><code>)videoformat.pixelheight : videooutputformatinfo.frameheightinpixels;</code>

<code>            </code><code>audioinputformatinfo.bitspersample = (</code><code>uint</code><code>)audioformat.bitspersample;</code>

<code>            </code><code>audioinputformatinfo.samplespersecond = (</code><code>uint</code><code>)audioformat.samplespersecond;</code>

<code>            </code><code>audioinputformatinfo.channelcount = (</code><code>uint</code><code>)audioformat.channels;</code>

<code>            </code><code>if</code> <code>(outputcontainerinfo.filepath ==</code><code>null</code> <code>|| outputcontainerinfo.filepath ==</code><code>string</code><code>.empty)</code>

<code>                </code><code>_savefullpath=system.io.path.combine(environment.getfolderpath(environment.specialfolder.myvideos),</code><code>"ccamerarecordvideo.tmp"</code><code>);</code>

<code>            </code><code>outputcontainerinfo.filepath = _savefullpath;</code>

<code>            </code><code>//outputcontainerinfo.filepath = _savefullpath;</code>

<code>            </code><code>if</code> <code>(audiooutputformatinfo.averagebitrate == 0)</code>

<code>                </code><code>audiooutputformatinfo.averagebitrate = 24000;</code>

<code>            </code><code>if</code> <code>(videooutputformatinfo.averagebitrate == 0)</code>

<code>                </code><code>videooutputformatinfo.averagebitrate = 2000000;</code>

<code>        </code><code>/// 开始捕获</code>

<code>        </code><code>public</code> <code>void</code> <code>startcaptrue()</code>

<code>            </code><code>if</code> <code>(capturedeviceconfiguration.alloweddeviceaccess || capturedeviceconfiguration.requestdeviceaccess())</code>

<code>                </code><code>_csource.start();</code>

<code>        </code><code>/// 停止捕获</code>

<code>        </code><code>public</code> <code>void</code> <code>stopcapture()</code>

<code>            </code><code>_videosink =</code><code>null</code><code>;</code>

<code>            </code><code>_audiosink =</code><code>null</code><code>;</code>

<code>            </code><code>_csource.stop();</code>

<code>        </code><code>/// 获得视频</code>

<code>        </code><code>/// &lt;returns&gt;&lt;/returns&gt;</code>

<code>        </code><code>public</code> <code>videobrush getvideobrush()</code>

<code>            </code><code>videobrush vbrush =</code><code>new</code> <code>videobrush();</code>

<code>            </code><code>vbrush.setsource(_csource);</code>

<code>            </code><code>return</code> <code>vbrush;</code>

<code>        </code><code>public</code> <code>rectangle getvideorectangle()</code>

<code>            </code><code>rectangle rctg =</code><code>new</code> <code>rectangle();</code>

<code>            </code><code>rctg.width =</code><code>this</code><code>._videowidth;</code>

<code>            </code><code>rctg.height =</code><code>this</code><code>._videoheight;</code>

<code>            </code><code>rctg.fill = getvideobrush();</code>

<code>            </code><code>return</code> <code>rctg;</code>

<code>        </code><code>/// 保存视频</code>

<code>        </code><code>public</code> <code>void</code> <code>saverecord()</code>

<code>            </code><code>if</code> <code>(_savefullpath ==</code><code>string</code><code>.empty)</code>

<code>                </code><code>messagebox.show(</code><code>"尚未录制视频,无法进行保存!"</code><code>,</code><code>"系统提示"</code><code>, messageboxbutton.ok);</code>

<code>                </code><code>return</code><code>;</code>

<code>            </code><code>savefiledialog sfd =</code><code>new</code> <code>savefiledialog</code>

<code>                </code><code>filter =</code><code>"mp4 files (*.mp4)|*.mp4"</code><code>,</code>

<code>                </code><code>defaultext =</code><code>".mp4"</code><code>,</code>

<code>                </code><code>filterindex = 1</code>

<code>            </code><code>};</code>

<code>            </code><code>if</code> <code>((</code><code>bool</code><code>)sfd.showdialog())</code>

<code>                </code><code>using</code> <code>(stream stm=sfd.openfile())</code>

<code>                    </code><code>filestream fs =</code><code>new</code> <code>filestream(_savefullpath, filemode.open, fileaccess.read);</code>

<code>                    </code><code>try</code>

<code>                        </code><code>byte</code><code>[] buffur =</code><code>new</code> <code>byte</code><code>[fs.length];</code>

<code>                        </code><code>fs.read(buffur, 0, (</code><code>int</code><code>)fs.length);</code>

<code>                        </code><code>stm.write(buffur, 0, (</code><code>int</code><code>)buffur.length);</code>

<code>                        </code><code>fs.close();</code>

<code>                        </code><code>file.delete(_savefullpath);</code>

<code>                    </code><code>catch</code> <code>(ioexception ioe)</code>

<code>                        </code><code>messagebox.show(</code><code>"文件保存失败!错误信息如下:"</code><code>+environment.newline+ioe.message,</code><code>"系统提示"</code><code>,messageboxbutton.ok);</code>

<code>                    </code><code>stm.close();</code>

<code>}</code>

当然上面说过必须有两个sink:

<code>    </code><code>public</code> <code>class</code> <code>videosinkextensions:videosink</code>

<code>        </code><code>//private usercontrol _page;</code>

<code>        </code><code>//private encodesession _session;</code>

<code>        </code><code>private</code> <code>localcamera _localcamera;</code>

<code>        </code><code>public</code> <code>videosinkextensions(localcamera localcamera)</code>

<code>            </code><code>//this._page = page;</code>

<code>            </code><code>this</code><code>._localcamera = localcamera;</code>

<code>            </code><code>//this._session = session;</code>

<code>            </code><code>this</code><code>.capturesource = _localcamera.source;</code>

<code>        </code><code>protected</code> <code>override</code> <code>void</code> <code>oncapturestarted()</code>

<code>            </code> 

<code>        </code><code>protected</code> <code>override</code> <code>void</code> <code>oncapturestopped()</code>

<code>        </code><code>protected</code> <code>override</code> <code>void</code> <code>onformatchange(videoformat videoformat)</code>

<code>        </code><code>protected</code> <code>override</code> <code>void</code> <code>onsample(</code><code>long</code> <code>sampletimeinhundrednanoseconds,</code><code>long</code> <code>framedurationinhundrednanoseconds,</code><code>byte</code><code>[] sampledata)</code>

<code>            </code><code>if</code> <code>(_localcamera.sessionstate == encodesessionstate.start)</code>

<code>                </code><code>_localcamera.ownpage.dispatcher.begininvoke(</code><code>new</code> <code>action&lt;</code><code>long</code><code>,</code><code>long</code><code>,</code><code>byte</code><code>[]&gt;((ts, dur, data) =&gt;</code>

<code>                    </code><code>_localcamera.session.writevideosample(data, data.length, ts, dur);</code>

<code>                </code><code>}), sampletimeinhundrednanoseconds, framedurationinhundrednanoseconds, sampledata);</code>

  

<code>    </code><code>public</code> <code>class</code> <code>audiosinkextensions:audiosink</code>

<code>        </code><code>public</code> <code>audiosinkextensions(localcamera localcamera)</code>

<code>        </code><code>protected</code> <code>override</code> <code>void</code> <code>onformatchange(audioformat audioformat)</code>

<code>        </code><code>protected</code> <code>override</code> <code>void</code> <code>onsamples(</code><code>long</code> <code>sampletimeinhundrednanoseconds,</code><code>long</code> <code>sampledurationinhundrednanoseconds,</code><code>byte</code><code>[] sampledata)</code>

<code>                    </code><code>_localcamera.session.writeaudiosample(data, data.length, ts, dur);</code>

<code>                </code><code>}), sampletimeinhundrednanoseconds, sampledurationinhundrednanoseconds, sampledata);</code>

<code>                </code><code>//计算音量变化</code>

<code>                </code><code>//for (int index = 0; index &lt; sampledata.length; index += 1)</code>

<code>                </code><code>//{</code>

<code>                </code><code>//    short sample = (short)((sampledata[index] &lt;&lt; 8) | sampledata[index]);</code>

<code>                </code><code>//    float sample32 = sample / 32768f;</code>

<code>                </code><code>//    float maxvalue = 0;</code>

<code>                </code><code>//    float minvalue = 0;</code>

<code>                </code><code>//    maxvalue = math.max(maxvalue, sample32);</code>

<code>                </code><code>//    minvalue = math.min(minvalue, sample32);</code>

<code>                </code><code>//    float lastpeak = math.max(maxvalue, math.abs(minvalue));</code>

<code>                </code><code>//    float miclevel = (100 - (lastpeak * 100)) * 10;</code>

<code>                </code><code>//    onvolumnchange(this, new volumnchangeargs() { volumn=miclevel});</code>

<code>                </code><code>//}</code>

<code>        </code><code>/// 定义一个事件,反馈音量变化</code>

<code>        </code><code>//public delegate void volumnchangehanlder(object sender, volumnchangeargs e);</code>

<code>        </code><code>//public event volumnchangehanlder volumnchange;</code>

<code>        </code><code>//private void onvolumnchange(object sender, volumnchangeargs e)</code>

<code>        </code><code>//    if (volumnchange != null)</code>

<code>        </code><code>//        volumnchange(sender, e);</code>

<code>        </code><code>//    }</code>

<code>    </code><code>//public class volumnchangeargs : eventargs</code>

<code>    </code><code>//{</code>

<code>    </code><code>//    public float volumn</code>

<code>    </code><code>//    {</code>

<code>    </code><code>//        get;</code>

<code>    </code><code>//        internal set;</code>

<code>    </code><code>//    }</code>

<code>    </code><code>//}</code>

有了这三个类,下面准备一个界面,使用localcamera进行视频录制操作。

Silverlight之视频录制

需要注意的是保存操作,事实上在encodesession中视频的保存路径是在视频录制之前就必须指定的(当然这一点并不难理解,因为长时间的视频录制是会形成很大的文件的,保存之前缓存到内存中也不是很现实),在localcamera中对保存方法的封装事实上是文件的读取和删除操作。另外在这个例子中用到了前面文章中自定义的oob控件,不明白的朋友可以查看前面的文章内容。下面是调用代码:

<a href="http://www.cnblogs.com/kenshincui/archive/2011/11/30/2269642.html#">+ view code</a>

ok,下面是视频录制的截图:

正在录制

Silverlight之视频录制

停止录制后保存

Silverlight之视频录制

播放录制的视频

Silverlight之视频录制

1.video sink和audio sink都是运行在不同于ui的各自的线程中,你可以使用ui的dispathcher或者synchronizationcontext进行不同线程之间的调用。

2.在video sink和audio sink的onsample方法中必须进行状态判断,因为sink实例创建之后就会执行onsample方法,但此时encodesession还没有启动因此如果不进行状态判读就会抛出com异常。

3.视频的宽度和高度不能够随意指定,这个在nesl的帮助文档中也是特意说明的,如果任意指定同样会抛出异常。

4.最后再次提醒大家,上面的视频录制是基于nesl的因此必须将应用运行到浏览器外(oob)。

Silverlight之视频录制

继续阅读