{"id":86,"date":"2009-08-29T17:55:42","date_gmt":"2009-08-29T07:55:42","guid":{"rendered":"http:\/\/www.computer-vision-software.com\/blog\/?p=86"},"modified":"2010-02-10T21:18:14","modified_gmt":"2010-02-10T11:18:14","slug":"cross-platform-solution-for-getting-mjpeg-stream-from-axis-ip-camera-axis-211m","status":"publish","type":"post","link":"http:\/\/www.computer-vision-software.com\/blog\/2009\/08\/cross-platform-solution-for-getting-mjpeg-stream-from-axis-ip-camera-axis-211m\/","title":{"rendered":"Cross-platform solution for getting MJPEG stream from AXIS ip-camera (AXIS 211M)"},"content":{"rendered":"<div style=\"text-align: left;\">\n<p style=\"text-align: left;\">This paper describes how-to get MJPEG stream from AXIS ip-camera in your C++ application. My approach is a cross-platform solution and much better than solution from <a onclick=\"javascript:pageTracker._trackPageview('\/outgoing\/www.computer-vision-software.com\/blog\/2009\/04\/how-to-get-mjpeg-stream-from-axis-ip-cameras-axis-211m-and-axis-214-ptz-as-camera-device-in-opencv-using-directshow\/');\"  href=\"http:\/\/www.computer-vision-software.com\/blog\/2009\/04\/how-to-get-mjpeg-stream-from-axis-ip-cameras-axis-211m-and-axis-214-ptz-as-camera-device-in-opencv-using-directshow\/\">http:\/\/www.computer-vision-software.com\/blog\/2009\/04\/how-to-get-mjpeg-stream-from-axis-ip-cameras-axis-211m-and-axis-214-ptz-as-camera-device-in-opencv-using-directshow\/<\/a>.<\/p>\n<p><!--more--><\/p>\n<p><strong>Dependencies<\/strong><\/p>\n<p>We used boost library (boost\/asio, <a onclick=\"javascript:pageTracker._trackPageview('\/outgoing\/www.boost.org\/');\"  href=\"http:\/\/www.boost.org\/\">http:\/\/www.boost.org<\/a>). There\u00a0are very useful network interfaces:<\/p>\n<ul>\n<li><span style=\"color: #003366;\"><em><strong>boost::asio::io_service<\/strong>: <\/em><span style=\"color: #000000;\">the <\/span><\/span>io_service class provides the core I\/O functionality for users of the asynchronous I\/O objects;<\/li>\n<li><span style=\"color: #003366;\"><strong><em>boost::asio::ip::tcp::socket<\/em><\/strong>: <span style=\"color: #000000;\">the <\/span><\/span>socket class has a function that will retrieve the remote endpoint;<\/li>\n<li><span style=\"color: #003366;\"><strong><em>boost::asio::ip::tcp::resolver<\/em><\/strong>:<\/span> the resolver class init net-connection.<\/li>\n<\/ul>\n<p><strong>Network interface implementing<\/strong><\/p>\n<p>I created few useful network functions over\u00a0&#8220;boost::asio&#8221;: connecting, sending and receiving packets.<\/p>\n<p>The network interface will be implemented via boost objects. Definitions are:<\/p>\n<p><span style=\"color: #003366;\"><em><strong>boost::asio::io_service<\/strong> m_ios;<br \/>\n<strong>boost::asio::ip::tcp::socket<\/strong> m_socket(m_ios);<br \/>\n<strong>boost::asio::ip::tcp::resolver<\/strong> m_resolver(m_ios);<\/em><\/span><\/p>\n<p>So, the functions like:<\/p>\n<pre class=\"python:nogutter\">void connect(void)\r\n{\r\n    boost::asio::ip::tcp::resolver::iterator\u00a0\u00a0 end_point;\r\n    boost::system::error_code\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  ecode;\r\n    boost::asio::ip::tcp::resolver::query\u00a0\u00a0\u00a0\u00a0 query(boost::asio::ip::tcp::v4(),\r\n                                                                 \u00a0\u00a0\u201c192.168.0.1\u201d, \u201c80\u201d);\r\n\r\n    end_point = m_resolver.resolve(query);\r\n    m_socket.connect(*iterator, ecode);\r\n\r\n    assert(!ecode);\r\n}\r\n\r\nint send(void *buffer, int buf_size)\r\n{\r\n    boost::system::error_code\u00a0\u00a0\u00a0ecode;\r\n    boost::uint32_t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0sent_size = 0;\r\n\r\n    sent_size = boost::asio::write(m_socket,\r\n                                             boost::asio::buffer(buffer, buffer_size),\r\n                                             boost::asio::transfer_all(),\r\n                                             ecode);\r\n    if(ecode)\r\n        printf(\"socket write operation failed\\n\");\r\n\r\n    return sent_size;\r\n}\r\n\r\nint receive(void *buffer, int buf_size)\r\n{\r\n    boost::system::error_code\u00a0\u00a0\u00a0ecode;\r\n    int\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 received_size = 0;\r\n\r\n    received_size = boost::asio::read(m_socket,\r\n                                                  boost::asio::buffer(buffer, buffer_size),\r\n                                                  boost::asio::transfer_at_least(1),\r\n                                                  ecode);\r\n\r\n    if(ecode)\r\n        printf(\"socket read operation failed\\n\");\r\n\r\n    return received_size;\r\n}<\/pre>\n<p>Also there is disconnect function, it is written in the same manner.<\/p>\n<p><em>Note: this is reductive version of code for better interpretation. <\/em><\/p>\n<p><strong>Getting JPEG frame from ip camera<br \/>\n<\/strong><\/p>\n<p>We should send request to the camera for getting mjpeg stream. The command like:<\/p>\n<p><span style=\"color: #003366;\"><em>\u201cGET \/axis-cgi\/mjpg\/video.cgi?resolution=&lt;width&gt;x&lt;height&gt;&amp;fps=&lt;fps&gt;\\r\\n\\r\\n\u201d,<\/em><\/span><\/p>\n<p>where:<\/p>\n<ul>\n<li><span style=\"color: #003366;\"><em><strong>&lt;width&gt;<\/strong><\/em><\/span> &#8211; width of requested frame;<\/li>\n<li><span style=\"color: #003366;\"><em><strong>&lt;height&gt;<\/strong><\/em><\/span> &#8211; height of requested frame;<\/li>\n<li><span style=\"color: #003366;\"><em><strong>&lt;fps&gt;<\/strong><\/em><\/span> &#8211; the requested number of frames per second;<\/li>\n<li><em><span style=\"color: #003366;\"><strong>\u201c\\r\\n\\r\\n<\/strong>\u201d<\/span><\/em> \u2013 end marker of request.<\/li>\n<\/ul>\n<p>Example is:<\/p>\n<p><span style=\"color: #003366;\"><em>\u201cGET \/axis-cgi\/mjpg\/video.cgi?resolution=640&#215;480&amp;fps=15\\r\\n\\r\\n\u201d<\/em><\/span><\/p>\n<p>The device sends response: 183 bytes. We need to check the response to make sure that is ok.<\/p>\n<p>E.g. successful response:<\/p>\n<p><span style=\"color: #003366;\"><em>\u201cHTTP\/1.0 200 OK\\r\\nCache-Control: no-cache\\r\\nPragma: no-cache\\r\\nExpires: Thu, 01 Dec 1994 16:00:00 GSM\\r\\nConnection: close\\r\\nContent-Type: multipart\/x-mixed-replace; boundary=&#8211;myboundary\u201d<\/em><\/span><\/p>\n<p>E.g. unsuccessful response:<\/p>\n<p><em><span style=\"color: #003366;\">\u201cHTTP\/1.0 501 Not implemented\\r\\nDate: Sat, 29 Aug 2009 14:00:10 GSM\\r\\nAccept-Ranges: bytes\\r\\nConnection: close\\r\\n\u2026\u201d<\/span><\/em><\/p>\n<p>As you can see, the successful response must contain <span style=\"color: #003366;\"><em>\u201cHTTP\/1.0 200 OK\u201d<\/em><\/span> string at beginning.<\/p>\n<p>The value of \u201cboundary\u201d (<span style=\"color: #003366;\"><em>\u201c&#8211;myboundary\u201d<\/em><\/span>) field is most important, because this string will be used as separator further.<\/p>\n<p>The frame will be\u00a0received part by part. The first packet size is 67 bytes it is meta-information. E.g.:<\/p>\n<p><span style=\"color: #003366;\"><em>\u201c&#8211;myboundary\\r\\nContent-Type: image\/jpeg\\r\\nContent-Length: 56296\\r\\n\\r\\n\u201d<\/em><\/span><\/p>\n<p><span style=\"color: #003366;\"><em>\u201c&#8211;myboundary\u201d<\/em><\/span> string at the begin is confirmation of\u00a0beginning of new frame. Also,\u00a0we should read and\u00a0save value of the field <span style=\"color: #003366;\"><em>\u201cContent-Length\u201d<\/em><\/span> (56296). It is size of\u00a0 compressed jpeg frame. The second and following packets are jpeg-picture essentially. We receive few packets with jpg data and save each packet to memory buffer and calculate total size of received packets, and if the total size is equal to value<em> <\/em>of <em><span style=\"color: #003366;\">\u201cContent-Length\u201d<\/span><\/em>,\u00a0it means\u00a0that full picture is\u00a0received and the memory buffer contains it. Now, you can save the memory buffer to the disc-storage (into \u201c123.jpg\u201d file e.g.), and open it with any graphic-viewer and make sure that is usual jpg image.<\/p>\n<p>Note: the jpeg picture should contain the end marker<span style=\"color: #003366;\"><em> \u201c\\r\\n\u201d<\/em><\/span> (2 bytes), so I recommend you to receive 2 bytes more.<\/p>\n<p>Next frame can be received with the same aproach: the first package size is 67 bytes\u2026<\/p>\n<p>Note:\u00a0 symbols <span style=\"color: #003366;\"><em>\u201c\\r\\n\u201d<\/em><\/span> \u2013 2 byte 0x0A and 0x0D accordingly.<\/p>\n<p>Also, I recommend to develop separated thread for getting JPG frames from ip camera in order to avoid losing of connection&#8230;<\/p>\n<p>We tested this solution under MS Windows, Linux, Intel P4 and also under Intel Atom and ARM Cortex-A8. All works fine.<\/p>\n<p>We\u00a0are using this approach in my module which decodes each JPG frame from Axis camera and convertes it to OpenCV IplImage (BGR frame).<\/p>\n<p>P.S. Besides, we developed solutions for Arecont and ACTi ip-cameras. Sure, the\u00a0implementations are different, but common idea\u00a0is the same.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>This paper describes how-to get MJPEG stream from AXIS ip-camera in your C++ application. My approach is a cross-platform solution and much better than solution from http:\/\/www.computer-vision-software.com\/blog\/2009\/04\/how-to-get-mjpeg-stream-from-axis-ip-cameras-axis-211m-and-axis-214-ptz-as-camera-device-in-opencv-using-directshow\/.<\/p>\n","protected":false},"author":30,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[84],"tags":[7,27,21,26,6],"class_list":["post-86","post","type-post","status-publish","format-standard","hentry","category-opencv","tag-arm","tag-ipcamera","tag-iplimage","tag-mjpeg","tag-opencv"],"_links":{"self":[{"href":"http:\/\/www.computer-vision-software.com\/blog\/wp-json\/wp\/v2\/posts\/86","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.computer-vision-software.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.computer-vision-software.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.computer-vision-software.com\/blog\/wp-json\/wp\/v2\/users\/30"}],"replies":[{"embeddable":true,"href":"http:\/\/www.computer-vision-software.com\/blog\/wp-json\/wp\/v2\/comments?post=86"}],"version-history":[{"count":0,"href":"http:\/\/www.computer-vision-software.com\/blog\/wp-json\/wp\/v2\/posts\/86\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.computer-vision-software.com\/blog\/wp-json\/wp\/v2\/media?parent=86"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.computer-vision-software.com\/blog\/wp-json\/wp\/v2\/categories?post=86"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.computer-vision-software.com\/blog\/wp-json\/wp\/v2\/tags?post=86"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}