【发布时间】:2011-07-05 00:24:30
【问题描述】:
我有一个奇怪的问题,我正在开发一个蓝牙相机,我们想为世界提供一个 mjpeg 接口。
Mjpeg 只是一个 http 服务器一个接一个地回复一个 jpeg 并保持连接保持打开状态。我的服务器现在正在给我:
HTTP/1.1 200 正常 传输编码:分块 缓存指令:无缓存 过期:0 Pragma-Directive:无缓存 服务器:TwistedWeb/10.0.0 连接:保持活动 Pragma:无缓存 缓存控制:无缓存、无存储、必须重新验证; 日期:2011 年 2 月 26 日星期六 20:29:56 GMT 内容类型:multipart/x-mixed-replace;边界=我的边界 HTTP/1.1 200 正常 传输编码:分块 缓存指令:无缓存 过期:0 Pragma-Directive:无缓存 服务器:TwistedWeb/10.0.0 连接:保持活动 Pragma:无缓存 缓存控制:无缓存、无存储、必须重新验证; 美食:2011 年 2 月 26 日星期六 20:29:56 GMT 内容类型:multipart/x-mixed-replace;边界=我的边界然后对于每一帧:
--myBOUNDARY 内容类型:图片/jpeg 内容大小:25992 二进制 JPEG 内容...... (新队)我为它制作了一个 Flash 客户端,所以我们可以在任何设备上使用相同的代码,服务器是在 Python 中使用 twisted 实现的,并且针对 Android 等,Android 中的问题是 Google 忘记了包括 mjpeg 支持....此客户端正在使用 URLStream。
代码是这样的:
包 net.aircable { 导入 flash.errors.*; 导入 flash.events.*; 导入 flash.net.URLRequest; 导入 flash.net.URLRequestMethod; 导入 flash.net.URLRequestHeader; 导入 flash.net.URLStream; 导入 flash.utils.ByteArray; 导入 flash.utils.Dictionary; 导入 flash.system.Security; 导入 mx.utils.Base64Encoder; 导入 flash.external.ExternalInterface; 导入 net.aircable.XHRMultipartEvent; 公共类 XHRMultipart 扩展 EventDispatcher{ 私有函数 trc(what: String): void{ //ExternalInterface.call("console.log", 什么); //对于安卓 跟踪(什么); } 私有变量uri:字符串; 私有变量用户名:字符串; 私有变量密码:字符串; 私有变量流:URLStream; 私有变量缓冲区:字节数组; 私有变量挂起:int; 私有变量标志:布尔值; 私有变量类型:字符串; 私有变量浏览器:字符串; 私有函数连接():无效{ 流 = 新 URLStream(); trc("连接") var request:URLRequest = new URLRequest(uri); request.method = URLRequestMethod.POST; request.contentType = "multipart/x-mixed-replace"; trc(request.contentType) /* request.requestHeaders = 新数组( new URLRequestHeader("内容类型", "multipart/x-mixed-replace"), 新的 URLRequestHeader(“连接”,“保持活动”), 新的 URLRequestHeader("keep-alive", "115")); */ 跟踪(request.requestHeaders); trc("request.requestHeaders") 配置监听器(); 尝试 { trc("连接"); 流。加载(请求); trc("已连接") } 捕捉(错误:错误){ trc("无法加载请求的资源"); } this.pending = 0; this.flag = false; this.buffer = new ByteArray(); } 公共函数 XHRMultipart(uri: String = null, 用户名:字符串=空, 密码:字符串=空){ trc("XHRMultipart()"); var v : String = ExternalInterface.call("function(){return navigator.appVersion+'-'+navigator.appName;}"); trc(v); v=v.toLowerCase(); if (v.indexOf("chrome") > -1){ 浏览器=“铬”; } else if (v.indexOf("safari") > -1){ 浏览器=“野生动物园”; } 别的 { 浏览器=空; } trc(浏览器); 如果(uri == null) uri = "../stream?ohhworldIhatethecrap.mjpeg"; this.uri = uri; 连接(); } 私有函数configureListeners():无效{ stream.addEventListener(Event.COMPLETE, completeHandler, false, 0, true); stream.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler, false, 0, true); stream.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler, false, 0, true); stream.addEventListener(Event.OPEN, openHandler, false, 0, true); stream.addEventListener(ProgressEvent.PROGRESS,progressHandler,假,0,真); stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler, false, 0, true); } 私有函数propagatePart(out:ByteArray,类型:String):void { trc("找到 " + out.length + " mime: " + type); dispatchEvent(new XHRMultipartEvent(XHRMultipartEvent.GOT_DATA, true, false, out)); } 私有函数 readLine(): String { 变种:字符串=“”; 变量临时:字符串; 而(真){ 如果(stream.bytesAvailable == 0) 休息; temp = stream.readUTFBytes(1); if (temp == "\n") 休息; 出+=温度; } 退出; } 私有函数 extractHeader(): 无效 { 变线:字符串; var headers: 对象 = {}; 变量头:数组; 而 ( (line=readLine()) != "" ){ 如果(stream.bytesAvailable == 0) 返回; if (line.indexOf('--') > -1){ 继续; } head = line.split(":"); if (head.length==2){ 标题[head[0].toLowerCase()]=head[1]; } } 待定=int(标题[“内容大小”]); 类型=标题[“内容类型”]; 如果(挂起> 0 &&类型!= null) 标志=真; trc("待定:" + 待定 +" 类型:" + 类型); } 私有函数 firefoxExtract(): void { trc("firefoxPrepareToExtract"); 如果(stream.bytesAvailable == 0){ trc("没有更多字节,正在中止") 返回; } 而(标志==假){ 如果(stream.bytesAvailable == 0){ trc("没有更多字节,正在中止 - 无法提取标题"); 返回; } 提取头() } trc("目前有:" + stream.bytesAvailable); trc("我们需要:" + 待定); if (stream.bytesAvailable =0; x-=1){ 缓冲区.位置=x; buffer.readBytes(temp, 0, 2); // 检查是否找到结束标记 if (temp[0]==0xff && temp[1]==0xd9){ 结束=x; 休息; } } trc("findImageInBuffer, start: " + start + " end: " + end); 如果(开始 >-1 && 结束 > -1){ var 输出:ByteArray = new ByteArray(); 缓冲区.位置=开始; buffer.readBytes(输出, 0 , end-start); 传播部分(输出,类型); 缓冲区.位置=0; // 丢弃所有内容 缓冲区长度=0; } } 私有函数 safariExtract(): 无效 { trc("safariExtract()"); 流.readBytes(缓冲区,缓冲区。长度); findImageInBuffer(); } 私有函数 chromeExtract(): 无效 { trc("chromeExtract()"); 流.readBytes(缓冲区,缓冲区。长度); findImageInBuffer(); } 私有函数提取图像():无效{ trc("提取图像"); 如果(浏览器 == 空){ firefoxExtract(); } 否则 if (browser == "safari"){ 野生动物园提取物(); } 否则如果(浏览器==“铬”){ 铬提取(); } } 私有函数 isCompressed():Boolean { 返回 (stream.readUTFBytes(3) == ZLIB_CODE); } 私有函数完成处理程序(事件:事件):无效{ trc("completeHandler:" + 事件); //提取图像(); //连接(); } 私有函数openHandler(事件:事件):无效{ trc("openHandler:" + 事件); } 私有函数progressHandler(事件:ProgressEvent):无效{ trc("progressHandler:" + 事件) trc("可用:" + stream.bytesAvailable); 提取图像(); if (event.type == ProgressEvent.PROGRESS) if (event.bytesLoaded > 1048576) { //1*1024*1024 bytes = 1MB trc("传输" + event.bytesLoaded +" 关闭") 流.close(); 连接(); } } 私有函数securityErrorHandler(事件:SecurityErrorEvent):无效{ trc("securityErrorHandler:" + 事件); } 私有函数httpStatusHandler(事件:HTTPStatusEvent):无效{ trc("httpStatusHandler:" + 事件); trc("可用:" + stream.bytesAvailable); 提取图像(); //连接(); } 私有函数 ioErrorHandler(event:IOErrorEvent):void { trc("ioErrorHandler:" + 事件); } } };客户端在 Firefox 上运行良好,我得到了所有的 http 标头:
--myBOUNDARY 内容类型:图片/jpeg 内容大小:25992所以我使用 content-size 来知道要前进多少字节。在 IE8 中也会发生同样的情况(即使是有缺陷的 IE 也兼容!)
在 Safari 上,它的工作方式有点不同(也许是 webkit 做的)
问题在于 Chrome,信不信由你,它无法正常工作。发生了一些奇怪的事情,显然我得到了第一个 tcp/ip 包,然后由于某种原因 Chrome 决定关闭连接,日志的输出是这样的:
XHRMultipart() 5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.114 Safari/534.16-Netscape 铬合金 连接 多部分/x-混合替换 request.requestHeaders 连接 连接的 openHandler: [Event type="open" bubbles=false cancelable=false eventPhase=2] openHandler: [Event type="open" bubbles=false cancelable=false eventPhase=2] progressHandler: [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=3680 bytesTotal=0] 可用:3680 提取图像 铬提取() findImageInBuffer,开始:0 结束:-1 httpStatusHandler: [HTTPStatusEvent type="httpStatus" bubbles=false cancelable=false eventPhase=2 status=200 responseURL=null] 可用:0 提取图像 铬提取() findImageInBuffer,开始:0 结束:-1在服务器关闭连接之前,我不应该得到 httpStatus,而这里不是这种情况。
请不要告诉我使用 HTML5 Canvas 或 Video 我都准备好了,问题是我们希望这个应用程序在许多操作系统中运行并且为所有操作系统编译视频编码器(例如 ffmpeg)不会使工作更轻松。我们还想提供 SCO 音频,它只是一个 PCM 流,所以我不能使用普通的 mjpeg。 Canvas 太慢了,我测试过,特别是在 Android 上。
【问题讨论】:
-
我稍微修正了代码,使其现在与 Konqueror 兼容。我的主要问题是我不是 AS3 专家,我来自 Python 世界,有一些黑暗的 Java 背景,以及一些 C/C++。
标签: flash actionscript-3 google-chrome twisted xmlhttprequest