気が付いたら1週間経ってしまいました。今日は後半、画面表示部分です。ここを綺麗に作ってあれば、コメント取得部分を差し替えるだけで済んだのでしょうが、こっちも残念な品質です。ほぼ1から作り直したいです。元のコードはTVTest 0.6.4のVMR9 Renderlessモードでの描画部分です。ここで取り上げているコードは、改造後のDirectShowFilter/VMR9Renderless.cppのCVMR9Allocator::PresentHelper関数(442行目以降)より一部抜粋となります。


512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
static int pos = 0;
static std::vector<CommentInfo> comment;
{
  extern CCritSec MsgQueueLock;
  extern std::queue<std::wstring> MsgQueue;
  CommentInfo info;
  info.StartCount = GetTickCount();
  UINT i = 0;
  CAutoLock msgLock(&MsgQueueLock);
  while (!MsgQueue.empty())
  {
    info.Comment = MsgQueue.front();
    info.Y = pos;
    MsgQueue.pop();
    comment.push_back(info);
    info.StartCount += 200;
    pos += 80;
    if (pos >= 1000)
      pos = 0;
  }
}

ifは必ず括弧{}で囲むというここ数年の自分の書き方をやぶっているところが、時間なくて焦っている何よりの証ですね。posはコメントのy座標になります。とりあえず重ならなければいいやというやっつけ仕事の位置決めです。MsgQueueは、別スレッドで獲得したメッセージが放り込まれます。ヘッダファイルすら使わないというあたりからは、余裕のなさが伺えます。

532
533
534
535
536
537
static CComPtr<IDirect3DTexture9> txVideo;
if (!txVideo)
  hr = m_D3DDev->CreateTexture(1920, 1080, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &txVideo, NULL);
CComPtr<IDirect3DSurface9> surfaceVideo;
hr = txVideo->GetSurfaceLevel(0, &surfaceVideo);
m_D3DDev->StretchRect(lpPresInfo->lpSurf, NULL, surfaceVideo, NULL, D3DTEXF_NONE);

ここで静的変数としてテクスチャを作っているのも、なかなかポイント高いです、悪い意味で。デバイスロストなどの都合があるので、クラスのメンバ変数辺りに置いておかないとどうしようもありません。よい子のみんなは真似してはいけません。

lpPresInfo->lpSurfに次のフレームの映像が入っています。元のコードでは、それをそのまま画面に表示させるだけでした(バックバッファへStretchRect)。一方、このコードでは一旦テクスチャであるtxVideoにコピーしています。VMR9にはlpPresInfo->lpSurfをテクスチャにする方法もありますが、それよりもこうしたほうがたまたまCPU使用率が低かったことと、ついでに1920×1080に大きさを揃えたかったという理由により、ここでStretchRectで拡大コピーしていたのだと思います。

540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
static CComPtr<IDirect3DTexture9> tx;
if (!tx)
{
  hr = m_D3DDev->CreateTexture(1920, 1080, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &tx, NULL);
}
D3DLOCKED_RECT lr;
hr = tx->LockRect(0, &lr, NULL, 0);
static ULONG_PTR gdipToken;
if (!gdipToken)
{
  Gdiplus::GdiplusStartupInput si;
  Gdiplus::GdiplusStartup(&gdipToken, &si, NULL);
}
{
  Gdiplus::Bitmap bitmap(1920, 1080, lr.Pitch, PixelFormat32bppARGB, static_cast<BYTE*>(lr.pBits));
  Gdiplus::Graphics g(&bitmap);
  Gdiplus::Font f(L"Tahoma", 48);
  Gdiplus::SolidBrush br(Gdiplus::Color(255, 255, 255, 255));
  Gdiplus::SolidBrush br2(Gdiplus::Color::Black);
  g.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
  g.Clear(Gdiplus::Color::Transparent);
  g.SetCompositingMode(Gdiplus::CompositingModeSourceOver);
  g.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAlias);
  g.DrawString(L"Test", 4, &f, Gdiplus::PointF(4, 5), &br2);
  g.DrawString(L"Test", 4, &f, Gdiplus::PointF(0, 0), &br);
  HDC hdc = g.GetHDC();
 
  SetTextAlign(hdc, TA_RIGHT);
  SetBkMode(hdc, TRANSPARENT);
  //RECT rc = {0, 0, 480, 480};
  //FillRect(hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH));
  DWORD current = GetTickCount();
  HGDIOBJ hOldFont = SelectObject(hdc, hfnt);
  for (std::size_t i = 0; i < comment.size(); ++i)
  {
    int pos = 1920 - static_cast<int>(current - comment[i].StartCount) / 3;
    if (pos >= 0)
    {
      SetTextColor(hdc, RGB(0, 0, 0));
      TextOut(hdc, pos + 4, comment[i].Y + 5, comment[i].Comment.data(), static_cast<int>(comment[i].Comment.length()));
      SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
      TextOut(hdc, pos, comment[i].Y, comment[i].Comment.data(), static_cast<int>(comment[i].Comment.length()));
    }
    else
    {
      comment.erase(comment.begin() + i);
      --i;
    }
  }
  SelectObject(hdc, hOldFont);
  g.ReleaseHDC(hdc);
  g.Flush();
}
tx->UnlockRect(0);

txやgdipTokenを静的変数としているのが……、というのはさっきも書いたとおりです。それはともかくここがコメントを書き込む一番中核の部分です。GDI+が出てきたかと思えばGDIというチャンポンは相変わらず余裕のなさの表れです。当初、GDIでの描画を試行していたことがあり、そのときのテキスト描画のコードをコピーしてきたという突貫工事が原因で、Graphics::DrawStringを使うように書き換えようと思ったはずですが、直されぬままです。

595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
hr = m_D3DDev->SetStreamSource(0, vb, 0, sizeof (CUSTOMVERTEX));
hr = m_D3DDev->SetFVF(D3DFVF_CUSTOMVERTEX);
hr = m_D3DDev->SetTexture(0, txVideo);
hr = m_D3DDev->SetTexture(1, tx);
hr = m_D3DDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
hr = m_D3DDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
hr = m_D3DDev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
hr = m_D3DDev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
hr = m_D3DDev->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
hr = m_D3DDev->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
hr = m_D3DDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA);
hr = m_D3DDev->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
hr = m_D3DDev->SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
hr = m_D3DDev->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2);
hr = m_D3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);

テクスチャステージで映像とコメントを合成して描画しています。映像の上にコメントをアルファブレンドしています。本当はもちろんテクスチャを貼り付けるポリゴンを作るコードがあるのですが、これまた酷い(デバイスロストとか解放とか何も考えていない)ですし、どうせ本来は定型的な処理なのでここでは省略します。


これで大体全部です。あとは、パッチを見てください。今から作り直すとしたら、Twitterの特定のハッシュタグ付きの発言を取り込むようにもしたいです。


スポンサード リンク

この記事のカテゴリ

  • ⇒ ニコニコ実況からコメントを取得した (2) 表示
  • ⇒ ニコニコ実況からコメントを取得した (2) 表示
  • ⇒ ニコニコ実況からコメントを取得した (2) 表示
  • ⇒ ニコニコ実況からコメントを取得した (2) 表示