core/vidl/vidl_dshow_live_istream.cxx
Go to the documentation of this file.
00001 // This is core/vidl/vidl_dshow_live_istream.cxx
00002 //=========================================================================
00003 #include "vidl_dshow_live_istream.h"
00004 //:
00005 // \file
00006 // \brief  DirectShow live video input stream support.
00007 //
00008 // See vidl_dshow_live_istream.h for details.
00009 //
00010 //=========================================================================
00011 
00012 #include <vidl/vidl_config.h>
00013 #include <vidl/vidl_dshow.h>
00014 #include <vcl_cassert.h>
00015 #include <vcl_cstring.h> // for memcpy
00016 
00017 //-------------------------------------------------------------------------
00018 //-------------------------------------------------------------------------
00019 sample_grabber_cb::sample_grabber_cb(void)
00020   : busy_index_(-1)
00021   , curr_index_(-1)
00022   , next_index_(0)
00023 {
00024   mutex_ = CreateMutex(0, false ,0);
00025 }
00026 
00027 STDMETHODIMP
00028 sample_grabber_cb::QueryInterface(REFIID riid, void **target)
00029 {
00030   if (target == 0) return E_POINTER;
00031   if (riid == __uuidof(IUnknown))
00032   {
00033     *target = static_cast<IUnknown*>(this);
00034     return S_OK;
00035   }
00036   if (riid == __uuidof(ISampleGrabberCB))
00037   {
00038     *target = static_cast<ISampleGrabberCB*>(this);
00039     return S_OK;
00040   }
00041   return E_NOTIMPL;
00042 }
00043 
00044 //: Retrieves the original media sample.
00045 STDMETHODIMP
00046 sample_grabber_cb::SampleCB(double time, IMediaSample *sample)
00047 {
00048   assert(sample);
00049 
00050   BYTE *buffer;
00051   DSHOW_ERROR_IF_FAILED(sample->GetPointer(&buffer));
00052 
00053   return BufferCB(time, buffer, sample->GetSize());
00054 }
00055 
00056 //: Retrieves a copy of the media sample (requires SetBufferSamples(true)).
00057 STDMETHODIMP
00058 sample_grabber_cb::BufferCB(double time, BYTE* buffer, long buffer_size)
00059 {
00060   assert(buffer);
00061 
00062   // allocate space for the buffer, if necessary
00063   const unsigned int i = next_index_;
00064   buffer_[i].resize(buffer_size);
00065   buffer_time_[i] = time;
00066 
00067   // copy buffer
00068   vcl_memcpy(&buffer_[i][0], buffer, buffer_size);
00069 
00070   // reset flags to reflect new state
00071   WaitForSingleObject(mutex_, INFINITE);
00072   curr_index_ = next_index_;
00073   next_index_ = (next_index_ + 1) % 3;
00074   if (next_index_ == busy_index_)
00075   {
00076     next_index_ = (next_index_ + 1) % 3;
00077   }
00078   ReleaseMutex(mutex_);
00079 
00080   return S_FALSE;
00081 }
00082 
00083 void sample_grabber_cb::advance(void)
00084 {
00085   busy_index_ = -1;
00086   while (busy_index_ == -1)
00087   {
00088     WaitForSingleObject(mutex_, INFINITE);
00089     if (curr_index_ != -1)
00090     {
00091       busy_index_ = curr_index_;
00092       curr_index_ = -1;
00093     }
00094     ReleaseMutex(mutex_);
00095     Sleep(0);
00096   }
00097 }
00098 
00099 vidl_frame_sptr sample_grabber_cb::current_frame(void)
00100 {
00101   return new vidl_shared_frame(&buffer_[busy_index_][0],
00102                                buffer_[busy_index_].size(), 1,
00103                                VIDL_PIXEL_FORMAT_UNKNOWN);
00104 }
00105 
00106 //-------------------------------------------------------------------------
00107 // vidl_dshow_live_istream implementation - construction & destruction
00108 //-------------------------------------------------------------------------
00109 //: Constructor - default
00110 template <class ParamsObject>
00111 vidl_dshow_live_istream<ParamsObject>::vidl_dshow_live_istream(void)
00112   : params_(ParamsObject())
00113   , register_(0)
00114 {
00115   // connect to the first available device
00116   vcl_vector<vcl_string> names = vidl_dshow::get_capture_device_names();
00117   if (names.size() > 0)
00118   {
00119     params_.set_device_name(names[0]);
00120     connect();
00121   }
00122   else
00123   {
00124     vidl_exception_error(
00125       vidl_dshow_exception("No capture devices found."));
00126   }
00127 }
00128 
00129 //: Constructor - from a string containing a device name.
00130 template <class ParamsObject>
00131 vidl_dshow_live_istream<ParamsObject>
00132 ::vidl_dshow_live_istream(const vcl_string& device_name)
00133   : params_(ParamsObject().set_device_name(device_name))
00134   , register_(0)
00135 {
00136   connect();
00137 }
00138 
00139 //: Constructor - from a parameter object.
00140 template <class ParamsObject>
00141 vidl_dshow_live_istream<ParamsObject>
00142 ::vidl_dshow_live_istream(const ParamsObject& params)
00143   : params_(params) // ***** dynamic_cast<const ParamsObject&>
00144   , register_(0)
00145 {
00146   connect();
00147 }
00148 
00149 // *****
00150 ////: Destructor.
00151 //template <class ParamsObject>
00152 //vidl_dshow_live_istream<ParamsObject>::~vidl_dshow_live_istream(void)
00153 //{
00154 //  close();
00155 //}
00156 
00157 //: Connect to the device specified in params object.
00158 template <class ParamsObject>
00159 void vidl_dshow_live_istream<ParamsObject>::connect(void)
00160 {
00161   // ***** no re-connection allowed, yet...
00162   //close();
00163 
00164   // connect to the device...
00165   moniker_ = vidl_dshow::get_capture_device_moniker(params_.device_name());
00166   if (!moniker_)
00167   {
00168     vidl_exception_error(vidl_exception("Requested device not found."));
00169   }
00170 
00171   // ***** start: build the filter graph here *****
00172   // create the filter graph manager
00173   DSHOW_ERROR_IF_FAILED(filter_graph_.CoCreateInstance(CLSID_FilterGraph));
00174 
00175   // create the capture graph builder
00176   CComPtr<ICaptureGraphBuilder2> graph_builder;
00177   DSHOW_ERROR_IF_FAILED(
00178     graph_builder.CoCreateInstance(CLSID_CaptureGraphBuilder2));
00179 
00180   // initialize the capture graph builder
00181   graph_builder->SetFiltergraph(filter_graph_);
00182 
00183   // add the selected source filter to filter graph
00184   CComPtr<IBaseFilter> source_filter;
00185   DSHOW_ERROR_IF_FAILED(filter_graph_->AddSourceFilterForMoniker(
00186     moniker_, 0, L"Source", &source_filter));
00187 
00188   // configure filter based on params structure
00189   params_.configure_filter(source_filter);
00190   // ***** for debugging only
00191   //params_.print_parameter_help(source_filter);
00192 
00193   // create sample grabber
00194   CComPtr<ISampleGrabber> sample_grabber;
00195   DSHOW_ERROR_IF_FAILED(
00196     sample_grabber.CoCreateInstance(CLSID_SampleGrabber));
00197   sample_grabber->SetBufferSamples(false);
00198   sample_grabber->SetOneShot(false);
00199   sample_grabber->SetCallback(&sample_grabber_callback_, 0);
00200 
00201   // set target output format type
00202   if (params_.target_output_format() != GUID_NULL)
00203   {
00204     AM_MEDIA_TYPE media_type;
00205     ZeroMemory(&media_type, sizeof(AM_MEDIA_TYPE));
00206     media_type.majortype = MEDIATYPE_Video;
00207     media_type.subtype = params_.target_output_format();
00208     sample_grabber->SetMediaType(&media_type);
00209   }
00210 
00211   // add sample grabber to the filter graph
00212   CComQIPtr<IBaseFilter> sample_grabber_filter(sample_grabber);
00213   DSHOW_ERROR_IF_FAILED(
00214     filter_graph_->AddFilter(sample_grabber_filter, L"Sample Grabber"));
00215 
00216   // create a null renderer or a file writing section
00217   CComPtr<IBaseFilter> filter;
00218   if (params_.output_filename() == "")
00219   {
00220     DSHOW_ERROR_IF_FAILED(filter.CoCreateInstance(CLSID_NullRenderer));
00221     DSHOW_ERROR_IF_FAILED(filter_graph_->AddFilter(filter, L"Null Renderer"));
00222   }
00223   else
00224   {
00225     DSHOW_ERROR_IF_FAILED(graph_builder->SetOutputFileName(
00226       &MEDIASUBTYPE_Avi,
00227       CA2W(params_.output_filename().c_str()),
00228       &filter, 0));
00229   }
00230 
00231   //CComPtr<IBaseFilter> vmr;
00232   //DSHOW_ERROR_IF_FAILED(vmr.CoCreateInstance(CLSID_VideoMixingRenderer));
00233   //DSHOW_ERROR_IF_FAILED(filter_graph_->AddFilter(vmr, L"Video Mixing Renderer"));
00234 
00235   // connect the filters
00236   DSHOW_ERROR_IF_FAILED(graph_builder->RenderStream(&PIN_CATEGORY_CAPTURE,
00237                                                     &MEDIATYPE_Video,
00238                                                     source_filter,
00239                                                     sample_grabber_filter,
00240                                                     filter));
00241 
00242   //DSHOW_ERROR_IF_FAILED(graph_builder->RenderStream(&PIN_CATEGORY_PREVIEW,
00243   //                                                  &MEDIATYPE_Video,
00244   //                                                  source_filter,
00245   //                                                  0, 0));
00246   // ***** end: build the filter graph here *****
00247 
00248   // **** testing renderers *****
00249   //CComPtr<IBaseFilter> vmr;
00250   //DSHOW_ERROR_IF_FAILED(vmr.CoCreateInstance(CLSID_VideoMixingRenderer));
00251   //DSHOW_ERROR_IF_FAILED(filter_graph_->AddFilter(vmr, L"Video Mixing Renderer"));
00252   ////CComQIPtr<IVMRFilterConfig> vmr_config(vmr);
00253   ////vmr_config->SetRenderingMode(VMRMode_Windowless);
00254   ////vmr_config->SetNumberOfStreams(1);
00255   ////CComQIPtr<IVideoWindow> video_window(vmr);
00256   ////video_window->put_AutoShow(OATRUE);
00257   ////video_window->put_Visible(OATRUE);
00258   ////video_window->put_FullScreenMode(OATRUE);
00259   //DSHOW_ERROR_IF_FAILED(graph_builder->RenderStream(&PIN_CATEGORY_PREVIEW,
00260   //                                                  &MEDIATYPE_Video,
00261   //                                                  source_filter,
00262   //                                                  0,
00263   //                                                  vmr));
00264   // **** testing renderers *****
00265 
00266   // ***** should I provide access to this through the public interface???
00267   //vidl_dshow::load_graph_from_file(filter_graph_, L"testing.grf");
00268 
00269   // ***** should I provide access to this through the public interface???
00270   if (params_.save_graph_to() != "")
00271   {
00272     vidl_dshow::save_graph_to_file(filter_graph_, params_.save_graph_to());
00273   }
00274 
00275   // get frame format information
00276   AM_MEDIA_TYPE media_type;
00277   DSHOW_ERROR_IF_FAILED(
00278     sample_grabber->GetConnectedMediaType(&media_type));
00279   vidl_dshow::get_media_info(media_type,
00280                              buffer_width_,
00281                              buffer_height_,
00282                              buffer_pixel_format_);
00283   vidl_dshow::delete_media_type(media_type);
00284 
00285   // ***** MSDN docs suggest turning the graph clock off (if not needed)
00286   //       for running the graph faster. Check this out.
00287   // *****
00288   //if (params_.turn_clock_off())
00289   //{
00290   //  CComQIPtr<IMediaFilter> media_filter(filter_graph_);
00291   //  media_filter->SetSyncSource(0);
00292   //}
00293 
00294   if (params_.register_in_rot())
00295   {
00296     vidl_dshow::register_in_rot(filter_graph_, register_);
00297   }
00298 
00299   filter_graph_->QueryInterface(
00300     IID_IMediaControl, reinterpret_cast<void**>(&media_control_));
00301 
00302   if (params_.run_when_ready())
00303   {
00304     run();
00305   }
00306   else
00307   {
00308     pause();
00309   }
00310 }
00311 
00312 template <class ParamsObject>
00313 inline void vidl_dshow_live_istream<ParamsObject>::close(void)
00314 {
00315   stop();
00316 
00317   if (register_ != 0)
00318   {
00319     vidl_dshow::remove_from_rot(register_);
00320   }
00321 
00322   media_control_.Release();
00323   moniker_.Release();
00324   filter_graph_.Release();
00325 }
00326 
00327 //-------------------------------------------------------------------------
00328 // vidl_dshow_live_istream implementation
00329 //-------------------------------------------------------------------------
00330 //: Initiate advance and wait for completion; synchronous advance.
00331 template <class ParamsObject>
00332 inline bool vidl_dshow_live_istream<ParamsObject>::advance_wait(void)
00333 {
00334   if (!advance_start()) { return false; }
00335   while (!is_frame_available()) { Sleep(0); }
00336   return true;
00337 }
00338 
00339 //: Initiate advance and return immediately; asynchronous advance.
00340 template <class ParamsObject>
00341 inline bool vidl_dshow_live_istream<ParamsObject>::advance_start(void)
00342 {
00343   sample_grabber_callback_.advance();
00344   return true;
00345 }
00346 
00347 //: Advance to the next frame (but don't acquire an image).
00348 template <class ParamsObject>
00349 inline bool
00350 vidl_dshow_live_istream<ParamsObject>::is_frame_available(void) const
00351 {
00352   return true;
00353 }
00354 
00355 //: Read the next frame from the stream (advance and acquire).
00356 template <class ParamsObject>
00357 inline vidl_frame_sptr
00358 vidl_dshow_live_istream<ParamsObject>::read_frame(void)
00359 {
00360   if (!advance_wait()) { return 0; }
00361   return current_frame();
00362 }
00363 
00364 //: Return the current frame in the stream
00365 template <class ParamsObject>
00366 inline vidl_frame_sptr
00367 vidl_dshow_live_istream<ParamsObject>::current_frame(void)
00368 {
00369   if (buffer_pixel_format_ == VIDL_PIXEL_FORMAT_UNKNOWN)
00370   {
00371     return sample_grabber_callback_.current_frame();
00372   }
00373   else
00374   {
00375     return new vidl_shared_frame(
00376       sample_grabber_callback_.current_frame()->data(),
00377       buffer_width_,
00378       buffer_height_,
00379       buffer_pixel_format_);
00380   }
00381 }
00382 
00383 template <class ParamsObject>
00384 inline void vidl_dshow_live_istream<ParamsObject>
00385 ::wait_for_state_change(HRESULT hr)
00386 {
00387   if (hr == S_FALSE)
00388   {
00389     OAFilterState state;
00390     DSHOW_ERROR_IF_FAILED(media_control_->GetState(INFINITE, &state));
00391   }
00392 }
00393 
00394 template <class ParamsObject>
00395 inline void vidl_dshow_live_istream<ParamsObject>::run(void)
00396 {
00397   wait_for_state_change(media_control_->Run());
00398 }
00399 
00400 template <class ParamsObject>
00401 inline void vidl_dshow_live_istream<ParamsObject>::pause(void)
00402 {
00403   wait_for_state_change(media_control_->Pause());
00404 }
00405 
00406 template <class ParamsObject>
00407 inline void vidl_dshow_live_istream<ParamsObject>::stop(void)
00408 {
00409   wait_for_state_change(media_control_->Stop());
00410 }
00411 
00412 // ***** make these with the usual *.txx macros? *****
00413 // verify these steps with vil library before attempting
00414 // 1. put this file into vidl_dshow_istream.txx
00415 // 2. put the macros in vidl_dshow_istream.h
00416 // 3. put the templates in:
00417 //      templates/vidl_dshow_istream+vidl_dshow_istream_params-.cxx
00418 //      templates/vidl_dshow_istream+vidl_dshow_istream_params_esf-.cxx
00419 #include <vidl/vidl_dshow_istream_params.h>
00420 template class vidl_dshow_live_istream<vidl_dshow_istream_params>;
00421 
00422 #if VIDL_HAS_DSHOW_ESF
00423   #include <vidl/vidl_dshow_istream_params_esf.h>
00424   template class vidl_dshow_live_istream<vidl_dshow_istream_params_esf>;
00425 #endif // HAS_EURESYS_ESF