DirectShowその2(CComPtr(ATL)導入)

C++的には、Com用のスマートポインタが是非とも必要だ。
調べたところ、CComPtrというものがATLに含まれることがわかった。
ATLはMFCと関係があるらしい?が、MFCは守備範囲外なので避けたいところだ・・・


で、WinDdkからMFC共々入手可能らしい。

CComPtrとCComQIPtrを使う

素晴らしくシンプルになった。

#include <tchar.h>
#include <iostream>
#include <string>
#include <vector>
#include <atlbase.h>

#include <dshow.h>
#pragma comment(lib, "strmiids")


class Com
{
public:
    Com()
    {
        CoInitialize(NULL);
    }

    ~Com()
    {
        CoUninitialize();
    }
};


static std::wstring to_WideChar(UINT uCodePage, const std::string &text)
{
    int size=MultiByteToWideChar(uCodePage, 0, text.c_str(), -1, NULL, 0);
    std::vector<wchar_t> buf(size);
    size=MultiByteToWideChar(uCodePage, 0, text.c_str(), -1, &buf[0], buf.size());
    return std::wstring(buf.begin(), buf.begin()+size);
}


int main(int argc, char **argv)
{
    if(argc<2){
        std::cout << "usage: " << argv[0] << "  {file}" << std::endl;
        return 0;
    }

    Com com;

    CComPtr<IGraphBuilder> gb;
    if(FAILED(gb.CoCreateInstance(CLSID_FilterGraph))){
        return 1;
    }

    CComQIPtr<IMediaControl> mc(gb);
    if(!mc){
        return 2;
    }

    std::wstring file=to_WideChar(CP_OEMCP, argv[1]);
    if(FAILED(gb->RenderFile(file.c_str(), NULL))){
        return 3;
    }

    mc->Run();

    std::cout << "hit any key" << std::endl;
    std::string line;
    std::getline(std::cin, line);

    return 0;
}

CComPtrは
C:\WinDDK\7600.16385.1\inc\atl71\atlcomcli.h
に書いてあるので見てAddRef, Releaseの動きを把握する。

QueryInterfaceで取得するものは、CComQIPtrを使う。


classとIID_XXXXが一意に結びついているので
iidは__uuidof(class)で取得できるらしく、故に明示的に指定する必要が無いということですな。

リンク時に警告が出るw

ddkとvc2010のバージョンがあっていないとかか?

main.obj : warning LNK4254: セクション 'ATL' (50000040) は '.rdata' (40000040) に異なる属性を伴ってマージされています

問題なく動いたが、無視できる警告なのかは知らない

DirectShowプログラミング開始

OpenCVとかKinectに行く前の下準備で、DirectShowをマスターすることにした。

この本を読みながら進めている。
VC2010 expressedition
を使う。

DirectShowのインストール?

VCとDirectXのバージョンによって情報が錯綜しているのだけど、
VC2010 expresseditionに関しては下記にdshow.hが存在した。
最初から入っているということでよさげ。

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include

動作確認

本の最初のDirectShowサンプルをC++風?に書いてみた。

#include <iostream>
#include <string>
#include <dshow.h>
#include <vector>
#pragma comment(lib, "strmiids")


class Com
{
public:
    Com()
    {
        CoInitialize(NULL);
    }

    ~Com()
    {
        CoUninitialize();
    }
};


class ComGraphBuilder
{
    IGraphBuilder *pigb;
public:
    ComGraphBuilder()
        : pigb(0)
    {
        HRESULT hr=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
                IID_IGraphBuilder, (void**) &pigb);
    }

    ~ComGraphBuilder()
    {
        if(pigb){
            pigb->Release();
        }
    }

    bool RenderFile(const std::wstring &file)
    {
        return pigb->RenderFile(file.c_str(), NULL)==S_OK;
    }

    IGraphBuilder* Get(){ return pigb; }
};


class ComMediaControl
{
    IMediaControl *pimc;
public:
    ComMediaControl(IGraphBuilder *pigb)
        : pimc(0)
    {
        HRESULT hr=pigb->QueryInterface(IID_IMediaControl, (void**)&pimc);
    }

    ~ComMediaControl()
    {
        if(pimc){
            pimc->Release();
        }
    }

    void Run()
    {
        pimc->Run();
    }

    IMediaControl *Get(){ return pimc; }
};


static std::wstring to_WideChar(UINT uCodePage, const std::string &text)
{
    int size=MultiByteToWideChar(uCodePage, 0, text.c_str(), -1, NULL, 0);
    std::vector<wchar_t> buf(size);
    size=MultiByteToWideChar(uCodePage, 0, text.c_str(), -1, &buf[0], buf.size());
    return std::wstring(buf.begin(), buf.begin()+size);
}


int main(int argc, char **argv)
{
    if(argc<2){
        std::cout << "usage: " << argv[0] << "  {file}" << std::endl;
        return 0;
    }

    Com com;

    ComGraphBuilder gb;
    if(!gb.Get()){
        return 1;
    }

    ComMediaControl mc(gb.Get());
    if(!mc.Get()){
        return 2;
    }

    if(!gb.RenderFile(to_WideChar(CP_OEMCP, argv[1]))){
        return 3;
    }

    mc.Run();

    std::cout << "hit any key" << std::endl;
    std::string line;
    std::getline(std::cin, line);

    return 0;
}

チルト

積んであったkinectを動かしてみる。
SDK1.5でチルト角の調整。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Kinect;


namespace Tilt
{
    public partial class Form1 : Form
    {
        KinectSensor kinect;

        public Form1()
        {
            InitializeComponent();

            kinect = KinectSensor.KinectSensors[0];
            kinect.Start();

            kinectTilt.Value = kinect.ElevationAngle;
        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            try
            {
                kinect.ElevationAngle = (int)kinectTilt.Value;
            }
            catch (InvalidOperationException ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
    }
}

ネット上によくある
using Microsoft.Research.Kinect;
とか
Runtime
というのが変わっているぽいのでSDK1.5版のメモ。

とりあえずVertexBuffer

ousttrue2012-05-03

PmxのVertexとIndices部分だけのパーサを書いて、VertexBufferの描画までやってみた。
次はモーションの実装をする。
bulletsharpを使う予定。

NUnit導入

テスト駆動開発にすべく取り入れる。
いくつかはまりポイントがあったのでメモ。


nunit.exeをC#4.0のランタイムで起動する。
[File]-[Select Runtime]で選択できるように見えるのだが、メニューから変更する方法がわからなかった。
たまたまnunit.exe.configを開いたら変更方法が書いてあったを見つけた。

  <startup useLegacyV2RuntimeActivationPolicy="true">
    <!-- Comment out the next line to force use of .NET 4.0 -->
    <!-- <supportedRuntime version="v2.0.50727" /> -->
    <supportedRuntime version="v4.0.30319" />
  </startup>


読み込み時に以下のエラーがでる。

Test load failed!

System.IO.FileNotFoundException: ファイルまたはアセンブリ'nunit.framework, Version=2.6.0.12051, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77'、またはその依存関係の1つが読み込めませんでした。指定されたファイルが見つかりません。
For further information, use the Exception Details menu item

nunit.framework.dllを見つけることができないのが原因なのだが、PATHを通してもうまくいかなかった。テスト対象のdllと同じディレクトリにnunit.frameworkd.dllをコピーしたら直った。

premake4でC#開発

vimじゃないとだめなのでちょっと寄り道。
こちら

を参考にIDEから降りる道を模索。

premake4でC#向けのMakefileの自動生成

premake4.lua

-- A solution contains projects, and defines the available configurations
solution "Sample"
configurations { "Release", "Debug" }
configuration "windows*"
do
end

configuration "Debug"
do
  flags { "Symbols" }
  targetdir "debug"
end

configuration "Release"
do
  flags { "Optimize" }
  targetdir "release"
end

configuration {}

------------------------------------------------------------------------------
-- Project
------------------------------------------------------------------------------
project "Sample"
--language "C"
--language "C++"
language "C#"
--kind "StaticLib"
--kind "DynamicLib"
kind "ConsoleApp"
--kind "WindowedApp"

files {
    "*.cs",
}
defines {
}
includedirs {
}
libdirs {
    "C:/Program Files (x86)/SlimDX SDK (January 2012)/Bin/net40/x86",
}
links {
    "SlimDX",
}
$ export PATH=/cygdrive/C/WINDOWS/Microsoft.NET/Framework/v4.0.30319:$PATH
$ premake4 --dotnet=msnet gmake
$ make verbose=t
==== Building Sample (release) ====
Creating obj/Release
mkdir -p obj/Release
csc /nologo /out:release/Sample.exe /optimize /t:exe /lib:C:/Program\ Files\ \(x86\)/SlimDX\ SDK\ \(January\ 2012\)/Bin/net40/
x86  /r:SlimDX.dll Hello.cs
$ ./release/Sample.exe
hello world

VCのソリューションも以下の方法で作れる。

$ premake4 vs2008
# vs2010は未対応だった premake4.3