core/vidl/vidl_dshow_file_istream.cxx
Go to the documentation of this file.
00001 // This is core/vidl/vidl_dshow_file_istream.cxx
00002 //=========================================================================
00003 #include "vidl_dshow_file_istream.h"
00004 //:
00005 // \file
00006 // \brief  DirectShow file input stream support.
00007 //
00008 // See vidl_dshow_file_istream.h for details.
00009 //
00010 //=========================================================================
00011 
00012 #include <vidl/vidl_dshow.h>
00013 #include <vidl/vidl_frame.h>
00014 
00015 //-------------------------------------------------------------------------
00016 // vidl_dshow_file_istream implementation - construction & destruction
00017 //-------------------------------------------------------------------------
00018 //: Constructor - from a string containing a device name.
00019 vidl_dshow_file_istream
00020 ::vidl_dshow_file_istream(const vcl_string& filename)
00021   : frame_index_(-1)
00022   , end_position_(0)
00023   , is_time_format_frame_(false)
00024   , is_valid_(false)
00025   , buffer_pixel_format_(VIDL_PIXEL_FORMAT_UNKNOWN)
00026   , buffer_width_(0)
00027   , buffer_height_(0)
00028   , register_(0)
00029 {
00030   vidl_dshow::initialize_com();
00031   open(filename);
00032 }
00033 
00034 //: Destructor.
00035 vidl_dshow_file_istream::~vidl_dshow_file_istream(void)
00036 {
00037   close();
00038 }
00039 
00040 //: Open the file specified in params object.
00041 void vidl_dshow_file_istream::open(const vcl_string& filename)
00042 {
00043   // ***** start: build the filter graph here *****
00044   DSHOW_ERROR_IF_FAILED(filter_graph_.CoCreateInstance(CLSID_FilterGraph));
00045 
00046   // create the capture graph builder
00047   CComPtr<ICaptureGraphBuilder2> graph_builder;
00048   DSHOW_ERROR_IF_FAILED(
00049     graph_builder.CoCreateInstance(CLSID_CaptureGraphBuilder2));
00050 
00051   // initialize the capture graph builder
00052   graph_builder->SetFiltergraph(filter_graph_);
00053 
00054   // add the selected source filter to filter graph
00055   CComPtr<IBaseFilter> source_filter;
00056   DSHOW_ERROR_IF_FAILED(filter_graph_->AddSourceFilter(
00057     CA2W(filename.c_str()), L"Source", &source_filter));
00058 
00059   // create sample grabber
00060   //CComPtr<ISampleGrabber> sample_grabber;
00061   DSHOW_ERROR_IF_FAILED(
00062     sample_grabber_.CoCreateInstance(CLSID_SampleGrabber));
00063   sample_grabber_->SetBufferSamples(true);
00064   sample_grabber_->SetOneShot(false);
00065 
00066   // ***** force sample grabber to a target type??
00067   //       we need to parameterize this (i.e., allow control from outside)
00068   //       So that, the user can specify the target output format...
00069   AM_MEDIA_TYPE media_type;
00070   ZeroMemory(&media_type, sizeof(AM_MEDIA_TYPE));
00071   media_type.majortype = MEDIATYPE_Video;
00072   media_type.subtype = MEDIASUBTYPE_RGB24;
00073   sample_grabber_->SetMediaType(&media_type);
00074 
00075   // add sample grabber to the filter graph
00076   CComQIPtr<IBaseFilter> sample_grabber_filter(sample_grabber_);
00077   DSHOW_ERROR_IF_FAILED(
00078     filter_graph_->AddFilter(sample_grabber_filter, L"Sample Grabber"));
00079 
00080   // create a null renderer
00081   CComPtr<IBaseFilter> null_renderer;
00082   DSHOW_ERROR_IF_FAILED(
00083     null_renderer.CoCreateInstance(CLSID_NullRenderer));
00084   DSHOW_ERROR_IF_FAILED(
00085     filter_graph_->AddFilter(null_renderer, L"Null Renderer"));
00086   //CComPtr<IBaseFilter> vmr;
00087   //DSHOW_ERROR_IF_FAILED(vmr.CoCreateInstance(CLSID_VideoMixingRenderer));
00088   //DSHOW_ERROR_IF_FAILED(filter_graph_->AddFilter(vmr, L"Video Mixing Renderer"));
00089 
00090   // connect the filters
00091   // ***** This is an ugly workaround, because RenderStream is not failing
00092   //       as it is supposed to...
00093   // *****
00094   CComPtr<IPin> pin;
00095   if (FAILED(graph_builder->FindPin(
00096         source_filter,        // Pointer to the filter to search.
00097         PINDIR_OUTPUT,        // Search for an output pin.
00098         0,                    // Search for any pin.
00099         &MEDIATYPE_Video,     // Search for a video pin.
00100         TRUE,                 // The pin must be unconnected.
00101         0,                    // Return the first matching pin (index 0).
00102         &pin)))               // This variable receives the IPin pointer.
00103   {
00104     DSHOW_ERROR_IF_FAILED(graph_builder->RenderStream(
00105       0, 0,
00106       source_filter,
00107       sample_grabber_filter,
00108       //vmr));
00109       null_renderer));
00110   }
00111   else
00112   {
00113     DSHOW_ERROR_IF_FAILED(graph_builder->RenderStream(
00114       0, &MEDIATYPE_Video,
00115       source_filter,
00116       sample_grabber_filter,
00117       null_renderer));
00118   }
00119   // ***** end: build the filter graph here *****
00120 
00121   // ***** should I provide access to this through the public interface???
00122   //vidl_dshow::save_graph_to_file(filter_graph_, L"testing2.grf");
00123 
00124   // get frame format information
00125   DSHOW_ERROR_IF_FAILED(
00126     sample_grabber_->GetConnectedMediaType(&media_type));
00127   vidl_dshow::get_media_info(media_type,
00128                              buffer_width_,
00129                              buffer_height_,
00130                              buffer_pixel_format_);
00131   vidl_dshow::delete_media_type(media_type);
00132 
00133   // ***** MSDN docs suggest turning the graph clock off (if not needed)
00134   //       for running the graph faster. Check this out.
00135   // *****
00136   //CComQIPtr<IMediaFilter> media_filter(filter_graph_);
00137   //media_filter->SetSyncSource(0);
00138 
00139   filter_graph_->QueryInterface(
00140     IID_IMediaControl, reinterpret_cast<void**>(&media_control_));
00141   if (media_control_->Pause() == S_FALSE)
00142   {
00143     OAFilterState state;
00144     DSHOW_ERROR_IF_FAILED(media_control_->GetState(INFINITE, &state));
00145   }
00146 
00147   // try to step frame by frame
00148   filter_graph_->QueryInterface(
00149     IID_IMediaSeeking, reinterpret_cast<void**>(&media_seeking_));
00150   if (SUCCEEDED(media_seeking_->SetTimeFormat(&TIME_FORMAT_FRAME)))
00151   {
00152     is_time_format_frame_ = true;
00153   }
00154 
00155   // get the end of stream position
00156   DSHOW_ERROR_IF_FAILED(media_seeking_->GetStopPosition(&end_position_));
00157 
00158   // ***** need to have params_ or add it to the interface
00159   if (false) // params_.register_in_rot())
00160   {
00161     vidl_dshow::register_in_rot(filter_graph_, register_);
00162   }
00163 }
00164 
00165 void vidl_dshow_file_istream::close(void)
00166 {
00167   // ***** do i need to stop before destroying??
00168   //CComQIPtr<IMediaControl> media_control(filter_graph_);
00169   //DSHOW_ERROR_IF_FAILED(media_control->Stop());
00170 
00171   if (register_ != 0)
00172   {
00173     vidl_dshow::remove_from_rot(register_);
00174   }
00175 
00176   sample_grabber_.Release();
00177   media_event_.Release();
00178   media_seeking_.Release();
00179   media_control_.Release();
00180   filter_graph_.Release();
00181 }
00182 
00183 //-------------------------------------------------------------------------
00184 // vidl_dshow_file_istream implementation
00185 //-------------------------------------------------------------------------
00186 //: Initiate advance and wait for completion; synchronous advance.
00187 inline bool vidl_dshow_file_istream::advance_wait(void)
00188 {
00189   if (!advance_start()) { return false; }
00190 
00191   // ***** do I need to sleep here for a bit?
00192   while (!is_frame_available());
00193 
00194   return true;
00195 }
00196 
00197 //: Blocks; no asynchronous advance supported.
00198 inline bool vidl_dshow_file_istream::advance_start(void)
00199 {
00200   if (!is_valid_ && frame_index_ != -1) { return false; }
00201 
00202   REFERENCE_TIME next;
00203   if (is_time_format_frame_)
00204   {
00205     next = frame_index_ + 1;
00206   }
00207   else
00208   {
00209     REFERENCE_TIME next_time;
00210     next_time
00211       = static_cast<REFERENCE_TIME>((frame_index_+1) * 10000000.0 / 30.0);
00212     //next_time = (frame_index_ + 1) * 10000000 / 30; // 1/30th of a second
00213     DSHOW_ERROR_IF_FAILED(media_seeking_->ConvertTimeFormat(
00214       &next, 0, next_time, &TIME_FORMAT_MEDIA_TIME));
00215   }
00216 
00217   if (next >= end_position_) { return false; }
00218 
00219   DSHOW_ERROR_IF_FAILED(media_seeking_->SetPositions(
00220     &next, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
00221     0,     AM_SEEKING_NoPositioning));
00222 
00223   buffer_time_[++buffer_index_ % 2] = next / 10000000.0;
00224 
00225   ++frame_index_;
00226   is_valid_ = true;
00227 
00228   return true;
00229 }
00230 
00231 //: Always true; no asynchronous advance supported.
00232 // ***** Apparently it is asynchronous after all...
00233 inline bool vidl_dshow_file_istream::is_frame_available(void) const
00234 {
00235   if (media_control_->Pause() == S_FALSE) { return false; }
00236 
00237   return is_valid();
00238 }
00239 
00240 //: Read the next frame from the stream (advance and acquire).
00241 inline vidl_frame_sptr vidl_dshow_file_istream::read_frame(void)
00242 {
00243   if (!advance_wait()) { return 0; }
00244   return current_frame();
00245 }
00246 
00247 //: Return the current frame in the stream
00248 inline vidl_frame_sptr vidl_dshow_file_istream::current_frame(void)
00249 {
00250   if (!is_valid_) { return 0; }
00251 
00252   // get the size needed for the buffer
00253   long buffer_size = 0;
00254   DSHOW_ERROR_IF_FAILED(sample_grabber_->GetCurrentBuffer(&buffer_size, 0));
00255 
00256   // allocate space for the buffer
00257   unsigned int i = buffer_index_ % 2;
00258   buffer_[i].resize(buffer_size);
00259 
00260   //copy the image in to the buffer
00261   DSHOW_ERROR_IF_FAILED(sample_grabber_->GetCurrentBuffer(
00262     &buffer_size, reinterpret_cast<long*>(&buffer_[i][0])));
00263 
00264   // *****
00265   //assert(buffer_size > 0);
00266 
00267   if (buffer_pixel_format_ == VIDL_PIXEL_FORMAT_UNKNOWN)
00268   {
00269     return new vidl_shared_frame(
00270       &buffer_[i][0], buffer_size, 1, VIDL_PIXEL_FORMAT_UNKNOWN);
00271   }
00272   else
00273   {
00274     return new vidl_shared_frame(
00275       &buffer_[i][0], buffer_width_, buffer_height_, buffer_pixel_format_);
00276   }
00277 }
00278 
00279 //: Seek to the given frame number.
00280 // \returns true if successful
00281 inline bool vidl_dshow_file_istream::seek_frame(unsigned int frame_number)
00282 {
00283   REFERENCE_TIME next;
00284   if (is_time_format_frame_)
00285   {
00286     next = frame_number;
00287   }
00288   else
00289   {
00290     REFERENCE_TIME next_time;
00291     next_time
00292       = static_cast<REFERENCE_TIME>(frame_number * 10000000.0 / 30.0);
00293     DSHOW_ERROR_IF_FAILED(media_seeking_->ConvertTimeFormat(
00294       &next, 0, next_time, &TIME_FORMAT_MEDIA_TIME));
00295   }
00296 
00297   // seeking out-of-range
00298   if (next >= end_position_)
00299   {
00300     // *****
00301     vcl_cout << "Failed: " << frame_number << vcl_endl;
00302     return false;
00303   }
00304 
00305   // seeking backwards
00306   if (frame_number < frame_index_ && frame_index_ != -1)
00307   {
00308     // *****
00309     vcl_cout << "Rewinding: " << frame_number << vcl_endl;
00310     // rewind to 0;
00311     //next = 0;
00312     //DSHOW_ERROR_IF_FAILED(media_seeking_->SetPositions(
00313     //  &next, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
00314     //  0,     AM_SEEKING_NoPositioning));
00315 
00316     media_control_->Stop();
00317       OAFilterState state;
00318       DSHOW_ERROR_IF_FAILED(media_control_->GetState(INFINITE, &state));
00319     if (media_control_->Pause() == S_FALSE)
00320     {
00321       OAFilterState state;
00322       DSHOW_ERROR_IF_FAILED(media_control_->GetState(INFINITE, &state));
00323     }
00324 
00325     frame_index_ = -1;
00326     is_valid_ = false;
00327   }
00328 
00329   // ***** ugly workaround until I figure how to seek correctly
00330   //       i think this should be working now, try again.
00331   // *****
00332   unsigned int seek_count = frame_number - frame_index_ + 1;
00333   while (--seek_count)
00334   {
00335     if (!advance_wait())
00336     {
00337       vcl_cout << "Failed Loop: " << frame_number << vcl_endl;
00338       is_valid_ = false;
00339       return false;
00340     }
00341   }
00342 
00343   frame_index_ = frame_number;
00344   is_valid_ = true;
00345 
00346   return true;
00347 
00348 #if 0 // commented out
00349   // seeking backwards
00350   if (frame_number < frame_index_ && frame_index_ != -1)
00351   {
00352     vcl_cout << "Failed: " << frame_number << vcl_endl;
00353 #if 0
00354     DWORD caps = AM_SEEKING_CanSeekBackwards;
00355     if (media_seeking_->CheckCapabilities(&caps) != S_OK)
00356     {
00357       vcl_cout << "Failed: " << frame_number << vcl_endl;
00358       return false;
00359     }
00360 #endif // 0
00361     return false;
00362   }
00363 
00364   // seek to the position
00365   DSHOW_ERROR_IF_FAILED(media_seeking_->SetPositions(
00366     &next, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
00367     0,     AM_SEEKING_NoPositioning));
00368 
00369   // ***** time stamp; not used...
00370   buffer_time_[++buffer_index_ % 2] = next / 10000000.0;
00371 
00372   frame_index_ = frame_number;
00373 
00374   vcl_cout << "Succeeded: " << frame_number << vcl_endl;
00375   return true;
00376 #endif // 0
00377 }