Closed usagi closed 10 years ago
modeを次のように追加します。
モード名、 main- と reciever+ にしましょう。内部的なシンボルとしては main_m1 と reciever_p1 にしましょう。
まて、UDP/IPでそのままじゃ1枚900KBもの画像2枚をそのまま送受信するなんて無理。
UDP/IPv4のパケット限界値は64KBなので、通信状況が理論値並に良好と仮定しても900KBの送信は15パケット必要。2枚分では29パケット必要。
送受信専用の型を用意してデータにシーケンシャルにカウントアップする番号を付与して飛んだら通信エラーによるフレームドロップとするか、再送要求をして補完する必要がある。再送要求まで実装するくらいならTCP/IPv4にした方が楽。
或いはUDP/IPv6にすればジャンボグラムとやら最大4GBまでのパケットを扱えるらしいが、調査しないとわかりません。
http://takuponv6.cocolog-nifty.com/blog/2008/10/ipv6-5rfc2471-1.html
今回は特殊化されたネットワークを使うわけではないのでUDP/IPv6のジャンボグラムは忘れましょう。一般的なネットワークは巨大なMTUなんてサポートしてない。
レイテンシーの都合からTCP/IPは使いたくない。
それと、無圧縮のBGRデータストリームとか、2枚転送するだけで14.0625Mbit必要。30FPSならば当然30倍の帯域が必要となるので、421.875Mbps必要。これはギガビットイーサのでなければ確保できない帯域。ギガビットイーサの一般的な実効帯域は650Mbps程度です。
画像の圧縮コストが遅延になるとはいえ、画像を圧縮しなければ一般的なネットワークでこの帯域確保は無理でしょう。
top.1.mp4の圧縮結果
-rw-r--r-- 1 usagi usagi 901K 2014-01-29T11:40:51 test.bmp
-rw-r--r-- 1 usagi usagi 471K 2014-01-29T11:39:33 test.png
-rw-r--r-- 1 usagi usagi 8.2K 2014-01-29T11:53:04 test.0.jpg
-rw-r--r-- 1 usagi usagi 17K 2014-01-29T11:52:44 test.10.jpg
-rw-r--r-- 1 usagi usagi 27K 2014-01-29T11:52:30 test.20.jpg
-rw-r--r-- 1 usagi usagi 35K 2014-01-29T11:52:04 test.30.jpg
-rw-r--r-- 1 usagi usagi 42K 2014-01-29T11:51:49 test.40.jpg
-rw-r--r-- 1 usagi usagi 48K 2014-01-29T11:51:33 test.50.jpg
-rw-r--r-- 1 usagi usagi 56K 2014-01-29T11:49:05 test.60.jpg
-rw-r--r-- 1 usagi usagi 67K 2014-01-29T11:48:48 test.70.jpg
-rw-r--r-- 1 usagi usagi 85K 2014-01-29T11:48:27 test.80.jpg
-rw-r--r-- 1 usagi usagi 124K 2014-01-29T11:40:23 test.90.jpg
-rw-r--r-- 1 usagi usagi 401K 2014-01-29T11:39:58 test.100.jpg
[KB/frame] | [KB/2-frames] | [packets] | [KB/sec] | [MB/sec] | [Mbps] | Ether-class | |
---|---|---|---|---|---|---|---|
test.bmp | 901 | 1,802 | 28.16 | 54,060 | 52.79 | 422.34375 | 1000Base |
test.png | 471 | 942 | 14.72 | 28,260 | 27.60 | 220.78125 | 1000Base |
test.100.jpg | 401 | 802 | 12.53 | 24,060 | 23.50 | 187.96875 | 1000Base |
test.90.jpg | 124 | 248 | 3.88 | 7,440 | 7.27 | 58.125 | 100base |
test.80.jpg | 85 | 170 | 2.66 | 5,100 | 4.98 | 39.84375 | 100base |
test.70.jpg | 67 | 134 | 2.09 | 4,020 | 3.93 | 31.40625 | 100base |
test.60.jpg | 56 | 112 | 1.75 | 3,360 | 3.28 | 26.25 | 100base |
test.50.jpg | 48 | 96 | 1.50 | 2,880 | 2.81 | 22.5 | 100base |
test.40.jpg | 42 | 84 | 1.31 | 2,520 | 2.46 | 19.6875 | 100base |
test.30.jpg | 35 | 70 | 1.09 | 2,100 | 2.05 | 16.40625 | 100base |
test.20.jpg | 27 | 54 | 0.84 | 1,620 | 1.58 | 12.65625 | 100base |
test.10.jpg | 17 | 34 | 0.53 | 1,020 | 1.00 | 7.96875 | 100base |
test.0.jpg | 8 | 16 | 0.26 | 492 | 0.48 | 3.84375 | 10Base |
暫定仕様:
sequence_idは30FPSで進行するとして256個を消費するには8.533秒を要する。受信側でこの時間より古いデータは破棄するようにすれば事実上問題は起こらないと考えられる。
struct frame_packet_t
{
uint16_t data_size;
uint8_t capture_id;
uint8_t sequence_id;
uint8_t data[65535-(16+8+8)/8];
};
こんなの用意しといて送信時も受診時も使ったらいい。
OpenCV内蔵のJPEGエンコーダー、デコーダーが遅すぎなければこれを使いましょう。新たにImageMagickを依存ライブラリーに追加する状況でもありませんし。
これ以上は時間無い。仕事終わったら再開する。たぶん実装する納期が今日中なら間に合うが問題はそれを @arisin が使う事、またテスト次第ではおそらくUDP/IPのパケットロスが問題になりパケットサイズの制限が必要になる。何れ続きはあとだ。
すみません。よろしくお願いします。
http://melpon.org/wandbox/permlink/3CrKngJwJo8iTWdb
現実としてloclahost的にはUDP/IPv4で32753*sizeof(uint16_t)=65506bytesのパケットを扱えるようだから、パケットの最大サイズは65506byte。
これにcapture_idは必須だから1byte減って、sequence_idも欲しいから1byte減って、data_sizeで2bytes減って、合計で4bytes減るから、65502bytesまでの画像データ本体部分まで送信可能、という事になる。
network-common.hxx
#pragma once
#include <vector>
#include <array>
#include <opencv2/core/core.hpp>
namespace arisin
{
namespace etupirka
{
struct frame_packet_t final
{
using sequence_id_t = uint8_t;
uint16_t real_data_size;
uint8_t capture_id;
sequence_id_t sequence_id;
static constexpr size_t info_size = sizeof(real_data_size) - sizeof(capture_id) - sizeof(sequence_id);
static constexpr size_t data_size = 65506 - info_size;
static constexpr size_t this_size = info_size + data_size;
uint8_t data[data_size];
inline void set_data(const cv::Mat& m, const int jpeg_quality = 60)
{
std::vector<uint8_t> buffer;
cv::imencode(".jpg", m, buffer, { CV_IMWRITE_JPEG_QUALITY, jpeg_quality });
if(buffer.size() > data_size)
throw std::runtime_error("encoded frame size is over.");
std::copy(std::begin(buffer), std::end(buffer), &data[0]);
real_data_size = buffer.size();
}
inline uint8_t* data_begin(){ return &data[0]; }
inline uint8_t* data_end(){ return data_begin() + size_t(data_size); }
using mutate_array_t = std::array<uint8_t, this_size>;
inline const mutate_array_t& mutate_to_array() const
{ return *reinterpret_cast<const mutate_array_t*>(this); }
};
}
}
動作確認一切して無いけど実装完了しました。
ありがとうございます。
とりあえずClose。バグがあったら別途バグごとにIssueで。
58 から #61 や #64 の高速化を施しては居るが、Raspberry Piにおけるmainモードの実行速度が 14.752[sec/frame] = 0.067 [FPS] では100倍高速化できたとしても 6.7 [FPS]に留まり、現実的にmainモードを現状でRaspberry Piに実装する事は無謀と思える。
そこで、現在のmain/recieverのモードに加え、重い処理をmain側ではなくreciever側で行う新たな動作モードを追加する。