【问题标题】:Windows compatibility with the Sony Camera Remote APIWindows 与 Sony Camera Remote API 的兼容性
【发布时间】:2014-01-24 18:56:21
【问题描述】:

在“如何使用摄像头远程 API 开发应用程序”教程中,它指出“摄像头远程 API 使用基于 HTTP 的 JSON-RPC。因此,您可以将摄像头远程 API 用于任何操作系统,例如 Android、IOS或 Microsoft® Windows®。”这是有道理的,因为协议与平台无关。但是,在此页面上的相机兼容性图表中:http://developer.sony.com/develop/cameras/ 声明必须安装 Sony Smart Remote Control App 才能“启用 API 的使用”。由于该应用只有 iOS 和 Android,这是否意味着这些 API 不能在 Windows 上使用?

我对为 Windows 8 平板电脑和 Windows 8 手机开发远程控制应用程序非常感兴趣。但如果我无法控制 A5000、A7R、A7、NEX-6、NEX-5R 或 NEX-5T,那么它就变得没那么有趣了。

是否可以通过纯 HTTP JSON 通信来控制这些摄像头?

谢谢

【问题讨论】:

  • 我刚刚下载了 API,它包含的只是 REST 文档和一个使用它的示例 android 应用程序。因此,只要您遵循他们的 REST 规范,操作系统似乎并不重要。你试过了吗?
  • 感谢您提供的信息。不,我还没试过。我计划很快潜入,但被比较图表中的那个注释推迟了。如果您在 Windows 上取得成功,我很想知道。

标签: windows camera windows-store-apps sony


【解决方案1】:

我不知道你是否解决了你的问题,但我有同样的问题,我设法让它以某种方式与 C++ 一起工作。我花了一些时间才弄清楚我必须做什么,我从来没有做过任何 HTTP 的东西,甚至是开发较少的即插即用驱动程序,所以我将逐步解释我是如何做到的,正如我希望我被解释过的那样。

在消息的末尾我提供了一个指向我的整个文件的链接,请随意尝试。

我使用 boost asio 库来解决每个与网络相关的问题,甚至更多(一切都是异步的,这是一个很棒的库,但对于像我这样无知的人来说很难掌握......)。我的大部分函数都是从文档中的示例部分复制粘贴的,这解释了为什么我的代码在某些地方很尴尬。这是我的主要功能,没什么特别的,我实例化了一个 asio::io_service,创建我的对象(我错误地命名为 multicast_manager)然后运行该服务:

#include <bunch_of_stuff>    
using namespace std;
namespace basio = boost::asio;    

int main(int argc, char* argv[]) {
    try {
        basio::io_service io_service;
        multicast_manager m(io_service, basio::ip::address::from_string("239.255.255.250"));
        io_service.run();
        m.parse_description();
        m.start_liveview();
        io_service.reset();
        io_service.run();
        m.get_live_image();
        io_service.reset();
        io_service.run();   
    } catch (const std::exception& e)   {
        std::cerr << "Exception: " << e.what() << "\n";
    }
    return 0;
}

通过 ssdp 发现相机

首先,我们必须使用其 upnp (universal plug and play) 功能连接到相机。原理是每个upnp设备都在监听组播端口230.255.255.250:1900的M-SEARCH请求。这意味着如果您向此地址发送正确的消息,设备将通过告诉您它存在来回答,并为您提供使用它的信息。文档中给出了正确的消息。我在这样做时遇到了两个陷阱:首先,我省略了在消息末尾添加换行符,如the http standard 中所指定。所以你要发送的消息可以这样构建:

multicast_manager(basio::io_service& io_service, const basio::ip::address& multicast_address)
    : endpoint_(multicast_address, 1900),
    socket_(io_service, endpoint_.protocol())
{
    stringstream os;
    os << "M-SEARCH * HTTP/1.1\r\n";
    os << "HOST: 239.255.255.250:1900\r\n";
    os << "MAN: \"ssdp:discover\"\r\n";
    os << "MX: 4\r\n";
    os << "ST: urn:schemas-sony-com:service:ScalarWebAPI:1\r\n";
    os << "\r\n";
    message_ = os.str();
    // ...

这部分重要的第二件事是检查消息是否发送到正确的网络接口。在我的情况下,即使它被禁用,它也会通过我的以太网卡出去,直到我更改了套接字中的正确选项,我用以下代码解决了这个问题:

    // ...
    socket_.set_option(basio::ip::multicast::outbound_interface(
                            basio::ip::address_v4::from_string("10.0.1.1")));
    socket_.async_send_to(
                basio::buffer(message_), endpoint_,
                boost::bind(&multicast_manager::handle_send_to, this,
                            basio::placeholders::error));       
}

现在我们听。我们从哪里听你可能会问你是否像我一样?什么端口,什么地址?好吧,我们不在乎:问题是,当我们发送消息时,我们定义了一个目标 ip 和端口(在端点构造​​函数中)。我们不一定要定义任何本地地址,它是我们自己的 ip 地址(事实上,我们确实定义了它,但只是为了让它知道可以选择哪个网络接口);而且我们没有定义任何本地端口,它实际上是自动选择的(我猜是操作系统?)。无论如何,重要的部分是任何收听多播组的人都会收到我们的消息并知道其来源,并将直接响应正确的 ip 和端口。所以这里不需要指定任何东西,不需要创建一个新的套接字,我们只需在一个瓶子里监听我们发送消息的同一个套接字:

void handle_send_to(const boost::system::error_code& error)
{
    if (!error) {
        socket_.async_receive(asio::buffer(data_),
                boost::bind(&multicast_manager::handle_read_header, this,
                        basio::placeholders::error,
                        basio::placeholders::bytes_transferred));
    }
}

如果一切顺利,答案如下:

HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
EXT: 
LOCATION: http://10.0.0.1:64321/DmsRmtDesc.xml
SERVER: UPnP/1.0 SonyImagingDevice/1.0
ST: urn:schemas-sony-com:service:ScalarWebAPI:1
USN: uuid:00000000-0005-0010-8000-10a5d09bbeda::urn:schemas-sony-com:service:ScalarWebAPI:1
X-AV-Physical-Unit-Info: pa=""; pl=;
X-AV-Server-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="SonyImagingDevice"; mv="1.0";

为了解析这条消息,我重用了来自 boost http client example 的解析,但我一次完成了它,因为由于某种原因我无法使用 UDP 套接字执行 async_read_until。无论如何,重要的是相机收到了我们的信息;另一个重要部分是描述文件 DmsRmtDesc.xml 的位置。

检索和读取描述文件

我们需要获取 DmsRmtDesc.xml。这次我们将在指定的 IP 地址和端口处直接向摄像机发送 GET 请求。这个请求类似于:

GET /DmsRmtDesc.xml HTTP/1.1
Host: 10.0.0.1
Accept: */*
Connection: close

不要忘记额外的空行。我不知道 Connection:close 是什么意思。接受行指定您接受的答案的应用类型,这里我们将接受任何答案。我使用 boost http 客户端示例获取了文件,基本上我打开了一个到 10.0.0.1:64321 的套接字并接收到后面是文件内容的 HTPP 标头。现在我们有了一个 xml 文件,其中包含我们要使用的 Web 服务的地址。让我们再次使用 boost 解析它,我们要检索摄像头服务地址,也许是实时取景流地址:

namespace bpt = boost::property_tree;
bpt::ptree pt;
bpt::read_xml(content, pt);
liveview_url = pt.get<string>("root.device.av:X_ScalarWebAPI_DeviceInfo.av:X_ScalarWebAPI_ImagingDevice.av:X_ScalarWebAPI_LiveView_URL");
for (bpt::ptree::value_type &v : pt.get_child("root.device.av:X_ScalarWebAPI_DeviceInfo.av:X_ScalarWebAPI_ServiceList")) {
    string service = v.second.get<string>("av:X_ScalarWebAPI_ServiceType");
    if (service == "camera")
        camera_service_url = v.second.get<string>("av:X_ScalarWebAPI_ActionList_URL");
}

完成后,我们可以开始向相机发送实际命令,并使用 API。

向相机发送命令

这个想法很简单,我们使用文档中提供的 json 格式构建命令,并通过 POST http 请求将其发送到相机服务。我们将启动 liveview 模式,因此我们发送 POST 请求(我们最终将不得不使用 boost property_tree 来构建我们的 json 字符串,这里我是手动完成的):

POST /sony/camera HTTP/1.1
Accept: application/json-rpc
Content-Length: 70
Content-Type: application/json-rpc
Host:http://10.0.0.1:10000/sony

{"method": "startLiveview","params" : [],"id" : 1,"version" : "1.0"}

我们将其发送到 10.0.0.1:10000 并等待答复:

HTTP/1.1 200 OK 
Connection: close 
Content-Length: 119 
Content-Type: application/json

{"id":1,"result":["http://10.0.0.1:60152/liveview.JPG?%211234%21http%2dget%3a%2a%3aimage%2fjpeg%3a%2a%21%21%21%21%21"]}

我们第二次得到liveview url,我不知道哪个更好,它们是相同的......

无论如何,现在我们知道如何向相机发送命令并检索它的答案,我们仍然需要获取图像流。

从实时取景流中获取图像

我们有 liveview url,我们有 API 参考指南中的规范。首先,我们要求相机向我们发送流,因此我们向 10.0.0.1:60152 发送 GET 请求:

GET /liveview.JPG?%211234%21http%2dget%3a%2a%3aimage%2fjpeg%3a%2a%21%21%21%21%21 HTTP/1.1
Accept: image/jpeg
Host: 10.0.0.1

我们等待答案,这应该不会花很长时间。答案从通常的 HTTP 标头开始:

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Pragma: no-cache
CACHE-CONTROL: no-cache
Content-Type: image/jpeg
transferMode.dlna.org: Interactive
Connection: Keep-Alive
Date: Wed, 09 Jul 2014 14:13:13 GMT
Server: UPnP/1.0 SonyImagingDevice/1.0

根据文档,这应该直接跟在 liveview 数据流之后,理论上包含在:

  • 8 字节的公共标头指定我们是否确实处于实时取景模式。
  • 128 字节的有效载荷数据给出了 jpg 数据的大小。
  • n 字节的 jpeg 数据。

然后我们再次获得公共标头,无限期地直到我们关闭套接字。

在我的例子中,公共标头以“88\r\n”开头,所以我不得不丢弃它,而 jpg 数据之后是 10 个额外字节,然后才切换到下一帧,所以我不得不将其纳入帐户。我还必须自动检测 jpg 图像的开头,因为 jpg 数据以包含我忽略其含义的数字的文本开头。这些错误很可能是由于我做错了什么,或者我不了解我在这里使用的技术。

我的代码现在可以运行,但最后几位非常临时,它肯定需要更好的检查。

它还需要大量重构才能使用,但它显示了我猜每个步骤的工作原理......

Here is the entire file if you want to try it out. And here is a working VS project on github.

【讨论】:

  • 为避免您遇到的解析和格式化问题,我建议使用 Boost.Beast 进行 http 请求/响应处理。
【解决方案2】:

感谢您的询问。

在 A5000、A7R、A7、NEX-6、NEX-5T、NEX-5R 相机中,安装以下应用程序。 https://www.playmemoriescameraapps.com/portal/usbdetail.php?eid=IS9104-NPIA09014_00-F00002 此应用程序将安装在相机中并启动。

现在您可以使用“Camera Remote API”从任何操作系统控制上述相机。

【讨论】:

  • 啊!这就解释了!非常感谢。我期待着完成这项工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多