我想创建一个 Flash MJPEG 播放器,以便不受支持的浏览器可以查看它。
我在这里尝试了 2 种路线,一种使用 urlStream,另一种使用套接字。我将使用套接字,因为它似乎越来越远。
MJPEG 播放器将播放来自远程 IP 摄像机的摄像机流。因为摄像头位于路由器后面,所以它位于端口 8006。我已在该端口的摄像头根目录上传了一个 crossdomain.xml 文件
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<site-control permitted-cross-domain-policies="master-only" />
<allow-access-from domain="*" to-ports="*" />
</cross-domain-policy>
此位置位于 h**p://domainofcam.com:8006/crossdomain.xml
在附件中您将找到我正在使用的 as3 代码。在我的 Flash 文件中,我在第 1 帧上,
Security.loadPolicyFile("xmlsocket:h**p://domainofcam_com:8006/crossdomain.xml");
trace("xmlsocket:h**p://domainofcam_com:8006/crossdomain.xml")
var cam:MJPEG = new MJPEG("h**p://domainofcam.com", "/mjpeg.cgi", 8006);
addChild(cam);
我的 MJPEG as3 文件是这样的:
package
{
import flash.display.Loader;
import flash.events.Event;
import flash.events.ProgressEvent;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.net.URLRequestHeader;
import flash.net.URLStream;
import flash.net.Socket;
import flash.utils.ByteArray;
import com.dynamicflash.util.Base64;
/**
* This is a class used to view a MJPEG
* @author Josh Chernoff | GFX Complex
*
*/
public class MJPEG extends Loader
{
private var _user:String; //Auth user name
private var _pass:String; //Auth user password
private var _host:String; //host server of stream
private var _port:int; //port of stream
private var _file:String; //Location of MJPEG
private var _start:int = 0; //marker for start of jpg
private var webcamSocket:Socket = new Socket(); //socket connection
private var imageBuffer:ByteArray = new ByteArray(); //image holder
public function MJPEG (host:String, file:String, port:int = 80, user:String = null, pass:String = null )
{
_host = host;
_file = file;
_port = port;
_user = user;
_pass = pass;
webcamSocket.addEventListener(Event.CONNECT, handleConnect);
webcamSocket.addEventListener(ProgressEvent.SOCKET_DATA, handleData);
webcamSocket.addEventListener(IOErrorEvent.IO_ERROR, IOErrorSocket);
webcamSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityError);
webcamSocket.connect(host, port);
}
private function IOErrorSocket(event:IOErrorEvent):void {
var date:Date = new Date();
trace(event);
}
private function securityError(event:SecurityErrorEvent):void {
var date:Date = new Date();
trace(event);
}
private function handleConnect(e:Event):void
{
// we're connected send a request
var httpRequest:String = "GET "+_file+" HTTP/1.1\r\n";
httpRequest+= "Host: localhost:80\r\n";
if(_user != null && _pass != null){
var source:String = String(_user + ":" + _pass);
var auth:String = Base64.encode(source);
httpRequest += "Authorization: Basic " + auth.toString()+ "\r\n";
//NOTE THIS MAY NEEED TO BE EDITED TO WORK WITH YOUR CAM
}
httpRequest+="Connection: keep-alive\r\n\r\n";
webcamSocket.writeMultiByte(httpRequest, "us-ascii");
}
function handleData(e:ProgressEvent):void {
// get the data that we received.
// append the data to our imageBuffer
webcamSocket.readBytes(imageBuffer, imageBuffer.length);
//trace(imageBuffer.length);
while(findImages()){
//donothing
}
}
private function findImages():Boolean
{
var x:int = _start;
var startMarker:ByteArray = new ByteArray();
var end:int = 0;
var image:ByteArray;
if (imageBuffer.length > 1) {
if(_start == 0){
//Check for start of JPG
for (x; x < imageBuffer.length - 1; x++) {
// get the first two bytes.
imageBuffer.position = x;
imageBuffer.readBytes(startMarker, 0, 2);
//Check for end of JPG
if (startMarker[0] == 255 && startMarker[1] == 216) {
_start = x;
break;
}
}
}
for (x; x < imageBuffer.length - 1; x++) {
// get the first two bytes.
imageBuffer.position = x;
imageBuffer.readBytes(startMarker, 0, 2);
if (startMarker[0] == 255 && startMarker[1] == 217){
end = x;
image = new ByteArray();
imageBuffer.position = _start;
imageBuffer.readBytes(image, 0, end - _start);
displayImage(image);
// truncate the imageBuffer
var newImageBuffer:ByteArray = new ByteArray();
imageBuffer.position = end;
imageBuffer.readBytes(newImageBuffer, 0);
imageBuffer = newImageBuffer;
_start = 0;
x = 0;
return true;
}
}
}
return false;
}
private function displayImage(image:ByteArray):void
{
this.loadBytes(image);
}
}
}
当我运行调试时,我得到以下输出:
[IOErrorEvent type="ioError" bubbles=false cancelable=false eventPhase=2 text="Error #2031: Socket Error. URL: h*p://domainofcam.com"] [SecurityErrorEvent type="securityError" bubbles=false cancelable=false eventPhase=2 text="Error #2048: Security sandbox violation: file:///Repository/projects/Surfcam/mjpg/MJPG.swf cannot load data from h*p://domainofcam.com:8006."]
最佳答案
我也遇到过同样的问题。解决方案是使用 URLStream 而不是 Socket。 这里是工作源代码,几乎没有变化 - 我已经制作了 MXML 组件以在 Flex 中使用它。
package ru.idecide.olimpstroy
{
import com.dynamicflash.util.Base64;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.HTTPStatusEvent;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Socket;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import flash.net.URLStream;
import flash.utils.ByteArray;
import mx.core.IMXMLObject;
public class MJPG extends EventDispatcher implements IMXMLObject
{
[Bindable]
public var user:String; //Auth user name
[Bindable]
public var pass:String; //Auth user password
[Bindable]
public var host:String; //host server of stream
[Bindable]
public var port:int = 80; //port of stream
[Bindable]
public var file:String; //Location of MJPEG
private var start:int = 0; //marker for start of jpg
private var imageBuffer:ByteArray = new ByteArray(); //image holder
public function MJPG()
{
loader = new Loader;
}
private var stream:URLStream;
public function initialized(document:Object, id:String):void
{
stream = new URLStream();
var request:URLRequest = new URLRequest(host + ":" + port.toString() + file);
request.requestHeaders.push(new URLRequestHeader("Authorization", "Basic " + Base64.encode(user + ":" + pass)));
configureListeners(stream);
try {
stream.load(request);
} catch (error:Error) {
trace("Unable to load requested URL.");
}
}
private function configureListeners(dispatcher:EventDispatcher):void {
dispatcher.addEventListener(Event.COMPLETE, completeHandler);
dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
dispatcher.addEventListener(Event.OPEN, openHandler);
dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
}
private function parseHeader():void {
trace("parseHeader", "length: " + imageBuffer.length);
stream.readBytes(imageBuffer, imageBuffer.length)
while(findImages()){
//donothing
}
}
private function completeHandler(event:Event):void {
trace("completeHandler: " + event);
parseHeader();
}
private function openHandler(event:Event):void {
trace("openHandler: " + event);
}
private function progressHandler(event:Event):void {
trace("progressHandler: " + event);
parseHeader();
}
private function securityErrorHandler(event:SecurityErrorEvent):void {
trace("securityErrorHandler: " + event);
}
private function httpStatusHandler(event:HTTPStatusEvent):void {
trace("httpStatusHandler: " + event);
}
private function ioErrorHandler(event:IOErrorEvent):void {
trace("ioErrorHandler: " + event);
}
private function onLoaded(e:Event):void {
//var bytes:ByteArray = loader.data;
while(findImages()){
//donothing
}
}
private function findImages():Boolean
{
var x:int = start;
var startMarker:ByteArray = new ByteArray();
var end:int = 0;
var image:ByteArray;
if (imageBuffer.length > 1) {
if(start == 0){
//Check for start of JPG
for (x; x < imageBuffer.length - 1; x++) {
// get the first two bytes.
imageBuffer.position = x;
imageBuffer.readBytes(startMarker, 0, 2);
//Check for end of JPG
if (startMarker[0] == 255 && startMarker[1] == 216) {
start = x;
break;
}
}
}
for (x; x < imageBuffer.length - 1; x++) {
// get the first two bytes.
imageBuffer.position = x;
imageBuffer.readBytes(startMarker, 0, 2);
if (startMarker[0] == 255 && startMarker[1] == 217){
end = x;
image = new ByteArray();
imageBuffer.position = start;
imageBuffer.readBytes(image, 0, end - start);
displayImage(image);
// truncate the imageBuffer
var newImageBuffer:ByteArray = new ByteArray();
imageBuffer.position = end;
imageBuffer.readBytes(newImageBuffer, 0);
imageBuffer = newImageBuffer;
start = 0;
x = 0;
return true;
}
}
}
return false;
}
private var loader:Loader;
private var _bitmapData:BitmapData;
private function displayImage(image:ByteArray):void
{
loader.loadBytes(image);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderComplete_handler);
}
private function loaderComplete_handler(event:Event):void
{
if (_bitmapData)
_bitmapData.dispose();
_bitmapData = Bitmap(loader.content).bitmapData;
dispatchEvent(new Event("bitmapDataUpdate"));
}
[Bindable(event="bitmapDataUpdate")]
public function get bitmapData():BitmapData
{
return _bitmapData;
}
}
}
我这样使用它:
<fx:Declarations>
<mp:MJPG host=""http://example.com" file="/mjpg/video.mjpg" user="{user}" pass="{password}" id="videoProvider"/>
</fx:Declarations>
<s:BitmapImage source="{videoProvider.bitmapData}" smooth="true" width="100%"/>
我建议您不要像示例中那样使用 Loader,因为它会使图像抽搐。在我的示例中,它运行良好。
关于flash - 使用 as3 通过 flash 加载和播放 mjpeg - 套接字错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8173496/