00001
00002
00003 #include "vidl_dshow.h"
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include <vidl/vidl_exception.h>
00013 #include <vidl/vidl_pixel_format.h>
00014
00015 #include <vcl_cassert.h>
00016 #include <vcl_iostream.h>
00017 #include <vcl_sstream.h>
00018 #include <vcl_map.h>
00019 #include <vcl_utility.h>
00020 #include <vcl_iterator.h>
00021 #include <vcl_algorithm.h>
00022
00023
00024
00025
00026 namespace
00027 {
00028 class com_manager
00029 {
00030
00031
00032 public:
00033 void find_capture_devices();
00034 void print_capture_device_names() const;
00035 vcl_vector<vcl_string> get_capture_device_names() const;
00036 CComPtr<IMoniker> get_capture_device_moniker(const vcl_string& name) const;
00037
00038
00039
00040 static com_manager& instance(void)
00041 {
00042 if (!instance_) { instance_ = new com_manager; }
00043 return *instance_;
00044 }
00045
00046 private:
00047 com_manager();
00048 ~com_manager();
00049 com_manager(const com_manager&);
00050 com_manager& operator=(const com_manager&);
00051 com_manager* operator&();
00052
00053 static com_manager* instance_;
00054
00055 vcl_map<vcl_string,CComPtr<IMoniker> > capture_devices_;
00056 };
00057
00058 com_manager* com_manager::instance_ = 0;
00059
00060 com_manager::com_manager(void)
00061 {
00062
00063 #ifdef _WIN32_DCOM
00064 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
00065 #else
00066 CoInitialize(NULL);
00067 #endif
00068
00069
00070
00071 }
00072
00073 com_manager::~com_manager(void)
00074 {
00075 capture_devices_.clear();
00076 CoUninitialize();
00077 }
00078
00079
00080 void com_manager::find_capture_devices(void)
00081 {
00082 capture_devices_.clear();
00083
00084 CComPtr<ICreateDevEnum> sys_device_enum;
00085 DSHOW_ERROR_IF_FAILED(
00086 sys_device_enum.CoCreateInstance(CLSID_SystemDeviceEnum));
00087
00088
00089 CComPtr<IEnumMoniker> cap_device_enum;
00090 HRESULT hr = sys_device_enum->CreateClassEnumerator(
00091
00092
00093 CLSID_VideoInputDeviceCategory,
00094 &cap_device_enum, 0);
00095 if (hr != S_OK)
00096 {
00097 vcl_cerr << "Category doesn't exist or is empty.\n"
00098 << DSHOW_GET_ERROR_TEXT(hr) << vcl_endl;
00099 return;
00100 }
00101
00102
00103 CComPtr<IMoniker> moniker;
00104 while (cap_device_enum->Next(1, &moniker, 0) == S_OK)
00105 {
00106 CComPtr<IPropertyBag> property_bag;
00107 hr = moniker->BindToStorage(0, 0, IID_IPropertyBag,
00108 reinterpret_cast<void**>(&property_bag));
00109 if (FAILED(hr))
00110 {
00111 vcl_cerr << "BindToStorage failed for device "
00112 << capture_devices_.size() + 1 << '\n'
00113 << DSHOW_GET_ERROR_TEXT(hr) << vcl_endl;
00114 continue;
00115 }
00116 else
00117 {
00118 CComVariant var;
00119
00120 if (SUCCEEDED(property_bag->Read(L"FriendlyName", &var, 0)))
00121 {
00122 assert(var.vt == VT_BSTR);
00123
00124
00125
00126 capture_devices_.insert(vcl_pair<vcl_string,CComPtr<IMoniker> >(
00127 vcl_string(CW2A(var.bstrVal)), moniker));
00128 }
00129 else
00130 {
00131 vcl_ostringstream oss;
00132 oss << capture_devices_.size();
00133
00134
00135 capture_devices_.insert(vcl_pair<vcl_string,CComPtr<IMoniker> >(
00136 "N/A " + oss.str(), moniker));
00137 }
00138 }
00139
00140 moniker.Release();
00141 }
00142 }
00143
00144
00145 void com_manager::print_capture_device_names(void) const
00146 {
00147
00148 vcl_vector<vcl_string> names = get_capture_device_names();
00149
00150 vcl_copy(names.begin(),
00151 names.end(),
00152 vcl_ostream_iterator<vcl_string>(vcl_cout, "\n"));
00153 }
00154
00155
00156 vcl_vector<vcl_string> com_manager::get_capture_device_names(void) const
00157 {
00158 vcl_vector<vcl_string> names;
00159
00160 vcl_map<vcl_string,CComPtr<IMoniker> >::const_iterator iterator
00161 = capture_devices_.begin();
00162
00163 while (iterator != capture_devices_.end())
00164 {
00165 names.push_back(iterator->first);
00166 iterator++;
00167 }
00168
00169 return names;
00170 }
00171
00172
00173 CComPtr<IMoniker>
00174 com_manager::get_capture_device_moniker(const vcl_string& name) const
00175 {
00176 vcl_map<vcl_string,CComPtr<IMoniker> >::const_iterator iterator
00177 = capture_devices_.find(name);
00178
00179 return iterator != capture_devices_.end() ? iterator->second : 0;
00180 }
00181 }
00182
00183
00184
00185 namespace
00186 {
00187 struct guid_string_entry
00188 {
00189 char* name;
00190 GUID guid;
00191 };
00192
00193 class guid_name_list
00194 {
00195 public:
00196 static vcl_string get_name(const GUID& guid);
00197
00198 private:
00199 static guid_string_entry names[];
00200 static unsigned int count;
00201 };
00202
00203 guid_string_entry guid_name_list::names[] = {
00204 #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
00205 { #name, { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } },
00206 #include <uuids.h>
00207 };
00208 #undef OUR_GUID_ENTRY
00209
00210 unsigned int guid_name_list::count
00211 = sizeof(guid_name_list::names) / sizeof(guid_name_list::names[0]);
00212
00213 vcl_string guid_name_list::get_name(const GUID &guid)
00214 {
00215 if (guid == GUID_NULL) { return "GUID_NULL"; }
00216
00217 for (unsigned int i = 0; i < count; i++)
00218 {
00219 if (names[i].guid == guid) { return names[i].name; }
00220 }
00221
00222
00223 vcl_string fourcc;
00224 fourcc.push_back(static_cast<char>((guid.Data1 & 0x000000FF) ));
00225 fourcc.push_back(static_cast<char>((guid.Data1 & 0x0000FF00) >> 8));
00226 fourcc.push_back(static_cast<char>((guid.Data1 & 0x00FF0000) >> 16));
00227 fourcc.push_back(static_cast<char>((guid.Data1 & 0xFF000000) >> 24));
00228
00229 return fourcc;
00230 }
00231 }
00232
00233
00234
00235
00236 void vidl_dshow::initialize_com(void)
00237 {
00238 com_manager::instance();
00239 }
00240
00241
00242 vcl_string
00243 vidl_dshow::get_error_text(const char* file, int line, HRESULT hr)
00244 {
00245 TCHAR err[MAX_ERROR_TEXT_LEN];
00246 DWORD result = AMGetErrorText(hr, err, MAX_ERROR_TEXT_LEN);
00247
00248 vcl_ostringstream oss;
00249 oss << file << ':' << line << ':';
00250 result
00251 ? oss << CT2A(err)
00252 : oss << "Unknown Error (" << vcl_hex << hr << ')';
00253
00254 return oss.str();
00255 }
00256
00257
00258 void vidl_dshow::register_in_rot(IUnknown* unknown, DWORD& reg)
00259 {
00260 assert(unknown != 0);
00261
00262 CComPtr<IRunningObjectTable> rot;
00263 DSHOW_ERROR_IF_FAILED(GetRunningObjectTable(0, &rot));
00264
00265 vcl_wostringstream oss;
00266 oss << L"FilterGraph "
00267 << vcl_hex << reinterpret_cast<DWORD>(unknown)
00268 << L" pid "
00269 << vcl_hex << GetCurrentProcessId();
00270
00271
00272 CComPtr<IMoniker> moniker;
00273 DSHOW_ERROR_IF_FAILED(
00274 CreateItemMoniker(L"!", oss.str().c_str(), &moniker));
00275 DSHOW_ERROR_IF_FAILED(
00276 rot->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, unknown, moniker,®));
00277 }
00278
00279
00280 void vidl_dshow::remove_from_rot(DWORD reg)
00281 {
00282 CComPtr<IRunningObjectTable> rot;
00283 DSHOW_ERROR_IF_FAILED(GetRunningObjectTable(0, &rot));
00284 DSHOW_ERROR_IF_FAILED(rot->Revoke(reg));
00285 }
00286
00287
00288 void vidl_dshow::save_graph_to_file(const CComPtr<IFilterGraph2>& filter_graph,
00289 const vcl_string& filename)
00290 {
00291 assert(filter_graph != 0);
00292
00293 CComPtr<IStorage> storage;
00294 DSHOW_ERROR_IF_FAILED(StgCreateDocfile(CA2W(filename.c_str()),
00295 STGM_CREATE
00296 | STGM_TRANSACTED
00297 | STGM_READWRITE
00298 | STGM_SHARE_EXCLUSIVE,
00299 0, &storage));
00300
00301 CComPtr<IStream> stream;
00302
00303 DSHOW_ERROR_IF_FAILED(storage->CreateStream(L"ActiveMovieGraph",
00304 STGM_CREATE
00305 | STGM_WRITE
00306 | STGM_SHARE_EXCLUSIVE,
00307 0, 0, &stream));
00308
00309 CComPtr<IPersistStream> persist_stream;
00310 filter_graph->QueryInterface(IID_IPersistStream,
00311 reinterpret_cast<void**>(&persist_stream));
00312
00313 DSHOW_ERROR_IF_FAILED(persist_stream->Save(stream, TRUE));
00314 DSHOW_ERROR_IF_FAILED(storage->Commit(STGC_DEFAULT));
00315 }
00316
00317
00318 void vidl_dshow::load_graph_from_file(const CComPtr<IFilterGraph2>& filter_graph,
00319 const vcl_wstring& filename)
00320 {
00321 assert(filter_graph != 0);
00322
00323 CComPtr<IStorage> storage;
00324 if (S_OK != StgIsStorageFile(filename.c_str()))
00325 {
00326 vidl_exception_error(vidl_dshow_exception(
00327 "Not a storage file.\n" + DSHOW_GET_ERROR_TEXT(E_FAIL)));
00328 }
00329 DSHOW_ERROR_IF_FAILED(StgOpenStorage(filename.c_str(), 0,
00330 STGM_TRANSACTED
00331 | STGM_READ
00332 | STGM_SHARE_DENY_WRITE,
00333 0, 0, &storage));
00334
00335 CComPtr<IPersistStream> persist_stream;
00336
00337 filter_graph->QueryInterface(IID_IPersistStream,
00338 reinterpret_cast<void**>(&persist_stream));
00339
00340 CComPtr<IStream> stream;
00341 DSHOW_ERROR_IF_FAILED(storage->OpenStream(L"ActiveMovieGraph", 0,
00342 STGM_READ
00343 | STGM_SHARE_EXCLUSIVE,
00344 0, &stream));
00345
00346 DSHOW_ERROR_IF_FAILED(persist_stream->Load(stream));
00347 }
00348
00349
00350 vcl_string vidl_dshow::get_guid_name(const GUID& guid)
00351 {
00352 return guid_name_list::get_name(guid);
00353 }
00354
00355
00356 GUID vidl_dshow::get_guid_from_fourcc(const vcl_string& fourcc)
00357 {
00358 unsigned long fourcc_cast = static_cast<unsigned long>(fourcc[0])
00359 | static_cast<unsigned long>(fourcc[1]) << 8
00360 | static_cast<unsigned long>(fourcc[2]) << 16
00361 | static_cast<unsigned long>(fourcc[3]) << 24;
00362
00363 const GUID guid = {
00364 fourcc_cast,
00365 0x0000,
00366 0x0010,
00367 { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }
00368 };
00369
00370 return guid;
00371 }
00372
00373
00374 void vidl_dshow::get_media_info(const AM_MEDIA_TYPE& amt,
00375 unsigned int& width,
00376 unsigned int& height,
00377 vidl_pixel_format& pixel_format)
00378 {
00379
00380 if ( (amt.formattype == FORMAT_VideoInfo) &&
00381 (amt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
00382 (amt.pbFormat != 0) )
00383 {
00384 VIDEOINFOHEADER *vih;
00385 vih = reinterpret_cast<VIDEOINFOHEADER*>(amt.pbFormat);
00386
00387 width = vih->bmiHeader.biWidth;
00388 height = vih->bmiHeader.biHeight < 0
00389 ? -vih->bmiHeader.biHeight
00390 : vih->bmiHeader.biHeight;
00391 #if 0 // ***** figure out how to handle bottom_up dibs...
00392 if (vih->bmiHeader.biHeight < 0)
00393 {
00394 is_bottom_up_ = true;
00395 }
00396 #endif
00397 }
00398 else
00399 {
00400
00401
00402 DSHOW_ERROR_IF_FAILED(VFW_E_INVALIDMEDIATYPE);
00403 }
00404
00405 if (amt.subtype == MEDIASUBTYPE_RGB24)
00406 {
00407 pixel_format = VIDL_PIXEL_FORMAT_BGR_24;
00408 }
00409 else if (amt.subtype == MEDIASUBTYPE_RGB555)
00410 {
00411 pixel_format = VIDL_PIXEL_FORMAT_RGB_555;
00412 }
00413 else if (amt.subtype == MEDIASUBTYPE_RGB565)
00414 {
00415 pixel_format = VIDL_PIXEL_FORMAT_RGB_565;
00416 }
00417 else if (amt.subtype == MEDIASUBTYPE_RGB8)
00418 {
00419 pixel_format = VIDL_PIXEL_FORMAT_MONO_8;
00420 }
00421 #if 0 // ***** add this one to vidl format list...
00422 else if (amt.subtype == MEDIASUBTYPE_ARGB32)
00423 {
00424 pixel_format = VIDL_PIXEL_FORMAT_ABGR_32;
00425 }
00426 #endif
00427 else if (amt.subtype == MEDIASUBTYPE_YUY2)
00428 {
00429 pixel_format = VIDL_PIXEL_FORMAT_YUYV_422;
00430 }
00431 else if (amt.subtype == MEDIASUBTYPE_UYVY)
00432 {
00433 pixel_format = VIDL_PIXEL_FORMAT_UYVY_422;
00434 }
00435 else if (amt.subtype == MEDIASUBTYPE_YV12)
00436 {
00437 pixel_format = VIDL_PIXEL_FORMAT_YVU_420P;
00438 }
00439 else if (amt.subtype == MEDIASUBTYPE_IYUV ||
00440 amt.subtype == vidl_dshow::get_guid_from_fourcc("I420"))
00441 {
00442 pixel_format = VIDL_PIXEL_FORMAT_YUV_420P;
00443 }
00444 else if (amt.subtype == MEDIASUBTYPE_Y41P)
00445 {
00446 pixel_format = VIDL_PIXEL_FORMAT_UYVY_411;
00447 }
00448 #if 0 // ***** what's the equivalent for this one?
00449 else if (amt.subtype == MEDIASUBTYPE_YVU9)
00450 {
00451 pixel_format = ;
00452 }
00453 #endif
00454 else if (amt.subtype == vidl_dshow::get_guid_from_fourcc("DX50"))
00455 {
00456 pixel_format = VIDL_PIXEL_FORMAT_UNKNOWN;
00457 }
00458 else if (amt.subtype == vidl_dshow::get_guid_from_fourcc("MP4S"))
00459 {
00460 pixel_format = VIDL_PIXEL_FORMAT_UNKNOWN;
00461 }
00462 else
00463 {
00464 pixel_format = VIDL_PIXEL_FORMAT_UNKNOWN;
00465 DSHOW_WARN_IF_FAILED(VFW_E_INVALIDMEDIATYPE);
00466 }
00467 }
00468
00469
00470 void vidl_dshow::delete_media_type(AM_MEDIA_TYPE& amt)
00471 {
00472 if (amt.cbFormat != 0)
00473 {
00474 CoTaskMemFree(reinterpret_cast<void*>(amt.pbFormat));
00475 amt.cbFormat = 0;
00476 amt.pbFormat = 0;
00477 }
00478 if (amt.pUnk != 0)
00479 {
00480
00481 amt.pUnk->Release();
00482 amt.pUnk = 0;
00483 }
00484 }
00485
00486
00487 void vidl_dshow::print_capture_device_names(void)
00488 {
00489 com_manager& com = com_manager::instance();
00490
00491 com.find_capture_devices();
00492
00493 com.print_capture_device_names();
00494 }
00495
00496
00497 vcl_vector<vcl_string> vidl_dshow::get_capture_device_names(void)
00498 {
00499 com_manager& com = com_manager::instance();
00500
00501 com.find_capture_devices();
00502
00503 return com.get_capture_device_names();
00504 }
00505
00506
00507 CComPtr<IMoniker>
00508 vidl_dshow::get_capture_device_moniker(const vcl_string& name)
00509 {
00510 com_manager& com = com_manager::instance();
00511
00512 com.find_capture_devices();
00513
00514 return com.get_capture_device_moniker(name);
00515 }
00516
00517
00518 void vidl_dshow::connect_filters(CComPtr<IFilterGraph2>& filter_graph,
00519 CComPtr<IBaseFilter>& source,
00520 CComPtr<IBaseFilter>& target)
00521 {
00522
00523
00524
00525
00526
00527 CComPtr<IEnumPins> source_pins_enum;
00528 CComPtr<IEnumPins> target_pins_enum;
00529
00530 CComPtr<IPin> target_pin;
00531
00532
00533 DSHOW_ERROR_IF_FAILED(source->EnumPins(&source_pins_enum));
00534 DSHOW_ERROR_IF_FAILED(target->EnumPins(&target_pins_enum));
00535
00536
00537 PIN_DIRECTION pin_dir;
00538
00539 CComPtr<IPin> source_pin;
00540 while (S_OK == source_pins_enum->Next(1, &source_pin, 0))
00541 {
00542
00543 if (FAILED(source_pin->QueryDirection(&pin_dir))
00544 || pin_dir == PINDIR_INPUT)
00545 {
00546 source_pin.Release();
00547 continue;
00548 }
00549
00550
00551 while (S_OK == target_pins_enum->Next(1, &target_pin, 0) )
00552 {
00553 if (FAILED(target_pin->QueryDirection(&pin_dir))
00554 || pin_dir == PINDIR_OUTPUT)
00555 {
00556 target_pin.Release();
00557 continue;
00558 }
00559
00560
00561 if (SUCCEEDED(filter_graph->ConnectDirect(source_pin, target_pin, 0)))
00562 {
00563 return;
00564 }
00565
00566 target_pin.Release();
00567 }
00568
00569 DSHOW_ERROR_IF_FAILED(target_pins_enum->Reset());
00570
00571 source_pin.Release();
00572 }
00573
00574 vidl_exception_error(vidl_dshow_exception(
00575 "Couldn't connect the filters." + DSHOW_GET_ERROR_TEXT(E_FAIL)));
00576 }
00577