svvitch
digital signage player
D:/vs_workspace/switch_sf/src/svvitch/CaptureScene.cpp
Go to the documentation of this file.
00001 #include "CaptureScene.h"
00002 #include <algorithm>
00003 #include <Poco/Util/XMLConfiguration.h>
00004 #include <Poco/UnicodeConverter.h>
00005 #include "Utils.h"
00006 
00007 
00008 CaptureScene::CaptureScene(Renderer& renderer): Scene(renderer),
00009     activeChangePlaylist(this, &CaptureScene::changePlaylist),
00010     _frame(0), _startup(0), _useStageCapture(false),
00011     _deviceNo(0), _routePinNo(0), _deviceW(640), _deviceH(480), _deviceFPS(30),
00012     _autoWhiteBalance(true), _whiteBalance(-100), _autoExposure(true), _exposure(-100),
00013     _flipMode(3), _deviceVideoType(MEDIASUBTYPE_YUY2),
00014     _px(0), _py(0),_pw(320), _ph(240), _spx(320), _spy(0),_spw(320), _sph(240),
00015     _device(NULL), _gb(NULL), _capture(NULL), _vr(NULL), _mc(NULL), _cameraImage(NULL),
00016     _sample(NULL), _surface(NULL), _fx(NULL), _data1(NULL), _data2(NULL), _data3(NULL),
00017     _lookup(NULL), _block(NULL), _activeBlock(NULL),
00018     _forceUpdate(false), _detectCount(0), _ignoreDetectCount(0), _main(NULL)
00019 {
00020 }
00021 
00022 CaptureScene::‾CaptureScene() {
00023     releaseFilter();
00024     SAFE_RELEASE(_cameraImage);
00025     SAFE_RELEASE(_sample);
00026     SAFE_RELEASE(_surface);
00027     SAFE_RELEASE(_fx);
00028     SAFE_DELETE(_data1);
00029     SAFE_DELETE(_data2);
00030     SAFE_DELETE(_data3);
00031     SAFE_DELETE(_lookup);
00032     SAFE_DELETE(_block);
00033     SAFE_DELETE(_activeBlock);
00034     _log.information("*release capture-scene");
00035 }
00036 
00037 bool CaptureScene::initialize() {
00038     _log.information("*initialize CaptureScene");
00039 
00040     bool useSampling = false;
00041     vector<int> activeBlocks;
00042     SetRect(&_clip, 0, 0, _deviceW, _deviceH);
00043     try {
00044         Poco::Util::XMLConfiguration* xml = new Poco::Util::XMLConfiguration("capture-config.xml");
00045         _useStageCapture = xml->getBool("device[@useStageCapture]", false);
00046         _deviceNo = xml->getInt("device[@no]", 0);
00047         _routePinNo = xml->getInt("device[@route]", 0);
00048         if (_useStageCapture) {
00049             _deviceW = xml->getInt("device[@w]", 640);
00050             _deviceH = xml->getInt("device[@h]", 480);
00051             //_deviceW = config().stageRect.right;
00052             //_deviceH = config().stageRect.bottom;
00053         } else {
00054             _deviceW = xml->getInt("device[@w]", 640);
00055             _deviceH = xml->getInt("device[@h]", 480);
00056         }
00057         _deviceFPS = xml->getInt("device[@fps]", 30);
00058         _useDeinterlace = xml->getBool("device[@deinterlace]", true);
00059         _flipMode = xml->getInt("device[@flipMode]", 3);
00060         string type = Poco::toLower(xml->getString("device[@type]", "yuv2"));
00061         if (type == "rgb24") {
00062             _deviceVideoType = MEDIASUBTYPE_RGB24;
00063         } else if (type == "rgb32") {
00064             _deviceVideoType = MEDIASUBTYPE_RGB32;
00065         } else if (type == "yuv2") {
00066             _deviceVideoType = MEDIASUBTYPE_YUY2;
00067         }
00068         int cx = xml->getInt("device[@cx]", 0);
00069         int cy = xml->getInt("device[@cy]", 0);
00070         int cw = xml->getInt("device[@cw]", _deviceW);
00071         int ch = xml->getInt("device[@ch]", _deviceH);
00072         SetRect(&_clip, cx, cy, cw, ch);
00073         string wb = Poco::toLower(xml->getString("device[@whitebalance]", "auto"));
00074         if (wb == "auto") {
00075             _autoWhiteBalance = true;
00076         } else {
00077             _autoWhiteBalance = false;
00078             _whiteBalance = -100;
00079              Poco::NumberParser::tryParse(wb, _whiteBalance);
00080         }
00081         string exposure = Poco::toLower(xml->getString("device[@exposure]", "auto"));
00082         if (exposure == "auto") {
00083             _autoExposure = true;
00084         } else {
00085             _autoExposure = false;
00086             _exposure = -100;
00087             Poco::NumberParser::tryParse(exposure, _exposure);
00088         }
00089 
00090         _px = xml->getInt("preview.x", 0);
00091         _py = xml->getInt("preview.y", 0);
00092         _pw = xml->getInt("preview.width", 320);
00093         _ph = xml->getInt("preview.height", 240);
00094 
00095         useSampling = xml->hasProperty("sampling");
00096         if (useSampling) {
00097             _sw = xml->getInt("sampling.width", 0);
00098             _sh = xml->getInt("sampling.height", 0);
00099             _spx = xml->getInt("sampling.preview.x", _px + _pw);
00100             _spy = xml->getInt("sampling.preview.y", _py);
00101             _spw = xml->getInt("sampling.preview.width", _pw);
00102             _sph = xml->getInt("sampling.preview.height", _ph);
00103             _intervalsBackground = xml->getInt("sampling.intervalsBackground", 3600);
00104             _intervalsForeground = xml->getInt("sampling.intervalsForeground", 30);
00105             _blockThreshold = xml->getInt("sampling.blockThreshold", 20);
00106             _lookupThreshold = xml->getInt("sampling.lookupThreshold", 40);
00107             _detectThreshold = xml->getInt("sampling.detectThreshold", 100);
00108             _detectedPlaylist = xml->getString("sampling.detectedPlaylist", "");
00109             string activePlaylist = xml->getString("sampling.activePlaylist", "");
00110             if (!activePlaylist.empty()) svvitch::split(activePlaylist, ',', _activePlaylist);
00111             _ignoreDetectTime = xml->getInt("sampling.ignoreDetectTime", 15 * 60);
00112             string ab = xml->getString("sampling.activeBlocks", "");
00113             svvitch::parseMultiNumbers(ab, 0, _sw * _sh - 1, activeBlocks);
00114         }
00115 
00116         xml->release();
00117     } catch (Poco::Exception& ex) {
00118         _log.warning(ex.displayText());
00119     }
00120 
00121     if (_useStageCapture || createFilter()) {
00122         _cameraImage = _renderer.createRenderTarget(_clip.right, _clip.bottom, D3DFMT_A8R8G8B8);
00123         string s = Poco::format("clip:%ld,%ld,%ld,%ld", _clip.left, _clip.top, _clip.right, _clip.bottom);
00124         _log.information(Poco::format("camera image: %dx%d(%s) %s-mode %s", _deviceW, _deviceH, s, string(_useStageCapture?"stage":"capture"), string(_cameraImage?"OK":"NG")));
00125 
00126         if (useSampling) {
00127             _sample = _renderer.createRenderTarget(_sw, _sh, D3DFMT_A8R8G8B8);
00128             _renderer.colorFill(_sample, 0xff000000);
00129             _surface = _renderer.createLockableSurface(_sw, _sh, D3DFMT_A8R8G8B8);
00130             const long size = _sw * _sh;
00131             _data1 = new INT[size];
00132             _data2 = new INT[size];
00133             _data3 = new INT[size];
00134             _lookup = new BOOL[size];
00135             _block = new INT[size];
00136             _activeBlock = new BOOL[size];
00137             for (long i = 0; i < size; i++) {
00138                 _data1[i] = -1;
00139                 _data2[i] = -1;
00140                 _data3[i] = -1;
00141                 _lookup[i] = false;
00142                 _block[i] = 0;
00143                 _activeBlock[i] = activeBlocks.empty() || std::find(activeBlocks.begin(), activeBlocks.end(), i) != activeBlocks.end();
00144             }
00145             _fx = _renderer.createEffect("fx/rgb2hsv.fx");
00146             if (_fx) {
00147                 HRESULT hr = _fx->SetTechnique("convertTechnique");
00148                 if FAILED(hr) _log.warning("failed effect not set technique convertTechnique");
00149                 hr = _fx->SetTexture("frame1", _cameraImage);
00150                 if FAILED(hr) _log.warning("failed effect not set texture1");
00151                 hr = _fx->SetTexture("frame2", _sample);
00152                 if FAILED(hr) _log.warning("failed effect not set texture2");
00153             } else {
00154                 return false;
00155             }
00156             _log.information(Poco::format("sampling image: %dx%d", _sw, _sh));
00157         }
00158         return true;
00159     }
00160     return false;
00161 }
00162 
00163 bool CaptureScene::createFilter() {
00164     HRESULT hr;
00165     hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&_gb);
00166     if (FAILED(hr)) {
00167         _log.warning(Poco::format("failed create filter graph: %s", errorText(hr)));
00168         return false;
00169     }
00170     _vr = new DSVideoRenderer(_renderer, true, NULL, &hr);
00171     if (FAILED(hr = _gb->AddFilter(_vr, L"DSVideoRenderer"))) {
00172         _log.warning(Poco::format("failed add filter: %s", errorText(hr)));
00173         return false;
00174     }
00175     hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&_capture);
00176     if (FAILED(hr)) {
00177         _log.warning(Poco::format("failed create capture graph builder2: %s", errorText(hr)));
00178         return false;
00179     }
00180     hr = _capture->SetFiltergraph(_gb);
00181     if (FAILED(hr)) {
00182         _log.warning(Poco::format("setup failed capture graph builder2: %s", errorText(hr)));
00183         return false;
00184     }
00185 
00186     IBaseFilter* src = NULL;
00187     if (fetchDevice(CLSID_VideoInputDeviceCategory, _deviceNo, &src)) {
00188         hr = _gb->AddFilter(src, L"Video Capture Source");
00189         if (FAILED(hr)) {
00190             _log.warning("failed add capture source");
00191         } else {
00192             SAFE_RELEASE(_device);
00193             _device = src;
00194             routeCrossbar(src, _routePinNo);
00195             setWhiteBalance(src, _autoWhiteBalance, _whiteBalance);
00196             setExposure(src, _autoExposure, _exposure);
00197         }
00198 
00199         IPin* renderPin = NULL;
00200         hr = _capture->FindPin(src, PINDIR_OUTPUT, &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, FALSE, 0, &renderPin);
00201         if (FAILED(hr)) {
00202             _log.warning("*not found preview pin.");
00203             hr = _capture->FindPin(src, PINDIR_OUTPUT, &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, FALSE, 0, &renderPin);
00204             if (FAILED(hr)) {
00205                 _log.warning("*not found capture pin.");
00206                 return false;
00207             } else {
00208                 _log.information("rendered capture pin");
00209             }
00210         } else {
00211             _log.information("rendered preview pin");
00212         }
00213         PIN_INFO info;
00214         renderPin->QueryPinInfo(&info);
00215         info.pFilter->Release();
00216         wstring wname(info.achName);
00217         string name;
00218         Poco::UnicodeConverter::toUTF8(wname, name);
00219         _log.information(Poco::format("render pin: %s", name));
00220 
00221         // 出力サイズの設定
00222         IAMStreamConfig* sc;
00223         hr = renderPin->QueryInterface(&sc);
00224         if (SUCCEEDED(hr)) {
00225             int count;
00226             int size;
00227             hr = sc->GetNumberOfCapabilities(&count, &size);
00228             float maxFPS = 0;
00229             long maxW = 0;
00230             long maxH = 0;
00231             int fixedCount = 0;
00232             int bestConfig = -1;
00233             int hitConfig = -1;
00234             VIDEO_STREAM_CONFIG_CAPS scc;
00235             for (int i = 0; i < count; i++) {
00236                 AM_MEDIA_TYPE* amt;
00237                 hr = sc->GetStreamCaps(i, &amt, reinterpret_cast<BYTE*>(&scc));
00238                 if (SUCCEEDED(hr)) {
00239                     float fps = 10000000.0f / scc.MinFrameInterval;
00240                     string videoType;
00241                     if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_RGB24)) {
00242                         videoType = "RGB24";
00243                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_RGB32)) {
00244                         videoType = "RGB32";
00245                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_RGB555)) {
00246                         videoType = "RGB555";
00247                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_RGB565)) {
00248                         videoType = "RGB565";
00249                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_YVYU)) {
00250                         videoType = "YVYU";
00251                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_UYVY)) {
00252                         videoType = "UYVY";
00253                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_IYUV)) {
00254                         videoType = "IYUV";
00255                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_YUY2)) {
00256                         videoType = "YUY2";
00257                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_Y41P)) {
00258                         videoType = "Y41P";
00259                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_YVU9)) {
00260                         videoType = "YVU9";
00261                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_YV12)) {
00262                         videoType = "YV12";
00263                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_YV12)) {
00264                         videoType = "YV12";
00265                     } else if (IsEqualGUID(amt->subtype, MEDIASUBTYPE_MJPG)) {
00266                         videoType = "MJPG";
00267                     } else {
00268                         videoType = Poco::format("unknown 0x%lx", amt->subtype.Data1);
00269                     }
00270                     bool outputFixed = scc.MinOutputSize.cx == scc.MaxOutputSize.cx && scc.MinOutputSize.cy == scc.MaxOutputSize.cy;
00271                     long mw = scc.MinOutputSize.cx;
00272                     long mh = scc.MinOutputSize.cy;
00273                     long gx = scc.OutputGranularityX;
00274                     long gy = scc.OutputGranularityY;
00275                     long w = scc.MaxCroppingSize.cx;
00276                     long h = scc.MaxCroppingSize.cy;
00277                     ULONG videoStandard = scc.VideoStandard;
00278                     string vinfo = Poco::format("min:%ldx%ld max crop:%ldx%ld fps:%0.2hf", mw, mh, w, h, fps);
00279                     _log.information(Poco::format("capability(%02d) video:%s %lu size fixed:%s %s", i, videoType, videoStandard, string(outputFixed?"true":"false"), vinfo));
00280 
00281                     if (IsEqualGUID(amt->formattype, FORMAT_VideoInfo) && IsEqualGUID(amt->subtype, _deviceVideoType)) {
00282                         // ビデオタイプが一致
00283                         if (!outputFixed && mw < _deviceW && mh < _deviceH && (maxW < w || maxH < h)) {
00284                             maxW = w;
00285                             maxH = h;
00286                             bestConfig = i;
00287                             _log.information(Poco::format("best configuration: %d", i));
00288                         }
00289                         if (w == _deviceW && h == _deviceH) {
00290                             hitConfig = i;
00291                             _log.information(Poco::format("hit configuration: %d", i));
00292                         }
00293                     }
00294                     if (outputFixed) fixedCount++;
00295                     DeleteMediaType(amt);
00296                 }
00297             }
00298 
00299             if (fixedCount == count) {
00300                 // 固定選択式
00301                 if (hitConfig >= 0) {
00302                     _log.information(Poco::format("fixed configuration setting: %d", hitConfig));
00303                     AM_MEDIA_TYPE* amt;
00304                     hr = sc->GetStreamCaps(hitConfig, &amt, reinterpret_cast<BYTE*>(&scc));
00305                     if SUCCEEDED(hr) {
00306                         long w = scc.MaxCroppingSize.cx;
00307                         long h = scc.MaxCroppingSize.cy;
00308                         float fps = 10000000.0f / scc.MinFrameInterval;
00309                         VIDEOINFOHEADER* info = reinterpret_cast<VIDEOINFOHEADER*>(amt->pbFormat);
00310                         info->bmiHeader.biWidth = w;
00311                         info->bmiHeader.biHeight = h;
00312                         // info->AvgTimePerFrame = 10000000 / _deviceFPS;
00313                         // info->AvgTimePerFrame = scc.MinFrameInterval;
00314                         hr = sc->SetFormat(amt);
00315                         if (FAILED(hr)) {
00316                             _log.warning(Poco::format("failed set format: %s", errorText(hr)));
00317                         } else {
00318                             _log.information(Poco::format("set size:%ldx%ld fps:%0.2hf", w, h, fps));
00319                         }
00320                         DeleteMediaType(amt);
00321                     }
00322                 } else {
00323                     _log.warning(Poco::format("not found fixed configuration setting: %dx%d", _deviceW, _deviceH));
00324                 }
00325 
00326             } else {
00327                 // ベスト設定をベースに使う
00328                 _log.information(Poco::format("best configuration based setting: %d", bestConfig));
00329                 AM_MEDIA_TYPE* amt;
00330                 hr = sc->GetStreamCaps(bestConfig, &amt, reinterpret_cast<BYTE*>(&scc));
00331                 if (SUCCEEDED(hr)) {
00332                     VIDEOINFOHEADER* info = reinterpret_cast<VIDEOINFOHEADER*>(amt->pbFormat);
00333                     info->bmiHeader.biWidth = _deviceW;
00334                     info->bmiHeader.biHeight = _deviceH;
00335                     // info->AvgTimePerFrame = 10000000 / _deviceFPS;
00336                     hr = sc->SetFormat(amt);
00337                     if (FAILED(hr)) {
00338                         _log.warning(Poco::format("failed set format: %s", errorText(hr)));
00339                     } else {
00340                         _log.information(Poco::format("set size:%dx%d fps:%d", _deviceW, _deviceH, _deviceFPS));
00341                     }
00342                     DeleteMediaType(amt);
00343                 }
00344             }
00345             // SAFE_RELEASE(sc);
00346         }
00347 
00348         // hr = _capture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, src, NULL, NULL);
00349 
00350         // add deinterlacer
00351         if (_deviceH > 240 && _useDeinterlace) {
00352             IBaseFilter* deinterlacer = NULL;
00353             if (fetchDevice(CLSID_LegacyAmFilterCategory, 0, &deinterlacer, string("honestech Deinterlacer"))) {
00354                 hr = _gb->AddFilter(deinterlacer, L"Deinterlacer");
00355                 if (SUCCEEDED(hr)) {
00356                     IPin* inPin = NULL;
00357                     if (getInPin(deinterlacer, &inPin)) {
00358                         hr = _gb->Connect(renderPin, inPin);
00359                         inPin->Release();
00360                         if (SUCCEEDED(hr)) {
00361                             SAFE_RELEASE(renderPin);
00362                             if (!getOutPin(deinterlacer, &renderPin)) {
00363                                 _log.warning(Poco::format("failed not get deinterlacer output-pin: %s", errorText(hr)));
00364                             } else {
00365                                 _log.information("add&connect deinterlacer");
00366                             }
00367                         } else {
00368                             _log.warning(Poco::format("failed not connect deinterlacer input-pin: %s", errorText(hr)));
00369                         }
00370                     } else {
00371                         _log.warning(Poco::format("failed not connect deinterlacer input-pin: %s", errorText(hr)));
00372                     }
00373                 } else {
00374                     _log.warning("failed not add deinterlacer");
00375                 }
00376             } else {
00377                 _log.warning("not found deinterlacer");
00378             }
00379         }
00380 
00381         hr = _gb->Render(renderPin);
00382         SAFE_RELEASE(renderPin);
00383         // SAFE_RELEASE(src);
00384         if (dumpFilter(_gb) == 0) {
00385             // 正しくレンダリングできた
00386             // IMediaSeeking* ms;
00387             // IMediaEvent* me;
00388             IMediaFilter* mf;
00389             hr = _gb->QueryInterface(&mf);
00390             if (SUCCEEDED(hr)) {
00391                 // リアルタイムでキャプチャするために基準タイマを外す
00392                 hr = mf->SetSyncSource(NULL);
00393                 SAFE_RELEASE(mf);
00394             }
00395             hr = _gb->QueryInterface(&_mc);
00396             if (SUCCEEDED(hr)) {
00397                 hr = _mc->Run();
00398             } else {
00399                 _log.warning("failed query interface: IMediaControl");
00400             }
00401         }
00402         return true;
00403 
00404     } else {
00405         _log.warning("not found video input device");
00406     }
00407     return false;
00408 }
00409 
00410 void CaptureScene::releaseFilter() {
00411     if (_mc) {
00412         HRESULT hr = _mc->Stop();
00413         if (SUCCEEDED(hr)) {
00414             // DirectShow停止待ち
00415             for (;;) {
00416                 OAFilterState fs;
00417                 hr = _mc->GetState(300, &fs);
00418                 if (hr == State_Stopped) {
00419                     break;
00420                 }
00421                 Sleep(100);
00422             }
00423         }
00424     }
00425 
00426     SAFE_RELEASE(_mc);
00427     SAFE_RELEASE(_vr);
00428     SAFE_RELEASE(_capture);
00429     SAFE_RELEASE(_device);
00430     SAFE_RELEASE(_gb);
00431 }
00432 
00433 LPDIRECT3DTEXTURE9 CaptureScene::getCameraImage() {
00434     return _cameraImage;
00435 }
00436 
00437 void CaptureScene::process() {
00438     if (!_main) {
00439         _main = dynamic_cast<MainScenePtr>(_renderer.getScene("main"));
00440     }
00441     if (_startup < 100) {
00442         _startup++;
00443         return;
00444     }
00445 
00446     if (_sample) {
00447         if (_renderer.getRenderTargetData(_sample, _surface)) {
00448             D3DLOCKED_RECT lockedRect = {0};
00449             if (SUCCEEDED(_surface->LockRect(&lockedRect, NULL, 0))) {
00450                 LPBYTE src = (LPBYTE)lockedRect.pBits;
00451                 const long size = _sw * _sh * 4;
00452                 long pos = 0;
00453                 for (long i = 0; i < size; i += 4) {
00454                     _data1[pos++] = src[i + 2];
00455                 }
00456                 _surface->UnlockRect();
00457             }
00458         }
00459 
00460         const long size = _sw * _sh;
00461         int lookup = 0;
00462         for (long i = 0; i < size; i++) {
00463              _lookup[i] = false;
00464             int d1 = 255 - _data2[i] + _data3[i];
00465             int d2 = abs(_data2[i] - _data3[i]);
00466             if (d1 > d2) {
00467                 d1 = d2;
00468             }
00469             _block[i] = d1;
00470             if (_activeBlock[i] && d1 > _blockThreshold) {
00471                 _lookup[i] = true;
00472                 lookup++;
00473             }
00474         }
00475         const int lookupThreshold = _sw * _sh * _lookupThreshold / 100;
00476         if (_ignoreDetectCount > 0) {
00477             _ignoreDetectCount--;
00478             if (_detectCount > 0) _detectCount--;
00479         } else {
00480             if (lookup >= lookupThreshold) {
00481                 _detectCount++;
00482             } else {
00483                 if (_detectCount > 0) _detectCount--;
00484             }
00485             if (_detectCount > _detectThreshold) {
00486                 if (!_detectedPlaylist.empty() && _main) {
00487                     string current = _main->getStatus("current-playlist-id");
00488                     if (_activePlaylist.empty() || std::find(_activePlaylist.begin(), _activePlaylist.end(), current) != _activePlaylist.end()) {
00489                         if (current != _detectedPlaylist) {
00490                             activeChangePlaylist();
00491                         } else {
00492                             _log.information(Poco::format("already playing: %s", _detectedPlaylist));
00493                         }
00494                     }
00495                 }
00496                 //_detectCount = 0;
00497                 _ignoreDetectCount = _ignoreDetectTime;
00498             }
00499         }
00500         // 背景のサンプリング
00501         if (_frame % _intervalsBackground == 0 || _forceUpdate) {
00502             if (_detectCount == 0 && _ignoreDetectCount == 0) {
00503                 if (_data2[0] < 0) {
00504                     for (long i = 0; i < size; i++) {
00505                         _data2[i] = _data1[i];
00506                     }
00507                 } else {
00508                     for (long i = 0; i < size; i++) {
00509                         _data2[i] = (_data1[i] + _data2[i]) >> 1;
00510                     }
00511                 }
00512                 _forceUpdate = false;
00513             } else {
00514                 _forceUpdate = true;
00515             }
00516         }
00517         // 動体のサンプリング
00518         if (_frame % _intervalsForeground == 0) {
00519             if (_data3[0] < 0) {
00520                 for (long i = 0; i < size; i++) {
00521                     _data3[i] = _data1[i];
00522                 }
00523             } else {
00524                 for (long i = 0; i < size; i++) {
00525                     _data3[i] = (_data1[i] + _data3[i]) >> 1;
00526                 }
00527             }
00528         }
00529         _frame++;
00530 
00531     } else {
00532         // _log.warning("failed get render target data");
00533     }
00534 }
00535 
00536 void CaptureScene::draw1() {
00537     if (_cameraImage) {
00538         LPDIRECT3DDEVICE9 device = _renderer.get3DDevice();
00539         if (_vr) {
00540             LPDIRECT3DSURFACE9 orgRT = NULL;
00541             HRESULT hr = device->GetRenderTarget(0, &orgRT);
00542 
00543             LPDIRECT3DSURFACE9 surface;
00544             _cameraImage->GetSurfaceLevel(0, &surface);
00545             hr = device->SetRenderTarget(0, surface);
00546             DWORD col = 0xffffffff;
00547             _vr->draw(-_clip.left, -_clip.top, _deviceW, _deviceH, 0, _flipMode, col, col, col, col);
00548             SAFE_RELEASE(surface);
00549 
00550             if (_sample) {
00551                 D3DSURFACE_DESC desc;
00552                 HRESULT hr = _sample->GetLevelDesc(0, &desc);
00553                 VERTEX dst[] =
00554                 {
00555                     {F(0              - 0.5), F(0               - 0.5), 0.0f, 1.0f, col, 0, 0},
00556                     {F(0 + desc.Width - 0.5), F(0               - 0.5), 0.0f, 1.0f, col, 1, 0},
00557                     {F(0              - 0.5), F(0 + desc.Height - 0.5), 0.0f, 1.0f, col, 0, 1},
00558                     {F(0 + desc.Width - 0.5), F(0 + desc.Height - 0.5), 0.0f, 1.0f, col, 1, 1}
00559                 };
00560                 _sample->GetSurfaceLevel(0, &surface);
00561                 hr = device->SetRenderTarget(0, surface);
00562                 _fx->Begin(NULL, 0);
00563                 _fx->BeginPass(0);
00564                 device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, dst, VERTEX_SIZE);
00565                 _fx->EndPass();
00566                 _fx->End();
00567                 SAFE_RELEASE(surface);
00568             }
00569 
00570             hr = device->SetRenderTarget(0, orgRT);
00571             SAFE_RELEASE(orgRT);
00572         } else if (_useStageCapture) {
00573             _renderer.copyTexture(_renderer.getCaptureTexture(), _cameraImage);
00574         } else {
00575         }
00576     }
00577 }
00578 
00579 void CaptureScene::draw2() {
00580     if (config().viewStatus) {
00581         LPDIRECT3DDEVICE9 device = _renderer.get3DDevice();
00582         if (_vr) {
00583             device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
00584             device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
00585             DWORD col = 0xffffffff;
00586             _renderer.drawTexture(_px, _py, _pw, _ph, _cameraImage, 0, col, col, col, col);
00587             device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
00588             device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
00589             if (_sample) {
00590                 col = 0xccffffff;
00591                 _renderer.drawTexture(_spx, _spy, _spw, _sph, _sample, 0, col, col, col, col);
00592                 const int sw = _spw / _sw;
00593                 const int sh = _sph / _sh;
00594                 for (int y = 0; y < _sh; y++) {
00595                     for (int x = 0; x < _sw; x++) {
00596                         const int i = y * _sw + x;
00597                         if (_activeBlock[i]) {
00598                             if (_lookup[i]) {
00599                                 _renderer.drawFontTextureText(_spx + x * sw, _spy + y * sh, sw, sh, 0xccff3333, "*");
00600                             } else {
00601                                 _renderer.drawFontTextureText(_spx + x * sw, _spy + y * sh, sw, sh, 0xccffffff, "*");
00602                             }
00603                         }
00604                         const BYTE data1 = _data1[i];
00605                         const BYTE data2 = _data2[i];
00606                         const BYTE data3 = _data3[i];
00607                         const BYTE block = _block[i];
00608                         const int px = _spw + x * 22;
00609                         _renderer.drawFontTextureText(_spx + px                     , _spy + y * 10, 10, 10, 0xccff3333, Poco::format("%2?X", data1));
00610                         _renderer.drawFontTextureText(_spx + px + (5 + _sw * 22)    , _spy + y * 10, 10, 10, 0xccff3333, Poco::format("%2?X", data2));
00611                         _renderer.drawFontTextureText(_spx + px + (5 + _sw * 22) * 2, _spy + y * 10, 10, 10, 0xccff3333, Poco::format("%2?X", data3));
00612                         _renderer.drawFontTextureText(_spx + px + (5 + _sw * 22) * 3, _spy + y * 10, 10, 10, 0xccff3333, Poco::format("%2?X", block));
00613                     }
00614                 }
00615                 _renderer.drawFontTextureText(_spx + _spw, _spy + _sh * 10, 10, 10, 0xcc33ccff, Poco::format("detect:%3d ignore:%3d", _detectCount, _ignoreDetectCount));
00616                 if (_forceUpdate) {
00617                     _renderer.drawFontTextureText(_spx + _spw, _spy + _sh * 10 + 10, 10, 10, 0xccffcc00, "*");
00618                 }
00619             }
00620             string s = Poco::format("LIVE! read %03lums", _vr->readTime());
00621             _renderer.drawFontTextureText(_px, _py, 10, 10, 0xccff3333, s);
00622         } else if (_useStageCapture) {
00623             device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
00624             device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
00625             DWORD col = 0xffffffff;
00626             _renderer.drawTexture(_px, _py, _pw, _ph, _cameraImage, 0, col, col, col, col);
00627             device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
00628             device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
00629             _renderer.drawFontTextureText(_px, _py, 10, 10, 0xccff3333, "STAGE RECORDING");
00630         } else {
00631             _renderer.drawFontTextureText(_px, _py, 10, 10, 0xccff3333, "NO SIGNAL");
00632         }
00633     }
00634 }
00635 
00636 
00637 /* 指定したデバイスクラスのフィルタを取得します */
00638 bool CaptureScene::fetchDevice(REFCLSID clsidDeviceClass, int index, IBaseFilter** pBf, string& deviceName) {
00639     ICreateDevEnum* pDevEnum =NULL;
00640     HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pDevEnum);
00641     if (FAILED(hr)) {
00642         _log.warning(Poco::format("system device enumeration failed: %s", errorText(hr)));
00643         return false;
00644     }
00645 
00646     IEnumMoniker* pEnumCat = NULL;
00647     hr = pDevEnum->CreateClassEnumerator(clsidDeviceClass, &pEnumCat, NULL);
00648     pDevEnum->Release();
00649     if (hr != S_OK) {
00650         _log.warning("category enumeration zero count or failed.");
00651         return false;
00652     }
00653 
00654     IMoniker* moniker = NULL;
00655     int count = 0;
00656     bool result = false;
00657     while (!result && SUCCEEDED(pEnumCat->Next(1, &moniker, NULL))) {
00658         string name;
00659         IPropertyBag* propBag = NULL;
00660         hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&propBag);
00661         if (SUCCEEDED(hr)) {
00662             // フィルタのフレンドリ名を取得するには、次の処理を行う。
00663             VARIANT varName;
00664             VariantInit(&varName);
00665             hr = propBag->Read(L"FriendlyName", &varName, 0);
00666             if (SUCCEEDED(hr)) {
00667                 UINT nLength = SysStringLen(V_BSTR(&varName) != NULL?V_BSTR(&varName):OLESTR(""));
00668                 if (nLength > 0) {
00669                     vector<WCHAR> wname(nLength);
00670                     wcsncpy(&wname[0], V_BSTR(&varName), nLength);
00671                     wname.push_back('¥0');
00672                     Poco::UnicodeConverter::toUTF8(wstring(&wname[0]), name);
00673                     _log.information(Poco::format("device: %s", name));
00674                 }
00675             }
00676             VariantClear(&varName);
00677             propBag->Release();
00678         }
00679         bool lookup = false;
00680         if (deviceName.empty()) {
00681             if (count == index) {
00682                 lookup = true;
00683                 deviceName = name;
00684             }
00685         } else if (deviceName == name) {
00686             lookup = true;
00687         }
00688         if (lookup) {
00689             hr = moniker->BindToObject(0, 0, IID_IBaseFilter, (void**)pBf);
00690             if (SUCCEEDED(hr)) {
00691                 result = true;
00692                 _log.information(Poco::format("lookup device: %s", name));
00693             }
00694         }
00695         moniker->Release();
00696         count++;
00697     }
00698     pEnumCat->Release();
00699     _log.information(Poco::format("device result: %s", string(result?"LOOKUP":"NOT FOUND")));
00700     return result;
00701 }
00702 
00703 bool CaptureScene::getPin(IBaseFilter* filter, IPin** pin, PIN_DIRECTION dir) {
00704     bool bFound = false;
00705     IEnumPins* pEnum = NULL;
00706     HRESULT hr = filter->EnumPins(&pEnum);
00707     if (SUCCEEDED(hr)) {
00708         while (!bFound && pEnum->Next(1, pin, 0) == S_OK) {
00709             PIN_DIRECTION PinDirThis;
00710             (*pin)->QueryDirection(&PinDirThis);
00711             if (dir == PinDirThis) {
00712                 IPin* pToPin = NULL;
00713                 hr = (*pin)->ConnectedTo(&pToPin);
00714                 if (SUCCEEDED(hr)) {
00715                     SAFE_RELEASE(pToPin);
00716                     SAFE_RELEASE(*pin);
00717                 } else if (hr == VFW_E_NOT_CONNECTED) {
00718                     bFound = true;
00719                 }
00720             }
00721         }
00722         // ピンの参照は残したまま戻す
00723         SAFE_RELEASE(pEnum);
00724     }
00725     return bFound;
00726 }
00727 
00728 /* 指定したフィルタの入力ピンを返します */
00729 bool CaptureScene::getInPin(IBaseFilter* filter, IPin** pin) {
00730     return getPin(filter, pin, PINDIR_INPUT);
00731 }
00732 
00733 /* 指定したフィルタの出力ピンを返します */
00734 bool CaptureScene::getOutPin(IBaseFilter* filter, IPin** pin) {
00735     return getPin(filter, pin, PINDIR_OUTPUT);
00736 }
00737 
00738 void CaptureScene::setWhiteBalance(IBaseFilter* src, bool autoFlag, long v) {
00739     IAMVideoProcAmp* procAmp = NULL;
00740     HRESULT hr = src->QueryInterface(&procAmp);
00741     if SUCCEEDED(hr) {
00742         // VideoProcAmp_Brightness
00743         // VideoProcAmp_Contrast
00744         // VideoProcAmp_Hue
00745         // VideoProcAmp_Saturation
00746         // VideoProcAmp_Sharpness
00747         // VideoProcAmp_Gamma
00748         // VideoProcAmp_ColorEnable
00749         // VideoProcAmp_WhiteBalance
00750         // VideoProcAmp_BacklightCompensation
00751         // VideoProcAmp_Gain
00752         long min, max, steppingDelta, defaultValue, capsFlags;
00753         hr = procAmp->GetRange(VideoProcAmp_WhiteBalance, &min, &max, &steppingDelta, &defaultValue, &capsFlags);
00754         string s = (capsFlags == VideoProcAmp_Flags_Auto)?"auto":"manual";
00755         _log.information(Poco::format("video whitebalance: %ld-%ld(%ld): %s", min, max, defaultValue, s));
00756         if (v == -100) v = defaultValue;
00757         if (autoFlag) {
00758             hr = procAmp->Set(VideoProcAmp_WhiteBalance, defaultValue, VideoProcAmp_Flags_Auto);
00759             if SUCCEEDED(hr) _log.information("video whitebalance: auto");
00760         } else {
00761             hr = procAmp->Set(VideoProcAmp_WhiteBalance, v, VideoProcAmp_Flags_Manual);
00762             if SUCCEEDED(hr) _log.information(Poco::format("video whitebalance: %ld", v));
00763         }
00764         SAFE_RELEASE(procAmp);
00765     }
00766 }
00767 
00768 void CaptureScene::setExposure(IBaseFilter* src, bool autoFlag, long v) {
00769     IAMCameraControl* cameraControl = NULL;
00770     HRESULT hr = src->QueryInterface(&cameraControl);
00771     if SUCCEEDED(hr) {
00772         // CameraControl_Pan
00773         // CameraControl_Tilt
00774         // CameraControl_Roll
00775         // CameraControl_Zoom
00776         // CameraControl_Exposure
00777         // CameraControl_Iris
00778         // CameraControl_Focus
00779         long min, max, steppingDelta, defaultValue, capsFlags;
00780         hr = cameraControl->GetRange(CameraControl_Exposure, &min, &max, &steppingDelta, &defaultValue, &capsFlags);
00781         string s = (capsFlags == CameraControl_Flags_Auto)?"auto":"manual";
00782         _log.information(Poco::format("camera exposure: %ld-%ld(%ld): %s", min, max, defaultValue, s));
00783         if (v == -100) v = defaultValue;
00784         if (autoFlag) {
00785             hr = cameraControl->Set(CameraControl_Exposure, defaultValue, CameraControl_Flags_Auto);
00786             if SUCCEEDED(hr) _log.information("camera exposure: auto");
00787         } else {
00788             hr = cameraControl->Set(CameraControl_Exposure, v, CameraControl_Flags_Manual);
00789             if SUCCEEDED(hr) _log.information(Poco::format("camera exposure: %ld", v));
00790         }
00791         SAFE_RELEASE(cameraControl);
00792     }
00793 }
00794 
00795 bool CaptureScene::routeCrossbar(IBaseFilter* src, int no) {
00796     IAMCrossbar* crossbar = NULL;
00797     HRESULT hr = _capture->FindInterface(&LOOK_UPSTREAM_ONLY, NULL, src, IID_IAMCrossbar, (void**)&crossbar);
00798     if (FAILED(hr)) {
00799         _log.warning(Poco::format("not found crossbar: %s", errorText(hr)));
00800         return false;
00801     }
00802 
00803     long cOutput = -1;
00804     long cInput = -1;
00805     hr = crossbar->get_PinCounts(&cOutput, &cInput);
00806     for (long i = 0; i < cOutput; i++) {
00807         long lRelated = -1;
00808         long lType = -1;
00809         long lRouted = -1;
00810         hr = crossbar->get_CrossbarPinInfo(FALSE, i, &lRelated, &lType);
00811         if (lType == PhysConn_Video_VideoDecoder) {
00812             hr = crossbar->get_IsRoutedTo(i, &lRouted);
00813             hr = crossbar->CanRoute(i, no);
00814             if (SUCCEEDED(hr)) {
00815                 hr = crossbar->Route(i, no);
00816                 crossbar->get_CrossbarPinInfo(TRUE, no, &lRelated, &lType);
00817                 if (SUCCEEDED(hr)) {
00818                     _log.information(Poco::format("crossbar routed to <%s>", getPinName(lType)));
00819                     break;
00820                 } else {
00821                     _log.warning(Poco::format("crossbar can't routing to <%s>: %s", getPinName(lType), errorText(hr)));
00822                 }
00823             }
00824         }
00825     }
00826     SAFE_RELEASE(crossbar);
00827     return true;
00828 }
00829 
00830 // フィルタ一覧
00831 int CaptureScene::dumpFilter(IGraphBuilder* gb) {
00832     if (!gb) return 0;
00833 
00834     IEnumFilters *pEnum = NULL;
00835     HRESULT hr = gb->EnumFilters(&pEnum);
00836 
00837     int count = 0;
00838     int vcount = 0;
00839     IBaseFilter *pFilter = NULL;
00840     while (pEnum->Next(1, &pFilter, 0) == S_OK) {
00841         FILTER_INFO info;
00842         HRESULT hr = pFilter->QueryFilterInfo(&info);
00843         if (SUCCEEDED(hr)) {
00844             wstring wname(info.achName);
00845             string name;
00846             Poco::UnicodeConverter::toUTF8(wname, name);
00847             _log.information(Poco::format("filter: %s", name));
00848             SAFE_RELEASE(info.pGraph);
00849         }
00850         IVideoWindow *vw;
00851         hr = pFilter->QueryInterface(&vw);
00852         if (SUCCEEDED(hr) && vw != NULL) {
00853             vcount++;
00854             SAFE_RELEASE(vw);
00855         }
00856         SAFE_RELEASE(pFilter);
00857         count++;
00858     }
00859     SAFE_RELEASE(pEnum);
00860     _log.information(Poco::format("dump filters: %d(in windowed filters: %d)", count, vcount));
00861     return vcount;
00862 }
00863 
00864 // タイプと名前を関連付けるヘルパー関数。
00865 const string CaptureScene::getPinName(long lType) {
00866     switch (lType) {
00867         case PhysConn_Video_Tuner:            return string("Video Tuner");
00868         case PhysConn_Video_Composite:        return string("Video Composite");
00869         case PhysConn_Video_SVideo:           return string("S-Video");
00870         case PhysConn_Video_RGB:              return string("Video RGB");
00871         case PhysConn_Video_YRYBY:            return string("Video YRYBY");
00872         case PhysConn_Video_SerialDigital:    return string("Video Serial Digital");
00873         case PhysConn_Video_ParallelDigital:  return string("Video Parallel Digital"); 
00874         case PhysConn_Video_SCSI:             return string("Video SCSI");
00875         case PhysConn_Video_AUX:              return string("Video AUX");
00876         case PhysConn_Video_1394:             return string("Video 1394");
00877         case PhysConn_Video_USB:              return string("Video USB");
00878         case PhysConn_Video_VideoDecoder:     return string("Video Decoder");
00879         case PhysConn_Video_VideoEncoder:     return string("Video Encoder");
00880             
00881         case PhysConn_Audio_Tuner:            return string("Audio Tuner");
00882         case PhysConn_Audio_Line:             return string("Audio Line");
00883         case PhysConn_Audio_Mic:              return string("Audio Microphone");
00884         case PhysConn_Audio_AESDigital:       return string("Audio AES/EBU Digital");
00885         case PhysConn_Audio_SPDIFDigital:     return string("Audio S/PDIF");
00886         case PhysConn_Audio_SCSI:             return string("Audio SCSI");
00887         case PhysConn_Audio_AUX:              return string("Audio AUX");
00888         case PhysConn_Audio_1394:             return string("Audio 1394");
00889         case PhysConn_Audio_USB:              return string("Audio USB");
00890         case PhysConn_Audio_AudioDecoder:     return string("Audio Decoder");
00891     }
00892     return string("Unknown Type");
00893 }
00894 
00895 // エラー文字列を返します。
00896 const string CaptureScene::errorText(HRESULT hr) {
00897     vector<WCHAR> err(1024);
00898     DWORD res = AMGetErrorText(hr, &err[0], 1024);
00899     string utf8;
00900     Poco::UnicodeConverter::toUTF8(wstring(&err[0]), utf8);
00901     string::size_type i = utf8.find("¥n");
00902     if (string::npos != i) utf8 = utf8.substr(0, i); // エラー文字列から改行を除去
00903     return utf8;
00904 }
00905 
00906 bool CaptureScene::changePlaylist() {
00907     if (_main) {
00908         bool prepared = false;
00909         string pl = _main->getStatus("prepared-playlist-id");
00910         if (pl == _detectedPlaylist) {
00911             prepared = true;
00912         } else {
00913             _log.information(Poco::format("change playlist: %s", _detectedPlaylist));
00914             prepared = _main->stackPrepareContent(_detectedPlaylist);
00915         }
00916         if (prepared) {
00917             for (int i = 0; i < 10; i++) {
00918                 Poco::Thread::sleep(200);
00919                 string pl = _main->getStatus("prepared-playlist-id");
00920                 if (pl == _detectedPlaylist) {
00921                     bool res = _main->switchContent();
00922                     if (!res) {
00923                         _log.warning("failed switch playlist");
00924                     }
00925                     return res;
00926                 }
00927             }
00928             _log.warning("failed timeup switch playlist");
00929         } else {
00930             _log.warning(Poco::format("failed not prepared: %s", _detectedPlaylist));
00931         }
00932     }
00933     return false;
00934 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines