65.9K
CodeProject 正在变化。 阅读更多。
Home

cam2web - 将摄像机流式传输到 Web 作为 MJPEG 流。

starIconstarIconstarIconstarIconstarIcon

5.00/5 (24投票s)

2017 年 8 月 7 日

GPL3

22分钟阅读

viewsIcon

103742

downloadIcon

2738

本文介绍了cam2web项目——一个旨在将摄像头作为MJPEG流传输的开源应用程序。

cam2web  

目录

引言

前段时间,我正在做一个基于树莓派板的机器人相关项目。其中一个要求是能够通过网络浏览器或任何最适合该任务的软件远程查看派的摄像头。快速搜索后,找到了许多关于该主题的教程,它们都基于MJPG-Streamer应用程序。然而,在我设想的项目中,使用它并不是一个真正的选择。首先,是由于该软件的一些限制,如果不进行大量修改,就无法实现我的目标。其次,远程查看摄像头只是项目的第一步。因此,决定创建一些自己的代码,以满足我现有项目的需求,并为我将来的一些想法奠定基础。结果,制定了一个快速的需求列表

  • 通过HTTP将摄像头作为MJPEG流传输,以便任何支持它的应用程序都可以使用;
  • 同时提供JPEG快照,以便不友好的MJPEG应用程序可以抓取单个图像;
  • 如果摄像头支持,提供一种控制亮度、对比度、饱和度、锐度等选项的方法;
  • 附带简单的内置Web UI,以便可以直接从网络浏览器查看摄像头,并设置其选项;
  • 支持身份验证,以便可以限制摄像头查看/配置

在工作顺利进行的同时,决定将项目做得更大一点,不仅适用于树莓派,还适用于通用Linux和Windows。结果,在GitHub上创建了一个新的开源项目——cam2web,将摄像头流式传输到Web。

应用程序概述

在深入了解实现和代码的技术细节之前,我们先来看看最终结果。cam2web在不同平台上的外观差异很大。Windows版本带有图形用户界面,允许用户启动摄像头流并配置不同的设置。而Linux和Pi版本则作为命令行工具提供。相反,Web UI在所有平台上看起来几乎相同。只有摄像头的选项看起来不同,因为不同的平台提供不同的API来访问摄像头。但是,我们稍后会介绍。

Windows版本

Windows版本的主窗体非常简单明了——它提供了检测到的摄像头(支持DirectShow API的设备)及其支持的分辨率列表。选择所需的摄像头并点击“开始流传输”按钮后,摄像头将开始向Web直播。

注意:分辨率框显示了摄像头支持的所有分辨率的默认平均帧率。但是,有些摄像头支持一系列帧率——最小/最大帧率。对于此类摄像头,可以覆盖默认帧率并设置所需的帧率。但是,不要指望所有帧率值都能从提供的范围内工作。由于DirectShow API的限制和某些摄像头驱动程序编写不当,许多帧率值可能无法工作。

Windows - Main form

如果您有自己喜欢的应用程序用于查看摄像头,则可以使用类似http://ip:port/camera/mjpeg的URL访问摄像头的MJPEG流。如果需要单个JPEG快照,请使用类似http://ip:port/camera/jpeg的URL。很简单,不是吗!

然而,其中一个要求是,我们实际上不需要任何特殊软件来查看摄像头;只需一个网络浏览器。要尝试它——只需点击主窗体上的“正在流传输...”链接即可。

Web view

许多摄像头提供不同的配置选项来设置亮度、对比度、饱和度等,这些选项在Web UI中点击“设置”按钮时可用。这些选项由应用程序持久化,因此下次启动时,所有设置都应设置为用户所做的任何配置。

cam2web的Windows版本还提供了用户界面来更改各种应用程序选项,例如监听传入连接的默认端口、提供JPEG图像的质量级别、MJPEG流的帧率(如果必须限制为某个值)等。

Windows - Settings form

最后,我们来到最后一个要求——用户身份验证。默认情况下,当应用程序首次启动时,它允许任何人做任何事情——查看摄像头并更改其设置。但是,在大多数情况下,这似乎不理想。因此,“访问权限”窗体允许指定谁可以做什么,并填充允许访问摄像头的用户列表

Windows - Access Rights form

关于更改配置需要注意的一点是——摄像头流必须重新启动才能使新设置生效。只需“停止流传输”/“开始流传输”——应该不是什么大问题。

Windows版本还有一些其他选项可用,但这次是命令行选项

  • /start - 应用程序启动时自动开始摄像头流传输。
  • /minimize - 应用程序启动时最小化窗口。
  • /fcfg:file_name - 存储应用程序设置的配置文件名。默认情况下,应用程序将所有设置(包括上次运行的摄像头/分辨率)存储在用户主目录中cam2web文件夹内的app.cfg中。但是,配置文件的名称可以设置不同,因此可以运行应用程序的多个实例,拥有不同的设置并流传输不同的摄像头。

Linux和树莓派版本

与Windows版本不同,Linux版本不提供任何图形用户界面,因此所有配置都通过命令行选项完成。其中大多数与Windows GUI中的选项非常相似,并且非常易于理解。要查看可用选项列表,只需使用-?选项运行应用程序。

当应用程序在Linux/Pi上启动时,它会自动启动摄像头流(前提是没有发生错误)。因此,可以通过网络浏览器或任何首选应用程序以相同的方式访问它。

与Windows版本不同,Linux/Pi版本不提供编辑可以访问摄像头的用户列表的方法。相反,Apache htdigest工具用于管理用户文件,其名称可以指定为命令行选项之一。但这会造成一个限制——只能创建一个具有管理员角色的用户,即admin。所有其他名称都获得用户角色。

获取代码并构建

cam2web代码发布在GitHub上,因此其最新版本可以从git仓库克隆或作为ZIP存档下载。此外,也可以作为存档下载已标记的发布版本

在构建cam2web本身之前,需要构建随附的web2h工具,该工具将一些常见的web文件(HTML、CSS、JS、JPEG和PNG)转换为头文件。然后将这些文件编译并链接到cam2web可执行文件中,以便它可以在不依赖外部文件的情况下提供默认的web界面。

但是,如果在调试配置中构建,则不需要web2h——所有web内容都从位于./web文件夹中的文件提供。

在Windows上构建

web2hcam2web应用程序提供了Microsoft Visual Studio解决方案文件(例如,可以使用Express 2013)。首先,构建src/tools/web2h/make/msvc/web2h.sln,然后构建src/apps/win/cam2web.sln。成功后,它将生成build/msvc/[configuration]/bin文件夹,其中包含应用程序的可执行文件。

在Linux和树莓派上构建

web2hcam2web提供了GNU make的Makefile。从项目的root文件夹运行以下命令,将在build/gcc/release/bin文件夹中生成所需的可执行文件。

pushd .
cd src/tools/web2h/make/gcc/
make
popd

pushd .
cd src/apps/linux/
# or cd src/apps/pi/
make
popd 

注意:必须安装libjpeg开发库才能成功构建cam2web(可能默认未安装)

sudo apt-get install libjpeg-dev 

从其他应用程序访问摄像头 (WEB API)

如前所述,流式摄像头不仅可以从网络浏览器访问,还可以从任何其他支持MJPEG流的应用程序访问(例如VLC媒体播放器,或用于IP摄像头监控的不同应用程序)。访问MJPEG流的URL格式为
http://ip:port/camera/mjpeg.

如果需要单个图像,下一个URL提供最新的摄像头快照
http://ip:port/camera/jpeg.

摄像头信息

要获取一些摄像头信息,例如设备名称、宽度、高度等,应向以下URL发送HTTP GET请求
http://ip:port/camera/info

它以JSON格式提供回复,可能如下所示

{
  "status":"OK",
  "config":
  {
    "device":"RaspberryPi Camera",
    "title":"My home camera",
    "width":"640"
    "height":"480",
  }
}

更改摄像头设置

可以使用以下URL获取摄像头设置/配置
http://ip:port/camera/config

要获取当前摄像头设置,向上述URL发送HTTP GET请求,它将所有值作为JSON回复提供

{
  "status":"OK",
  "config":
  {
    "awb":"Auto",
    "brightness":"50",
    "contrast":"15",
    "effect":"None",
    "expmeteringmode":"Average",
    "expmode":"Auto",
    "hflip":"1",
    "saturation":"25",
    "sharpness":"100",
    "vflip":"1",
    "videostabilisation":"0"
  }
}

如果只需要某些值,则它们的名称(用逗号分隔)可以作为vars变量传递。例如
http://ip:port/camera/config?vars=brightness,contrast

要设置摄像头的属性,使用相同的URL,但必须使用HTTP POST请求。发布的数据必须包含以JSON格式编码的要设置的变量集合。例如,发布下面的JSON将设置摄像头的亮度和对比度选项

{
  "brightness":"50",
  "contrast":"15"
}

成功后,回复JSON将把status变量设置为“OK”。否则,它将包含失败原因。

获取摄像头属性描述

从1.1.0版本开始,cam2web应用程序提供了摄像头提供的所有属性的描述。这允许,例如,有一个单一的WebUI代码,它首先查询可用属性列表,然后进行统一渲染。可以通过以下URL获取属性描述
http://ip:port/camera/properties

JSON响应提供了所有可用属性的名称、默认值、类型、显示名称和顺序。对于整数类型属性,它还包括允许的最小值和最大值。对于选择类型属性,它提供了该属性所有可能的选择。

{
  "status":"OK",
  "config":
  {
    "hflip":
    {
      "def":0,
      "type":"bool",
      "order":8,
      "name":"Horizontal Flip"
    },
    "brightness":
    {
      "min":0,
      "max":100,
      "def":50,
      "type":"int",
      "order":0,
      "name":"Brightness"
    },
    "awb":
    {
      "def":"Auto",
      "type":"select",
      "order":4,
      "name":"White Balance",
      "choices":
      [
        ["Off","Off"],
        ["Auto","Auto"],
        ["Sunlight","Sunlight"],
        ...
      ]
    },
    ...
  }
}

获取版本信息

要获取流式摄像头的cam2web应用程序的版本信息,使用以下URL
http://ip:port/version,它以以下格式提供信息

{
  "status":"OK",
  "config":
  {
    "platform":"RaspberryPi",
    "product":"cam2web",
    "version":"1.0.0"
  }
}

访问权限

访问JPEG、MJPEG和摄像头信息URL的权限授予可以查看摄像头的人。访问摄像头配置URL的权限授予可以配置摄像头的人。版本URL对任何人开放。

自定义Web UI

所有cam2web的发布版本都附带嵌入式Web资源,因此应用程序可以在不依赖任何额外文件的情况下提供默认的Web UI。但是,即使不深入构建工具,也可以覆盖默认的用户界面。

在Windows上使用配置UI或在Linux上使用命令行选项,可以指定用于提供自定义Web内容的文件夹名称。当未设置该选项时,将提供嵌入式UI。当设置该选项时,将提供指定文件夹中找到的任何内容。

获取默认Web文件

自定义Web UI最简单的方法是首先获取所有默认内容作为起点。所有必需的文件都可以在两个文件夹中找到:src/webexternals/jquery。这些文件必须放在一个文件夹中,因此基本目录树应如下所示

index.html
cameraproperties.html
styles.css
cam2web.png
cam2web_white.png
camera.js
cameraproperties.js
cameraproperty.js
jquery.js
jquery.mobile.css
jquery.mobile.js 

所有文件就位后,可以将应用程序配置为从包含它们的文件夹提供Web内容。为了确保一切正常,请修改styles.css中的一些样式或更改index.html中的一些HTML。如果成功,您就可以进一步自定义Web UI了。

源代码概述

现在是时候稍微浏览一下代码了。这将是一个非常高级的概述,不会深入探讨实现细节。更像是一个架构审查。

大部分代码是用C++编写的,偶尔也有一些纯C。在少数地方,使用了C++ 11的一些特性——现在获取支持它的编译器应该不是问题。

cam2web项目依赖于一些依赖项。首先,使用mongoose嵌入式web服务器作为轻量级HTTP服务器,它易于集成。其次,使用libjpeg-turbo进行JPEG压缩(如果性能不是问题,也可以使用libjpeg)。至于其余的——所有代码都在那里。甚至还制作了一个非常简单的JSON解析器,而不是引入额外的依赖项。

Windows版本的图形用户界面是使用传统的Win32 API完成的。没有MFC,没有Qt,只是KISS。事实上,Windows版本尽可能地偏爱静态链接,这导致了一个不需要甚至MSVC redistributables的单个可执行文件。然而,由于使用了一些UI控件,该应用程序要求Windows Vista作为最低支持操作系统。

至于Web UI,使用了一些jQuery。但是,由于它都是可定制的,如前所述,因此可以全部废弃并替换为任何首选的Web技术。

摄像头

摄像头的API是少数几个在支持的平台之间不同的地方之一。

  • DirectShow API用于在Windows上访问摄像头(USB/集成摄像头、采集卡等)。它是一个基于COM的API,在MSDN上文档完善,并且在Code Project上有大量示例。
  • Video for Linux API(V4L或V4L2,版本2)用于在Linux上访问摄像头(同样是USB/集成摄像头或任何其他支持此接口的设备)。
  • 多媒体抽象层(MMAL)是访问树莓派上摄像头模块的API。

所有这些API都非常不同,用于访问不同平台上摄像头的代码也一样。由于其余大部分代码与平台无关,因此定义了IVideoSource abstract类(一个接口)来设置与不同摄像头通信的通用方式,然后为不同平台提供了三个实现。

// Interface for video source events' listener
class IVideoSourceListener
{
public:
    virtual ~IVideoSourceListener( ) { }

    // New video frame notification
    virtual void OnNewImage( const std::shared_ptr<const XImage>& image ) = 0;

    // Video source error notification
    virtual void OnError( const std::string& errorMessage, bool fatal ) = 0;
};    
    
// Interface for video sources continuously providing frames to display/process
class IVideoSource
{
public:
    virtual ~IVideoSource( ) { }

    // Start video source so it initializes and begins providing video frames
    virtual bool Start( ) = 0;
    // Signal video source to stop, so it could finalize and clean-up
    virtual void SignalToStop( ) = 0;
    // Wait till video source (its thread) stops
    virtual void WaitForStop( ) = 0;
    // Check if video source is still running
    virtual bool IsRunning( ) = 0;

    // Get number of frames received since the start of the video source
    virtual uint32_t FramesReceived( ) = 0;

    // Set video source listener returning the old one
    virtual IVideoSourceListener* SetListener( IVideoSourceListener* listener ) = 0;
};    

尽管我们不打算深入探讨实现细节(源代码可供参考),但仍然值得一提的是,每个平台上的摄像头提供图像数据的方式。Windows上的实现提供RGB24格式的图像,如果需要,它易于处理,并且libjpeg(-turbo)也很好地接受它。

MMAL实现也可以提供RGB24数据,但这可能不是树莓派上的首选。由于Pi的CPU不如桌面电脑强大,因此最好避免在那里进行软件JPEG压缩。幸运的是,MMAL API允许请求已经压缩的图像,这是硬件加速的。因此,MMAL实现提供了两种选项,默认为压缩选项。

最后是Video for Linux的实现。正如V4L文档所建议的,API确实支持RGB24格式。但是,我未能找到一个支持该格式的摄像头。相反,我测试过的所有摄像头都支持YUYV格式。因此,如果需要RGB数据,则需要进行一些解码。但是,与MMAL一样,V4L也允许请求已经压缩的JPEG,这再次节省了CPU使用率。因此,V4L实现模仿了MMAL——提供了一个选项,可以请求未压缩数据或压缩数据,默认为后者。

IVideoSource

摄像头配置

上面定义的IVideoSource接口只提供启动/停止摄像头和从中获取图像的API。显然,这还不够,因此每个特定的实现都提供额外的方​​法来配置摄像头并设置/获取不同的选项,例如亮度、对比度等。初始配置由主应用程序根据用户选择进行。但是,其余需要持久化并从web UI(REST API)更改的配置,最好也进行抽象。

因此,定义了一个简单的IObjectConfigurator接口,它允许使用字符串键/值对配置不同的对象(记住,KISS原则)。

// Interface to configure objects with string key/value pairs
class IObjectConfigurator
{
public:
    virtual ~IObjectConfigurator( ) { }

    // Set/Get specified property
    virtual XError SetProperty( const std::string& propertyName,
                                const std::string& value ) = 0;
    virtual XError GetProperty( const std::string& propertyName,
                                std::string& value ) const = 0;

    // Get values of all properties provided by an object
    virtual std::map<std::string, std::string> GetAllProperties( ) const = 0;
};

上述接口可以由实现IVideoSource的三个类实现。但是,摄像头类保留了提供特定配置方法的功能(以防它们在其他不关心配置抽象的项目中被重用)。相反,另外实现了三个类来为它们绑定的摄像头类提供抽象配置。

IObjectConfigurator

嵌入式Web服务器

如前所述,cam2web使用mongoose嵌入式Web服务器来处理HTTP请求。它小巧且易于集成。仅包含两个文件——源文件和头文件。并且在各种平台上都能顺利构建。是的,它的许可证相当严格。由于它是根据GPL发布的,因此确实要求以相同的许可证提供组合作品。好吧,对于cam2web来说,这不是问题。

mongoose web服务器是用纯C语言实现的。这使得它很小,但对C++用户不太友好。因此,在其周围做了一个小型包装器,使其具有更多的面向对象设计,甚至向最终用户隐藏了所有mongoose内部。

// All implementation details will be hidden, so user does
// not even know we are using mongoose
namespace Private
{
    class XWebServerData;
}

// A class to provide simple embedded web server API
class XWebServer : private Uncopyable
{
public:
    XWebServer( const std::string& documentRoot = "", uint16_t port = 8000 );
    ~XWebServer( );

    // Get/Set document root
    std::string DocumentRoot( ) const;
    XWebServer& SetDocumentRoot( const std::string& documentRoot );

    // Get/Set authentication domain
    std::string AuthDomain( ) const;
    XWebServer& SetAuthDomain( const std::string& authDomain );

    // Get/Set port
    uint16_t Port( ) const;
    XWebServer& SetPort( uint16_t port );

    // Add/Remove web handler
    XWebServer& AddHandler( const std::shared_ptr<IWebRequestHandler>& handler,
                            UserGroup allowedUserGroup = UserGroup::Anyone );
    void RemoveHandler( const std::shared_ptr<IWebRequestHandler>& handler );

    // Remove all handlers
    void ClearHandlers( );
    
    // Start/Stop the Web server
    bool Start( );
    void Stop( );

    // ... some more methods ...
    
private:
    Private::XWebServerData* mData;
};

上述XWebServer类提供了Web服务器的基本配置和生命周期管理。它允许从指定文件夹提供静态内容,或者通过调用继承IWebRequestHandler abstract类的Web请求处理程序动态处理请求。每个处理程序都需要提供HandleHttpRequest()方法的实现,其目的是根据收到的Web请求(使用IWebRequest接口描述)提供Web响应(通过使用IWebResponse接口)。

// Interface for web request details
class IWebRequest
{
public:
    virtual ~IWebRequest( ) { }

    virtual std::string Uri( )    const = 0;
    virtual std::string Method( ) const = 0;
    virtual std::string Proto( )  const = 0;
    virtual std::string Query( )  const = 0;
    virtual std::string Body( )   const = 0;

    virtual std::string GetVariable( const std::string& name ) const = 0;

    virtual std::map<std::string, std::string> Headers( ) const = 0;
};

// Interface used to provide web response
class IWebResponse
{
public:
    virtual ~IWebResponse( ) { }

    // Length of data, which is still enqueued for sending
    virtual size_t ToSendDataLength( ) const = 0;

    virtual void Send( const uint8_t* buffer, size_t length ) = 0;
    virtual void Printf( const char* fmt, ... ) = 0;

    virtual void SendChunk( const uint8_t* buffer, size_t length ) = 0;
    virtual void PrintfChunk( const char* fmt, ... ) = 0;

    virtual void SendError( int errorCode, const char* reason = nullptr ) = 0;

    virtual void CloseConnection( ) = 0;

    // Generate timer event for the connection associated with the response
    // after the specified number of milliseconds
    virtual void SetTimer( uint32_t msec ) = 0;
};

// Base class for web request handlers
class IWebRequestHandler
{
protected:
    IWebRequestHandler( const std::string& uri, bool canHandleSubContent );

public:
    virtual ~IWebRequestHandler( ) { }

    const std::string& Uri( ) const { return mUri;  }
    bool CanHandleSubContent( ) const { return mCanHandleSubContent; }

    // Handle the specified request
    virtual void HandleHttpRequest
            ( const IWebRequest& request, IWebResponse& response ) = 0;

    // Handle timer event
    virtual void HandleTimer( IWebResponse& ) { };

private:
    std::string mUri;
    bool        mCanHandleSubContent;
};

因此,XWebServerpublic API不依赖于特定嵌入式Web服务器的选择实现。它可以是mongoose,也可以是任何其他服务器——所有实现细节都对用户隐藏。

XWebServer

在实现新的请求处理程序时,需要指定它们的两个属性。首先是它们将要服务的URI。例如,一个Web请求处理程序可以服务“/camera/config”URI,而另一个则服务“/camera/info”URI。当HTTP请求进入Web服务器时,它会检查已注册的处理程序列表,并调用与请求URI匹配的处理程序。如果未找到处理程序,服务器将尝试从指定的文档根目录查找静态内容,否则将返回404 – 页面未找到。

请求处理程序的第二个属性是它是否可以处理子内容。如果不能,则处理程序仅用于服务分配给它的URI,例如“/camera/config”。但如果它可以服务子内容,则它将用于以给定URI开头的任何内容,例如“/camera/config/brightness”。所以,它就像服务一个文件夹。显然,然后由处理程序的实现来检查请求的URI并适当地处理它。

处理嵌入式内容

我们已经讨论了抽象Web请求处理程序的概念。现在是时候讨论一些实现特定角色的具体实现方式了。首先,让我们看看嵌入式Web资源的提供方式。

如前所述,XWebServer的API允许指定文档根文件夹,该文件夹用于提供静态内容。当用户想要提供自己的自定义Web UI时,这是很好的选择。但是,正如一开始所述,所有版本的cam2web都不依赖外部文件,而是从嵌入式资源提供默认的Web UI。这是通过XEmbeddedContentHandler类实现的

// Structure defining embedded content
typedef struct
{
    uint32_t       Length;
    const char*    Type;
    const uint8_t* Body;
}
XEmbeddedContent;

// Web request handler serving embedded content
class XEmbeddedContentHandler : public IWebRequestHandler
{
public:
    XEmbeddedContentHandler( const std::string& uri, const XEmbeddedContent* content ) :
        IWebRequestHandler( uri, false ), mContent( content )
    { }

    // Handle request providing given embedded content
    void HandleHttpRequest( const IWebRequest& /* request */, IWebResponse& response )
    {
        response.Printf( "HTTP/1.1 200 OK\r\n"
                         "Content-Type: %s\r\n"
                         "Content-Length: %u\r\n"
                         "\r\n", mContent->Type, mContent->Length );
        response.Send( mContent->Body, mContent->Length );
    }

private:
    const XEmbeddedContent* mContent;
};

这个网络处理程序所做的只是简单地发送指定嵌入式资源的内容以及一些HTML头。它不检查URI,因为这在网络服务器寻找适当的请求处理程序时已经完成。它可能会检查HTTP方法,以确保它是GET请求,但这里保持简单。

以下是定义一些嵌入式内容并将其注册到Web服务器的示例

// Define simple test page
XEmbeddedContent testHtml =
{
    35, "text/html",
    (const uint8_t*) "<html><body>Test page</body></html>"
};

// ...
XWebServer server;

server.AddHandler( make_shared<XEmbeddedContentHandler>( "test.html", &testHtml ) );

就是这样——我们的Web服务器已准备好从嵌入式资源提供一些HTML内容。其他任何东西——样式、JavaScript、图像等——也都是这样做的。当然,这只是一个示例(仍然有效)。实际上,我不会花时间自己硬编码cam2web所需的所有Web内容。这就是为什么提供了web2h工具(前面提到过),它根据提供的Web文件生成包含XEmbeddedContent定义的头文件。

处理摄像头配置

另一个值得一提的Web请求处理程序是XObjectConfigurationRequestHandler——一个Web请求处理程序,它允许通过前面提到的IObjectConfigurator接口获取/设置对象的配置(属性)。这样,请求处理程序甚至不知道它配置的是什么——所有这些都对它隐藏起来。

// Request handler used to configure objects (camera objects)
class XObjectConfigurationRequestHandler : public IWebRequestHandler
{
public:
    XObjectConfigurationRequestHandler( const std::string& uri, 
                        const std::shared_ptr<IObjectConfigurator>& objectToConfig );

    void HandleHttpRequest( const IWebRequest& request, IWebResponse& response );

private:
    void HandleGet( const std::string& varsToGet, IWebResponse& response );
    void HandlePost( const std::string& body, IWebResponse& response );

private:
    std::shared_ptr<IObjectConfigurator> ObjectToConfig;
};

此请求处理程序的实现检查用于它的HTTP方法的类型。如果是GET请求,它会检索对象的配置并将其作为JSON响应提供。如果是POST请求,它会解析发布的JSON并尝试设置其中找到的任何属性。对于任何其他请求类型,它只是简单地说“抱歉,不允许此方法”。

处理JPEG和MJPEG请求

最后,我们来处理提供JPEG快照和MJEPS流的处理程序。这些由XVideoSourceToWeb类管理,该类可以处理任何实现IVideoSource接口的任意视频源,然后通过IWebRequestHandler接口提供JPEG和MJPEG处理程序。

namespace Private
{
    class XVideoSourceToWebData;
}

// Class providing a bridge between IVideoSource and IWebRequestHandler
class XVideoSourceToWeb : private Uncopyable
{
public:
    XVideoSourceToWeb( uint16_t jpegQuality = 85 );
    ~XVideoSourceToWeb( );
    
    // Get video source listener, which could be fed to some video source
    IVideoSourceListener* VideoSourceListener( ) const;

    // Create web request handler to provide camera images as JPEGs
    std::shared_ptr<IWebRequestHandler> 
         CreateJpegHandler( const std::string& uri ) const;

    // Create web request handler to provide camera images as MJPEG stream
    std::shared_ptr<IWebRequestHandler> CreateMjpegHandler( const std::string& uri,
                                                            uint32_t frameRate ) const;

    // Get/Set JPEG quality (valid only if camera provides uncompressed images)
    uint16_t JpegQuality( ) const;
    void SetJpegQuality( uint16_t quality );

private:
    Private::XVideoSourceToWebData* mData;
};

XVideoSourceToWeb::VideoSourceListener()方法提供了一个监听器,它被馈送到正在使用的任何视频源。还记得IVideoSource::SetListener()方法吗?这允许XVideoSourceToWeb从视频源获取视频帧。然后,XVideoSourceToWeb::CreateJpegHandler()提供了一个Web请求处理程序,用于提供JPEG快照——如果视频源提供了已经压缩的图像数据,它只需将其输出到Web响应中;否则,它会先进行软件JPEG压缩,然后进行Web响应。

XVideoSourceToWeb::CreateMjpegHandler()方法执行类似操作,但提供Web请求处理程序以服务MJPEG流。第一张图像以类似于JPEG请求处理程序的方式提供(除了HTTP头略有不同),然后其他图像通过IWebRequestHandler::HandleTimer()回调提供,该回调通过IWebResponse::SetTimer()请求。

例如,对于树莓派摄像头,它可能看起来像这样

XWebServer               server;
XVideoSourceToWeb        video2web;
shared_ptr<XRaspiCamera> xcamera = XRaspiCamera::Create( );

// subscribe XVideoSourceToWeb object for video frame events
xcamera->SetListener( video2web.VideoSourceListener( ) );

// add JPEG and MJPEG handlers to the web server
server.AddHandler( video2web.CreateJpegHandler( "/camera/jpeg" ) ).
       AddHandler( video2web.CreateMjpegHandler( "/camera/mjpeg", 30 ) );

通过上述架构,添加对新型摄像头或任何视频源的支持变得异常容易。我们所需要做的就是为特定摄像头提供IVideoSource接口的实现,然后XVideoSourceToWeb将完成其余的工作(前提是我们能从摄像头获取RGB24或压缩数据)。

XVideoSourceToWeb

访问控制

到目前为止,我们已经了解了如何配置XWebServer来从指定文件夹提供静态内容,或者通过IWebRequestHandler接口的不同实现来提供动态内容。但是,对于控制谁可以访问什么,我们还没有提及。我们不想让我们的摄像头暴露给公众,不是吗?

访问控制是通过用户组实现的。添加Web请求处理程序时,需要指定哪些用户组可以访问它。如果是UserGroup::Anyone,则任何人都可以访问该处理程序。但是,如果是UserGroup::UserUserGroup::Admin,则用户必须经过身份验证并属于所需组。所有这些都由XWebServer检查。当它找到某个URI的请求处理程序时,它首先检查当前用户是否允许访问它。如果允许,则调用处理程序并执行其工作。如果不允许,Web服务器会发送摘要身份验证请求。

这是一个快速示例,展示了如何允许所有人访问一些嵌入式Web内容,用户查看摄像头,以及管理员更改其设置

webServer.AddHandler( make_shared<XEmbeddedContentHandler>
                    ( "index.html", &web_index_html ),
                      UserGroup::Anyone ).
          AddHandler( make_shared<XEmbeddedContentHandler>
                    ( "styles.css", &web_styles_css ),
                      UserGroup::Anyone ).
          AddHandler( video2web.CreateJpegHandler( "/camera/jpeg" ),
                      UserGroup::User ).
          AddHandler( video2web.CreateMjpegHandler( "/camera/mjpeg", 30 ),
                      UserGroup::User ).
          AddHandler( make_shared<XObjectConfigurationRequestHandler>( "/camera/config",
                      cameraConfig ), UserGroup::Admin );

当然,为了使其工作,需要添加一些用户。XWebServer API提供了添加单个用户或从具有htdigest格式的文件加载用户的方法。

Web UI

cam2web的Web部分保持非常简单——一个非常基本的HTML页面和一些使用jQuery的JavaScript,用于动态加载摄像头配置页面,然后发送GET/POST请求以获取/设置摄像头的属性。jQuery Mobile也用于获取一些在不同Web浏览器中看起来一致的用户控件。

默认情况下,Web UI尝试直接通过图像元素显示MJPEG流。但是,并非所有Web浏览器都支持此功能。Firefox和Chrome支持,IE不支持。因此,如果MJPEG流无法启动,Web UI将切换到JPEG模式,此时图像元素会连续刷新JPEG快照。详细信息可以在camera.js中找到。

结论

好吧,最初只是一个旨在用于树莓派业余机器人项目的小项目,现在已经发展成为一个更大的项目,它可能会发挥很好的作用,并应用于不同的应用程序。例如,有些人可能决定在家中使用传统的USB摄像头或分散在家中的树莓派构建自己的视频监控系统。或者只是从工作中监控单个摄像头,以确保您的房子仍在原地。

该项目的部分内容可以轻松地在许多不同的应用程序中重用。例如,访问摄像头的代码可以轻松集成到任何处理视频的应用程序中。同样,mongoose Web服务器包装器也是如此——C++ API提供了一种更简单的方式来处理需要嵌入式Web服务器支持的应用程序中的HTTP请求。

如前所述,该项目已发布在GitHub上。因此其代码可供学习或重用(前提是遵守许可证)。欢迎任何代码评论、错误报告、修复等。

历史

2017年8月30日 - 1.1.0版本

  • REST API已更新,提供摄像头属性的描述(对/camera/properties进行GET请求)。此描述现在允许拥有通用的WebUI,用于渲染所有受支持API(DirectShow、V4L、MMAL)的摄像头属性。
  • 添加了新的配置选项以指定流式摄像头的名称,该名称显示在WebUI中。
  • Windows:添加了配置选项以覆盖默认摄像头帧率。它允许从一系列支持的帧率中进行选择。注意:并非所有帧率值都有效。这是由于DirectShow API的限制以及一些糟糕的摄像头驱动程序造成的,它们不关心自己报告了什么。
  • Windows:主应用程序窗口现在显示实际的摄像头帧率。
  • Windows:如果CPU支持SSSE3指令,则使用SSSE3指令优化BGR到RGB的转换。
  • Windows:主窗口图标和系统托盘图标(如果最小化)显示Web活动指示——当流式摄像头被访问时。
  • Windows:添加了一个选项,允许使用文件夹选择表单指定自定义WebUI的文件夹。
  • Windows:添加了配置选项以指定窗口/托盘图标的颜色。这使得区分流式传输不同摄像头的多个应用程序实例变得更容易。

2017年7月30日 - 项目首次公开发布

© . All rights reserved.