openFramworksのofxOSCでは画像を送ることが可能だが、サンプルにも書かれている通り、一定以上のサイズはサンプルのコードでは送る事ができない。具体的には大体65000Byte以上くらいなのだが、これはOSCが利用しているUDPのペイロードサイズの制限(より正確にはIPパケット)によるものだ。ちょうどよい解説が@ITにあったので引用しておく。
1つのUDPパケットで運ぶことのできるデータ(「ペイロード(荷物)」という)の長さは、下位層のIPパケットの長さの制約を受ける。(標準の)IPパケットでは、1回の送信で、最大では65515bytes(65535bytesから、IPヘッダの最低サイズ20bytesを引いたもの)までのデータを送信することができる(IPヘッダ・オプションが付くと、さらに小さくなる)。そのため、1つのUDPパケットで送信することのできる最大ペイロード・サイズは、65515bytesからUDPヘッダのサイズ(8bytes)を減算した、65507bytesまでとなる。このため、この「長さ」フィールドの値は、8(データが空の場合)~65515となる。
基礎から学ぶWindowsネットワーク:第13回 データグラム通信を実現するUDPプロトコル (3/4) - @IT
今回、これを超える大きさのデータのやり取りをしたいと思って実装したので詳細を書いておく。oFのバージョンは0.9.8。
方法の概略
送信側でデータをサイズ制限以下に分割してOSCメッセージにして送信、受信側でデータを結合する。
OSCメッセージには、データ自体とデータの開始フラグと終了フラグをつけておくことで、受信側でデータをどのように結合すればいいか分かるようにした。
コード
全体のファイルはGithubに置いた。macOS 10.12.4 のXcode 8.3 で開発した。相手先のIPアドレスはsettings.xmlで設定する。
GitHub - torukawanabe/oscImageTransferExample
送信部
ofBuffer型の変数imgAsBufferに画像データが読み込まれている。他のデータを考慮しても確実に上記サイズ制限に収まるであろう60000バイトで分割している。
void ofApp::sendImage(){ img.load(imgAsBuffer); static const int dividingSize = 60000; int numOfSend = (imgAsBuffer.size() / dividingSize) + 1; cout << "NOW POST IMG SIZE:" << imgAsBuffer.size() << " ,NUM OF SEND:" << numOfSend << endl; if (numOfSend == 1) { ofxOscMessage m; m.setAddress("/image"); m.addBoolArg(true); m.addBoolArg(true); vector<char> sendBuff = vector<char>(imgAsBuffer.begin(), imgAsBuffer.end()); m.addBlobArg(ofBuffer(sendBuff.data(), sendBuff.size())); sender.sendMessage(m, false); }else{ vector<char>::iterator it = imgAsBuffer.begin(); for (int i=0; i<numOfSend; i++) { ofxOscMessage m; vector<char> sendBuff; sendBuff.clear(); m.setAddress("/image"); if(i == 0){ m.addBoolArg(true); m.addBoolArg(false); }else if(i == (numOfSend - 1)){ m.addBoolArg(false); m.addBoolArg(true); }else{ m.addBoolArg(false); m.addBoolArg(false); } for(int j=0; j<dividingSize; j++){ sendBuff.push_back(*it); if(it == imgAsBuffer.end()) break; it++; } cout << "sendBuff" << i << ":" << sendBuff.size() << endl; m.addBlobArg(ofBuffer(sendBuff.data(), sendBuff.size())); sender.sendMessage(m, false); } } cout << "ofApp:: sending image with size: " << imgAsBuffer.size() << endl; }
受信側
vector
void ofApp::updateOSC(){ while(receiver.hasWaitingMessages()){ // get the next message ofxOscMessage m; receiver.getNextMessage(m); if(m.getAddress() == "/image" ){ bool isFirst = m.getArgAsBool(0); bool isLast = m.getArgAsBool(1); if(isFirst){ receivedBuffer.clear(); } ofBuffer buff = m.getArgAsBlob(2); for (char c: buff){ receivedBuffer.push_back(c); } // ofBufferが最後に0を挿入するので末尾を削除 receivedBuffer.pop_back(); if(isLast){ ofBuffer imgBuff = ofBuffer(receivedBuffer.data(), receivedBuffer.size()); receivedImg.load(imgBuff); } } } }
注意点
ofBufferは内部で持つvector
*1:NULL終端のため