2014-01-24 69 views
3

在「如何使用Camera Remote API開發應用程序」中指出:「Camera Remote API使用HTTP上的JSON-RPC。因此,您可以將Camera Remote API與任何操作系統,如Android,IOS或Microsoft®Windows®。「這是合理的,因爲協議是平臺不可知的。但是,在此頁面的相機兼容性圖表中:http://developer.sony.com/develop/cameras/它聲明必須安裝Sony Smart Remote Control應用程序才能「啓用API使用」。由於該應用只有iOS和Android,這是否意味着API無法在Windows上使用?Windows與Sony Camera Remote API的兼容性

我非常希望爲Windows 8平板電腦開發一個遠程控制應用程序,然後開發Windows 8手機。但是,如果我無法控制A5000,A7R,A7,NEX-6,NEX-5R或NEX-5T,那麼它就不那麼有趣了。

是否可以通過普通的HTTP JSON通信控制這些攝像機?

謝謝

+0

我剛剛下載的API和所有它包含的是REST文檔和示例使用它的android應用程序。因此,只要遵循其REST規範,操作系統似乎並不重要。你試過了嗎? – Jon

+0

感謝您的信息。不,我還沒有嘗試過。我打算很快潛入,但在比較圖中被這個音符推遲了。我很想聽聽您是否在Windows上取得成功。 –

回答

8

我不知道你是否解決了你的問題,但我有同樣的問題,我設法使它以某種方式與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設備正在偵聽M-SEARCH請求的多播端口230.255.255.250:1900。這意味着如果您將正確的信息發送到此地址,設備將通過告訴您它是否存在來回答,併爲您提供使用信息。文檔中給出了正確的信息。我遇到了兩個陷阱:首先,我省略了在消息結尾添加換行符,如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和端口。因此,沒有必要在這裏說明什麼,沒有必要建立一個新的socket,我們只是聽我們在一個瓶子發送我們的信息相同的插座:

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。這次我們將直接發送一個GET請求到相機,在指定的IP地址和端口。此請求類似於:

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

不要忘記多餘的空行。我不知道連接是什麼:close意味着什麼。 accept線指定您接受的答案的應用程序類型,在這裏我們將採取任何答案。我使用boost http客戶端的例子得到了這個文件,基本上我打開一個socket到10.0.0.1:64321並接收HTPP頭文件,然後是文件內容。現在我們有一個xml文件,其中包含我們要使用的web服務的地址。讓我們再分析它使用升壓,我們要檢索的攝像機服務地址,也許實時取景的流地址:

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請求到相機服務發送。我們將推出實時取景模式,所以我們發送POST請求(我們最終將不得不使用升壓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"]} 

我們得到實時查看網址第二次,我不知道哪一個更好,它們是相同的...

無論如何,現在我們知道如何向攝像機發送命令並檢索其答案,我們仍然需要獲取圖像流。

從實時取景流

抓取的圖像我們有實時取景的網址,我們有API的參考指南中的規範。第一件事,第一,我們要求相機向我們發送流,所以我們發送GET請求來10.0.0.1:60152:

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 

我們等待答案,不應該長時間。答案開始與通常HTTTP頭:

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 

根據文檔,這應該是直接跟着實時取景的數據流至極在於理論:公共報頭指定如果

  • 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.