tiny dots

裏方の日々

MotionJPEGの罠

誰でも一度はお世話になる神アドオンことofxIpVideoGrabberでちょっとハマったのでメモ。

お題は向こうが独自実装したMotionJPEG配信サーバーと繋いで動画を表示するというもの。
実装の参考にしたのは

とのこと。ちなみにRack製。

以下ハマりポイント。まずはブラウザで検証。

  • URL直叩きして表示されるものと表示ないものがある
  • img要素に食わせても表示される時とされない時がある
  • 表示されない時もエラーは出ていない

次にofxIpVideoGrabberのexampleで検証。

  • レスポンスは帰ってきておりConnection Errorになっていない
  • 他に比べて受信データが大きい

一応ofImageにも食わせてみたけどダメでした。
で、まずはRackが返すヘッダを疑ってみるとどうもステータスコードとContent-Lengthが含まれていないっぽい。唯一正常に表示されるFirefoxは勝手に200と解釈してる様子。とりあえずこの点を修正してもらうもハズレの模様。
もう一度ヘッダを見てみるとバウンダリ部分がboundary= -- boundaryみたいに変なスペースが入っており、このへんの処理が上手くいっていないのかなと。しかしここと直してもらってもだめ。。
受信データのkb/secが増え続けている様子からチャンクを正しく区切れていないことは明らかなのだが、、、あとは改行コードくらいか?と思いチェックしてもらう。
するとどうも改行とキャリッジリターンが実装されてなかったことが判明。これを修正してもらうと...繋がった。。。どおりで延々受信し続けるわけだ...。
というかFirefoxは勝手にチャンクを解釈して表示してたってことか?独自解釈すぎでは?と思ったけどMotionJPEG自体オワコンかつブラウザ任せの規格なのでしかたないのか。

まとめとしては結局MotionJPEGを配信する際の注意点になってしまうのだが、、、

  • 改行(\n)とキャリッジリターン(\r)を正しく実装する
  • バウンダリ文字列を正しく実装する

とかそのくらいか。oF関係ないわ。ハマったわりにズコーなオチでしたがだいたいそんなものですね。。

深度 + アルファブレンディングの闇

今やってる案件で、画像を円形のマスクで抜きつつ3D空間の中で動かす+パフォーマンス的にはFBOに突っ込みたいというお題があって↓のようなコードを書いていたがうまくいかず。

void ofApp::setup()
{
    ofEnableAlphaBlending();
    
    shader.load("mask");
    image.loadImage("image.jpg");
    
    fbo.allocate(ofGetWidth(), ofGetHeight(), GL_RGBA, 8);
    fbo.begin();
    {
        ofClear(0, 0);
    }
    fbo.end();

    mask.allocate(image.width, image.height, GL_RGBA, 8);
    mask.begin();
    {
        ofClear(0, 0);
        ofFill();
        ofSetColor(255, 255);
        ofCircle(mask.getWidth() * 0.5, mask.getHeight() * 0.5, 40);
    }
    mask.end();
}

void ofApp::update()
{
    fbo.begin();
    {
        ofEnableDepthTest();
        ofEnableAlphaBlending();
        
        ofClear(0, 0);
        
        maskShader.begin();
        {
            maskShader.setUniformTexture("mask", maskFbo.getTextureReference(), 1);
            
            ofSetColor(255, 255);
            image.draw(0, 0);
        }
        maskShader.end();
    }
    fbo.end();
}

void ofApp::draw()
{
    ofBackground(0, 0);
    fbo.draw(0, 0);    
}

シェーダーの中身はこんな感じ。

// mask.vert
#version 120

varying vec2 texCoordVarying;

void main()
{
    texCoordVarying = gl_MultiTexCoord0.xy;
    gl_Position = ftransform();
}
// mask.frag
#version 120

uniform sampler2DRect tex0;
uniform sampler2DRect mask;

varying vec2 texCoordVarying;

void main()
{
    vec4 texel0 = texture2DRect(tex0, texCoordVarying);
    vec4 texel1 = texture2DRect(mask, texCoordVarying);
    
    gl_FragColor = vec4(texel0.rgb, texel0.a * texel1.a);
}

これを実行すると↓な画ができあがる。んー。 f:id:hideyukisaito:20150712012022p:plain

フラグメントシェーダーの中でアルファテストをかけてみる。

if (texel1.a < 0.6) {
    discard;
}

すると、、、 f:id:hideyukisaito:20150712012256p:plain

ん〜おしい。。。ちなみに切り捨てるアルファ値を上げると見るに耐えないくらいエッジがジャギってしまい全然ダメ。 心が折れる寸前までいったのでバカにされる覚悟でof-slackに質問を投げてみたところ、けっこうこのトピックに悩まされる人は多いらしくあの手この手の解決方法を提案していただけました。 そのうちのひとつが深度テストをせずに自前でZ-Sortするというもので、わりと定石とのこと。コード的にはこんなん。

ofEasyCam cam;
vector<ofVec3f> positions;

bool sortByDistance(const pair<int, float> &left, const pair<int, float> &right)
{
    return left.second == right.second ? false : left.second > right.second;
}

deque< pair<int, float> > distances;
for (unsigned int i = 0; i < positions.size(); ++i) {
    // 単純な z ではなくカメラからの距離で計算する
    distances_.push_back(make_pair<int, float>(i, cam.getPosition().distance(positions[i])));
}

sort(distances.begin(), distances.end(), sortByDistance);

for (deque< pair<int, float> >::iterator it = distances.begin(); it != distances.end(); ++it) {
    ofCircle(positions[it->first], 50);
}

おーいい感じ! f:id:hideyukisaito:20150712013350p:plain

エッジの綺麗さはまだ課題だけど、まあ誤魔化しはきくレベル。
早速プロジェクトの方に移植してみるもなんかうまくいかない。。あーそいういえばカメラの方を回してたんだった。ということで

float angle;
ofVec3f axis;
cam.getRotation(angle, axis);

deque< pair<int, float> > distances;
for (unsigned int i = 0; i < positions.size(); ++i) {
    ofVec3f v = positions[i];
    v.rotate(angle, axis);
    distances_.push_back(make_pair<int, float>(i, cam.getPosition().distance(v)));
}

という具合にカメラの回転を考慮して計算してやると見事いい感じになった。今回は対象オブジェクトの数も少ないのでこの方法でいくことに決定。

他にもポイントスプライトを使って点の中心から一定距離のピクセルをdiscardするアプローチを教えてもらったりして目からウロコでした。

marina.sys.wakayama-u.ac.jp

あとソフトパーティクルとか

wgld.org

デプスピーリングとか

marina.sys.wakayama-u.ac.jp

奥が深いが闇も深い。なんにしろ積極的に質問投げるべきですね。。。
早くこれ完成形お披露目したいなー。

家族の風景

久しぶりにスカッと晴れた土曜日。築地で家族と待ち合わせて寿司を食べる。14時の待ち合わせが14時半になり、15時になり。。というテンポ感は昔から変わらず。
父のリクエストですしざんまい本店へ。築地まで来てわざわざざんまい...?と思いつつ、やはり寿司は寿司というだけでもう尊いものである。
食後、甘いものでもということで銀座の風月堂へ。スイーツ男児としてはパフェを頼みたかったけど、さすがに寿司の直後では胃が受け付けず。。

友人知人とはとてもしないような本当に他愛もない会話に、なぜか今日はどうしようもなく家族を感じる。昔は家族との実もないやりとりなんて不毛!くだらない!などと思っていたこともあったけど、結局は実のなさこそが家族なんだなと、実家を離れてみて分かるようになった。

ここ最近まったく気持ちが上向かずーというのは人間関係だったりお金の工面だったり仕事の忙しさだったり、端的に言うと他人が当たり前に出来ている(ように見える)ことが全然うまくできないなあという、いい歳こいて激しく自己嫌悪なムードにすっかり支配されていてー「明日のことは明日(生きてたら)考えよう」という感じで風呂も入らず着替えもせず洗濯物の散らかったベッドに倒れ込むという荒みまくった毎日を送っていたので、妙におセンチな気分で今日という日を噛み締めてしまった次第。

正直自分以外は誰もが上手くいっているように見える。最近はそもそも生きることに向いてないんじゃないか?とか、誰にも迷惑をかけずにこの世から消え去る方法ばかりを考えていた。
明日生きるのだって打ち合わせとか仕上げなきゃいけない作業があるから、、くらいしか理由がなくなりつつあった。まあ実際は多くの人がその程度の理由で生き続けているのかもしれないけど。。

家族に会ってそういう何やかんやがスパっと吹き飛ぶわけではないけど、決して楽ではない環境の中、自分をここまで育てる途中には死にたい、もうダメかもしれないと思うこともあっただろうし、父親なんかは生死の境を経験したのち後遺症を抱えてもなお日々を楽しく生きようと努力を続けている。というようなことに思いを馳せる機会ーチャンスと言ってもいいーを得ることができたのは、今現在の自分にとってとても有難いことだった。

みんなのように上手くはできないけど、頑張ってもう少し生き延びてみようと思った。そんな家族の風景。


ハナレグミ 家族の風景 - YouTube

気付けば

攻殻機動隊 新劇場版が今週公開。


6.20全国公開「攻殻機動隊 新劇場版」PV - YouTube

サントラ出てたので買った。コーネリアス新譜まだ?

むろみー!

昨日のJ-WAVE SPARKでかかってた上坂すみれ「七つの海よりキミの海」がすごかった。波打際のむろみさん観てなかったので知らなかった。。


上坂すみれ「七つの海よりキミの海」 - YouTube

ofxHeatMap

誰しも一度はoFでヒートマップを描きたいと思うものですが、そんな想いにこたえるアドオンを書きました。

github.com

まあ lucasb-eyer/heatmap · GitHub のラッパーなんですが。。
添付のgoスクリプト独自のカラースキームを生成できるのが地味に便利です。 おそらく今やってる案件が終わったら二度と使わないであろう。

邂逅

1月の三浦ライドで知り合って以来、いつか走りに行こうと連絡を取り合っていたS氏。念願の邂逅ということで宮ヶ瀬湖半〜裏ヤビツをアテンドさせて頂きました。 天気は快晴。30℃近いどピーカンにつき入水せずにはいられない。

#入水 #rapha #outsideisfree #earlysummer

そしていきなり頂上。

8時に矢野口集合でヤビツ到着が13時ちょいとなかなかいいペース。まだまだ登れるな?てなわけでぐるっと大垂水回りで帰ることに。結果、自己新記録更新です。

無茶する同士だということが分かったので今後もガシガシ攻めたい所存。