###【その2】ZOOM, Teamsなどのためのサブタイトル(字幕)表示ツール【受信・表示部】
###受信・表示プログラム
前回 https://qiita.com/quittardis/items/317df7b23a64e82bf6a2
の続きです。
前回の送信プログラムでudp経由で送られてきた全角漢字をカメラ画像にスーパー
インポーズして字幕として表示します。さらにこの画像をSpoutで仮想カメラ化して
ZOOMやTeamsで使えるようにします。
ソースコードのbugfix版はhttps://github.com/ultrahamlet/SjFX のsrcフォルダに置きました。
(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でも設定できる。