【その２】ZOOM, Teamsなどのためのサブタイトル（字幕）表示ツール【受信・表示プログラム】

###【その２】ZOOM, Teamsなどのためのサブタイトル（字幕）表示ツール【受信・表示部】

###受信・表示プログラム

　前回　https://qiita.com/quittardis/items/317df7b23a64e82bf6a2　
　の続きです。
　前回の送信プログラムでudp経由で送られてきた全角漢字をカメラ画像にスーパー
インポーズして字幕として表示します。さらにこの画像をSpoutで仮想カメラ化して
ZOOMやTeamsで使えるようにします。


**ソースコードのbugfix版はhttps://github.com/ultrahamlet/SjFX のsrcフォルダに置きました。**

![スクリーンショット 2021-02-15 115548.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430586/af0bfc76-bfd7-d8a4-8e45-3c8ed85b2ffa.jpeg)

  （buildが苦手という方は、このhttps://github.com/ultrahamlet/SjFX
gitにあるbinaryファイルで実験できる。
SjFX.exeが受信・表示プログラム。WindowsProject.exeが文字送信プログラム。
実験時にWebCamの接続とSpoutCamのインストールも忘れずに）
　
　先ず、SpoutSDK https://spout.zeal.co/ 
 をopenframeworksのプロジェクトフォルダと同じ階層に置きます。

openframeworksのアドオンは、

ofxNetworks
ofxXmlSettings

を追加します。


ofApp.hでinlcludeに以下の四行を追加します。

```
#include "ofxXmlSettings.h"
#include "..\..\SpoutSDK\SpoutSender.h"
#include <Windows.h>
#include "ofxNetwork.h"

```

###ofApp.h
ofApp.hでinlcludeに以下のように変数を追加します。

```
		ofVideoGrabber grabber;
		//
		char sendername[256];     // Sender name
		SpoutSender sender;    // Spout sender object
		unsigned int senderwidth;  // Dimensions of the sender can be independent
		unsigned int senderheight; // of the application window if using an fbo
		ofFbo  oFbo;
		ofFbo spFbo;           // For texture send example
		// udp FONT
		ofxUDPManager udpConnectionRx;
		ofxUDPManager udpConnectionTx;

		string rxMessage;
		string txMessage;
		string recvStr;
		string ostr;
		BOOL received;
		ofTrueTypeFont font;
```

ofApp.cppの#include "ofApp.h"以下の行を追加します。
主に漢字コードを変換する関数です。


```
#include <codecvt>
//--------------------------------------------------------------
std::wstring multi_to_wide_capi(std::string const& src)
{
	std::size_t converted{};
	std::vector<wchar_t> dest(src.size(), L'\0');
	if (::_mbstowcs_s_l(&converted, dest.data(), dest.size(), src.data(), _TRUNCATE, ::_create_locale(LC_ALL, "jpn")) != 0) {
		throw std::system_error{ errno, std::system_category() };
	}
	dest.resize(std::char_traits<wchar_t>::length(dest.data()));
	dest.shrink_to_fit();
	return std::wstring(dest.begin(), dest.end());
}
std::string wide_to_utf8_cppapi(std::wstring const& src)
{
	std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
	return converter.to_bytes(src);
}

std::string multi_to_utf8_cppapi(std::string const& src)
{
	auto const wide = multi_to_wide_capi(src);
	return wide_to_utf8_cppapi(wide);
}

std::string toUtf8(const std::wstring &str)
{
	std::string ret;
	int len = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0, NULL, NULL);
	if (len > 0)
	{
		ret.resize(len);
		WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len, NULL, NULL);
	}
	return ret;
}
```
###ofApp::setup()
void ofApp::setup()に以下の行を追加します。
フォントの読み込みと、
udpの接続の設定です。
フォントはdataフォルダの下のFotnsにWindows/Fontsからコピーしておきます。


```
grabber.setup(ofGetWidth(), ofGetHeight());
	
	senderwidth = ofGetWidth();
	senderheight = ofGetHeight();
	// Create an RGBA fbo for texture transfers
	spFbo.allocate(senderwidth, senderheight, GL_RGBA);
	oFbo.allocate(ofGetWidth(), ofGetHeight(), GL_RGBA);
	//oFbo.getTextureReference().getTextureData().bFlipTexture = true;
	
	// If no name is specified, the executable name is used.
	sender.SetSenderName(sendername);
	// Update caption in case of multiples of the same sender
	ofSetWindowTitle(sender.GetName());
	//
	// Font setting
	ofTrueTypeFontSettings fsettings("Fonts/meiryo.ttc", 24);
	fsettings.addRanges(ofAlphabet::Emoji);//絵文字
	fsettings.addRanges(ofAlphabet::Japanese);//日本語
	fsettings.addRange(ofUnicode::Space);//スペース
	fsettings.addRange(ofUnicode::IdeographicSpace);//全角スペース
	fsettings.addRange(ofUnicode::Latin);//アルファベット等
	fsettings.addRange(ofUnicode::Latin1Supplement);//記号、アクサン付き文字など
	fsettings.addRange(ofUnicode::NumberForms);//数字？
	fsettings.addRange(ofUnicode::Arrows);//矢印
	fsettings.addRange(ofUnicode::MathOperators);//数式記号
	fsettings.addRange(ofUnicode::Hiragana);//ひらがな
	fsettings.addRange(ofUnicode::Katakana);//カタカナ
	fsettings.addRange(ofUnicode::MiscSymbolsAndPictographs);//絵文字など
	fsettings.addRange(ofUnicode::Emoticons);//エモーティコン
	//settings.addRange(ofUnicode::KatakanaHalfAndFullwidthForms);//エモーティコン
	//
	if (!font.load(fsettings))
		cout << "couldn't load font" << endl;
	//
	// udp connect
	udpConnectionRx.Create();
	udpConnectionRx.Bind(12345); //incomming data on my port # ...  
	udpConnectionRx.SetNonBlocking(true);

	received = false;

```

###ofApp::update()
void ofApp::update()を以下のリストを追加します。
udpデータを待ち受け受信します。
受信したデータは、recvStrにストアします。


```

grabber.update();

	// UDP 受信
	char data[512];
	//unsigned short int *val;
	udpConnectionRx.Receive(data, 512);
	//std::cout << str << "-" <<  std::endl;
	uint16_t *val = (uint16_t*)data;
	//std::cout << val[0] << std::endl;
	BOOL strOK = true;
	for (int i = 0; i < sizeof(val); i++) {
		if (val[i] < 256) { strOK = false; break; }
	}
	if (strOK) {
		recvStr = data;
		received = true;
		std::cout << recvStr << std::endl;
		// shift-jis -> utf8
		ostr = multi_to_utf8_cppapi(recvStr);
		std::cout <<  "Received" << std::endl;
	}
```
### ofApp::draw()
void ofApp::draw()に以下のリストを追加します。

受信したデータを表示し、Spoutで仮想カメラに送信します。
**ソースコードのbugfix版はhttps://github.com/ultrahamlet/SjFX のsrcフォルダに置きました。以下のコードは、2月16日以降修正しています。それ以前のコードでは、SpoutCamに文字が出力されません。**

```
    oFbo.begin();
	
	 ofSetColor(255, 255, 255);
	 
	 grabber.draw(0, 0);
	// font.drawString(u8"漢字テスト", 0, 200);
	 if (received) font.drawString(ostr, 200, ofGetHeight()-30);
	oFbo.end();

	spFbo.begin();    
	//grabber.draw(0, 0);
	if (flipY) {
		ofScale(-1, 1);
		ofTranslate(-ofGetWidth(), 0);
	}
	oFbo.draw(0, 0);
	 sender.SendFbo(oFbo.getId(), senderwidth, senderheight, false);
	spFbo.end();

	oFbo.draw(0, 0);
```
### SpoutCamのインストール

 最後にSpoutCam-2.023をインストールします。
　SpoutCam-2.023\release\x64下の
　_register_run_as_admin.bat
　（zoom用に32bit版もインストールする。）
  を、管理者として実行します。
これで、ZOOM、TeamsなどからカメラとしてSpoutを選択します。

###あっ忘れてました。
　dataフォルダ化に以下の内容のsttings.htmlを置いてください。
  ここにはカメラに最適の縦横の値を書いておきます。

```
<settings>
    <app>
        <width>960</width>
        <height>544</height>
    </app>
</settings>

```
　
gitのbinの中のMySetting.exeでも設定できる。