svvitch
digital signage player
|
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 }