从Silverlight 4(Beta)流式传输网络摄像头

Ken*_*ith 16 silverlight webcam silverlight-4.0

Silverlight 4中的新网络摄像头很酷.通过将其作为画笔公开,它允许超出Flash所具有的任何场景.

与此同时,在本地访问网络摄像头似乎只是故事的一半.没有人购买网络摄像头,这样他们就可以拍摄自己的照片并制作出有趣的面孔.他们购买网络摄像头是因为他们希望其他人看到最终的视频流,即他们希望将该视频流式传输到互联网,Skype或任何其他几十个视频聊天网站/应用程序.到目前为止,我还没弄清楚如何做到这一点

事实证明,这是非常简单的,以获得原始(Format32bppArgb格式)的字节流的保持,这表现在这里.

但除非我们想将原始字节流传输到服务器(这会占用过多的带宽),否则我们需要以某种方式对其进行编码.这更复杂.MS已经在Silverlight中实现了几个编解码器,但据我所知,他们都专注于解码视频流,而不是首先对其进行编码.这与我无法弄清楚如何直接访问H.264编解码器的事实不同.

有大量的开源编解码器(例如,在这里的ffmpeg项目中),但它们都是用C语言编写的,并且它们看起来不容易移植到C#.除非翻译10000多行看似这样的代码就是你的乐趣:-)

const int b_xy= h->mb2b_xy[left_xy[i]] + 3;
const int b8_xy= h->mb2b8_xy[left_xy[i]] + 1;
*(uint32_t*)h->mv_cache[list][cache_idx ]= *(uint32_t*)s->current_picture.motion_val[list][b_xy + h->b_stride*left_block[0+i*2]];
*(uint32_t*)h->mv_cache[list][cache_idx+8]= *(uint32_t*)s->current_picture.motion_val[list][b_xy + h->b_stride*left_block[1+i*2]];
h->ref_cache[list][cache_idx ]= s->current_picture.ref_index[list][b8_xy + h->b8_stride*(left_block[0+i*2]>>1)];
h->ref_cache[list][cache_idx+8]= s->current_picture.ref_index[list][b8_xy + h->b8_stride*(left_block[1+i*2]>>1)];
Run Code Online (Sandbox Code Playgroud)

Mono项目中的mooncodecs文件夹(这里)在C#(ADPCM和Ogg Vorbis)中有几个音频编解码器,还有一个视频编解码器(Dirac),但它们似乎只实现了各自格式的解码部分,java也是如此移植它们的实现.

我找到了Ogg Theora的C#编解码器(csTheora,http://www.wreckedgames.com/forum/index.php?topic = 1053.0 ),但同样,它只是解码,就像它所基于的jheora编解码器一样.

当然,从Java移植编解码器可能比从C或C++移植更容易,但我发现的唯一java视频编解码器只是解码(例如jheora或jirac).

所以我有点回到原点.看起来我们通过Silverlight将网络摄像头(或麦克风)连接到Internet的选项是:

(1)等待微软就此提供一些指导;

(2)花费大脑周期将一个C或C++编解码器移植到兼容Silverlight的C#;

(3)将原始的,未压缩的字节流发送到服务器(或者用zlib之类的东西稍微压缩),然后在服务器端编码; 要么

(4)等待比我聪明的人想出这个并提供解决方案.

还有其他人有更好的指导吗?我是否错过了对其他人来说非常明显的事情?(例如,Silverlight 4在某个地方是否有一些我错过的课程可以解决这个问题?)

Ken*_*ith 3

我想我应该让感兴趣的人知道我实际采取的方法。我使用 CSpeex 来编码语音,但我编写了自己的基于块的视频编解码器来编码视频。它将每个帧分为 16x16 块,确定哪些块已充分更改以保证传输,然后使用经过大量修改的 FJCore 版本对更改的块进行 Jpeg 编码。(FJCore 通常做得很好,但需要对其进行修改,以不写入 JFIF 标头,并加速各种对象的初始化。)所有这些都使用大致基于的专有协议传递到专有媒体服务器实时传输协议。

在 144x176 下有一个向上流和四个向下流时,我目前每秒获得 5 帧,总共使用 474 Kbps(约 82 Kbps/视频流 + 32 Kbps/音频),并消耗了我的大约 30% 的 CPU开发盒。质量不是很好,但对于大多数视频聊天应用程序来说是可以接受的。

自从我发布最初的问题以来,已经多次尝试实施解决方案。最好的可能是 SocketCoder 网站,这里(和这里)。

然而,由于 SocketCoder 运动 JPEG 风格的视频编解码器会转换每个帧的整体,而不仅仅是已更改的块,因此我的假设是 CPU 和带宽要求对于大多数应用程序来说将是令人望而却步的。

不幸的是,在可预见的未来,我自己的解决方案将不得不保持专有:-(。

2010 年 7 月 3 日编辑:我刚刚获得分享我对 FJCore 库的修改的权限。我已经在这里发布了该项目(不幸的是,没有任何示例代码):

http://www.alanta.com/Alanta.Client.Media.Jpeg.zip

如何使用它的(非常粗略的)示例:

    public void EncodeAsJpeg()
    {
        byte[][,] raster = GetSubsampledRaster();
        var image = new Alanta.Client.Media.Jpeg.Image(colorModel, raster);
        EncodedStream = new MemoryStream();
        var encoder = new JpegFrameEncoder(image, MediaConstants.JpegQuality, EncodedStream);
        encoder.Encode();
    }


    public void DecodeFromJpeg()
    {
        EncodedStream.Seek(0, SeekOrigin.Begin);
        var decoder = new JpegFrameDecoder(EncodedStream, height, width, MediaConstants.JpegQuality);
        var raster = decoder.Decode();
    }
Run Code Online (Sandbox Code Playgroud)

我的大部分更改都是围绕两个新类 JpegFrameEncoder(而不是 JpegEncoder)和 JpegFrameDecoder(而不是 JpegDecoder)。基本上,JpegFrameEncoder 写入没有任何 JFIF 标头的编码帧,并且 JpegFrameDecoder 解码帧而不期望任何 JFIF 标头告诉它要使用什么值(它假设您将以其他带外方式共享这些值) )。它还实例化一次所需的任何对象(作为“静态”),以便您可以以最小的开销快速实例化 JpegFrameEncoder 和 JpegFrameDecoder。预先存在的 JpegEncoder 和 JpegDecoder 类的工作方式应该与往常一样,尽管我只做了很少的测试来确认这一点。

我想改进它的很多东西(我不喜欢静态对象——它们应该被实例化并单独传入),但它目前对于我们的目的来说已经足够好了。希望它对其他人有帮助。我会看看是否可以改进代码/文档/示例代码/等。如果我有时间。