core/vidl/vidl_dshow.cxx
Go to the documentation of this file.
00001 // This is core/vidl/vidl_dshow.cxx
00002 //=========================================================================
00003 #include "vidl_dshow.h"
00004 //:
00005 // \file
00006 // \brief  DirectShow helper functions used in vidl.
00007 //
00008 // See vidl_dshow.h for details.
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 // Helper class, which should only concern this implementation.
00025 //-------------------------------------------------------------------------
00026 namespace
00027 {
00028   class com_manager
00029   {
00030     //SINGLETON_PROTECTION(com_manager);
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     //const CComPtr<IBaseFilter>& get_capture_device(int dev_num) const;
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     // SingleThreaded Support only (for now).
00063 #ifdef _WIN32_DCOM
00064     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
00065 #else
00066     CoInitialize(NULL);
00067 #endif
00068 
00069     // ***** is this necessary *****
00070     //find_capture_devices();
00071   }
00072 
00073   com_manager::~com_manager(void)
00074   {
00075     capture_devices_.clear();
00076     CoUninitialize();
00077   }
00078 
00079   //: Find existing capture devices (clears the previous list, if any).
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     // Create an enumerator for the video capture devices.
00089     CComPtr<IEnumMoniker> cap_device_enum;
00090     HRESULT hr = sys_device_enum->CreateClassEnumerator(
00091       //CLSID_LegacyAmFilterCategory,
00092       //CLSID_VideoCompressorCategory,
00093       CLSID_VideoInputDeviceCategory,
00094       &cap_device_enum, 0);
00095     if (hr != S_OK) // might be S_FALSE, and FAILED(S_FALSE) != true
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     // Iterate over the enumerated devices and add them to the map.
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           //capture_devices_.push_back(
00125           //  device_wrapper(moniker, var.bstrVal, false));
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           //capture_devices_.push_back(device_wrapper(moniker,L"N/A" + oss.str(),false));
00135           capture_devices_.insert(vcl_pair<vcl_string,CComPtr<IMoniker> >(
00136             "N/A " + oss.str(), moniker));
00137         }
00138       }
00139 
00140       moniker.Release(); // need to release before re-using
00141     }
00142   }
00143 
00144   //: Print existing capture devices.
00145   void com_manager::print_capture_device_names(void) const
00146   {
00147     //vcl_ostream_iterator<vcl_string> out(vcl_cout, "\n");
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   //: Get existing capture devices.
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   //: Get IMoniker associated with name (call find_capture_devices first).
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     // return guids FOURCC instead.
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 } // unnamed namespace
00232 
00233 //-------------------------------------------------------------------------
00234 //-------------------------------------------------------------------------
00235 //: Initialize COM (must be called before using any of these functions).
00236 void vidl_dshow::initialize_com(void)
00237 {
00238   com_manager::instance();
00239 }
00240 
00241 //: Get an error description for the given HRESULT.
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 //: Register a filter graph in ROT for external loading with GraphEdit.
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   //vcl_wcout << oss.str() << vcl_endl;
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,&reg));
00277 }
00278 
00279 //: Remove a filter graph from ROT.
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 //: Save filter graph to a *.grf file.
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   //const vcl_wstring stream_name = L"ActiveMovieGraph";
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 //: Load filter graph from a *.grf file.
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   { // might return S_FALSE; can't use FAILED(hr)
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 //: Get GUID name or FOURCC.
00350 vcl_string vidl_dshow::get_guid_name(const GUID& guid)
00351 {
00352   return guid_name_list::get_name(guid);
00353 }
00354 
00355 //: Get multimedia subtype GUID from FOURCC.
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 //: Extract information from AM_MEDIA_TYPE object.
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   // Examine the format block.
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     // SampleGrabber requires VIDEOINFOHEADER type.
00401     //   Wrong format. Free the format block and bail out.
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   { // MEDIASUBTYPE_DX50
00456     pixel_format = VIDL_PIXEL_FORMAT_UNKNOWN;
00457   }
00458   else if (amt.subtype == vidl_dshow::get_guid_from_fourcc("MP4S"))
00459   { // WMMEDIASUBTYPE_MP4S
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 //: Delete AM_MEDIA_TYPE memory.
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     // Unnecessary because pUnk should not be used, but safest.
00481     amt.pUnk->Release();
00482     amt.pUnk = 0;
00483   }
00484 }
00485 
00486 //: Print a list of capture device names (i.e., FriendlyName)
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 //: Get a list of capture device names (i.e., FriendlyName)
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 //: Get IMoniker associated with name.
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 //: Connect two filters directly. ***** check this, might have errors
00518 void vidl_dshow::connect_filters(CComPtr<IFilterGraph2>& filter_graph,
00519                                  CComPtr<IBaseFilter>& source,
00520                                  CComPtr<IBaseFilter>& target)
00521 {
00522   //assert(!filter_graph);
00523   //assert(!source);
00524   //assert(!target);
00525 
00526   // All the needed pin & pin enumerator pointers
00527   CComPtr<IEnumPins> source_pins_enum;
00528   CComPtr<IEnumPins> target_pins_enum;
00529 
00530   CComPtr<IPin> target_pin;
00531 
00532   // Get the pin enumerators for both the filters
00533   DSHOW_ERROR_IF_FAILED(source->EnumPins(&source_pins_enum));
00534   DSHOW_ERROR_IF_FAILED(target->EnumPins(&target_pins_enum));
00535 
00536   // Loop on every pin on the source Filter
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     // Make sure that we have the output pin of the source filter
00543     if (FAILED(source_pin->QueryDirection(&pin_dir))
00544       || pin_dir == PINDIR_INPUT)
00545     {
00546       source_pin.Release();
00547       continue;
00548     }
00549 
00550     // I have an output pin; loop on every pin on the target Filter
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       // Try to connect them and exit if u can, else loop more
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