/*************************************************************************************/
/*
 * File name:  MILSampleGrabber.cpp
 * Location: See Matrox Example Launcher in the MIL Control Center
 * 
 *
 * Synopsis:   This example demonstrates how to retrieve buffers form any source accessible
 *             through a DirectShow filter and use the given buffer within a MIL application.
 *
 *             This program creates a DirectShow graph,
 *             adds a user selected capture filter to the graph and
 *             retrieve each sample to display them using a Mil Display.
 *
 * Requirements: If using Visual Studio 2005 (not required for more recent versions):
 *                   Windows SDK (http://msdn.microsoft.com/en-us/bb980924.aspx)
 *                   Run the Windows SDK Configuration tool (http://blogs.msdn.com/b/windowssdk/archive/2008/03/01/integrating-windows-sdk-and-vs-with-new-sdk-configuration-tool.aspx)
 *                   Patch for Visual Studio 2005 SP1 available from: http://support.microsoft.com/kb/949009/.
 *
 * Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
 * All Rights Reserved
 */

#include <mil.h>

/* Include for the FilterGraph Manager class.
 * It contains the function necessary to insert filters and control them. */
#include "..\..\CommonWrapper\C++\FilterGraphWrapper.h"

/* Includes the function to select capture filter and
 * the wrapper class to manage the MIL SampleGrabber filter used to retrieve
 * a MIL buffer from any possible source. */
#include "MilSampleGrabberWrapper.h"

/* Hook function to process the incoming buffer. */
void ProcessSample(MIL_ID bufID, AM_MEDIA_TYPE* pmt, void* pUserData);

/* Structure to store information passed to the processing hook function. */
typedef struct
   {
   MIL_ID  MilImageDisp;
   MIL_INT ProcessedImageCount;
   } HookDataStruct;


/* Main function.*/
/* --------------*/
int MosMain(void)
   {
   HRESULT hr = S_OK;
   MIL_ID   MilApp          = M_NULL;
   MIL_ID   MilHostSystem   = M_NULL;
   MIL_ID   MilDisplayImage = M_NULL;
   MIL_ID   MilDisplay      = M_NULL;

   FilterGraphWrapper      WrapperFilterGraph;
   MilSampleGrabberWrapper WrapperMilSampleGrabber;

   HookDataStruct          UserHookData;

   MosPrintf(MIL_TEXT("Matrox Sample Grabber DirectShow filter example:\n"));
   MosPrintf(MIL_TEXT("------------------------------------------------\n\n"));
   MosPrintf(MIL_TEXT("This example will insert a video capture source to the graph\n"));
   MosPrintf(MIL_TEXT("and using \"Matrox Sample Grabber\" filter between the source\n"));
   MosPrintf(MIL_TEXT("and a \"Null Renderer\".\n"));
   MosPrintf(MIL_TEXT("\"Matrox Sample Grabber\" filter will send a MIL buffer\n"));
   MosPrintf(MIL_TEXT("to the registered callback function where it will be copied\n"));
   MosPrintf(MIL_TEXT("to a MIL display.\n"));
   MosPrintf(MIL_TEXT("\n"));

   /* Allocate a host system for future display allocation. */
   MappAlloc(M_DEFAULT, &MilApp);
   MsysAlloc(M_SYSTEM_HOST, M_DEFAULT, M_DEFAULT, &MilHostSystem);

   /* Start building the Filter Graph using the wrapper. */
   WrapperFilterGraph.InitGraph(CLSID_NullRenderer);

   BOOL bCaptureFilterSelected = FALSE;
   if (WrapperFilterGraph.IsInitialized())
      {
      if (FilterFormCategoryCount(CLSID_VideoInputDeviceCategory) == 0)
         {
         MosPrintf(MIL_TEXT("\nERROR\n\n"));
         MosPrintf(MIL_TEXT("No video capture filters are available.\n"));
         MosPrintf(MIL_TEXT("Make sure the desired filter is properly installed.\n"));
         MosPrintf(MIL_TEXT("Press <Enter> to end.\n"));
         MosGetch();

         /* Release all remaining DirectShow interfaces. */
         WrapperMilSampleGrabber.ReleaseFilter();
         WrapperFilterGraph.UninitGraph();
         MsysFree(MilHostSystem);
         MappFree(MilApp);
         return 0;
         }

      CComPtr<IBaseFilter> selectedCaptureFilter = NULL;

      /* Select the desired source from the list of video capture filters. */
      LPCWSTR selectedCaptureFilterName = new WCHAR[1024];
      MosPrintf(MIL_TEXT("Select a video capture source from the list:\n"));
      bCaptureFilterSelected = SelectFilterFromCategory(CLSID_VideoInputDeviceCategory, &selectedCaptureFilter, selectedCaptureFilterName);
      if (bCaptureFilterSelected && !selectedCaptureFilter)
         {
         MosPrintf(MIL_TEXT("\nERROR\n\n"));
         MosPrintf(MIL_TEXT("The selected filter creation failed.\n"));
         MosPrintf(MIL_TEXT("Make sure the desired filter is properly installed and configured.\n"));
         MosPrintf(MIL_TEXT("Press <Enter> to end.\n"));
         MosGetch();

         /* Release all remaining DirectShow interfaces. */
         selectedCaptureFilter = NULL;
         WrapperFilterGraph.UninitGraph();
         MsysFree(MilHostSystem);
         MappFree(MilApp);
         return 0;
         }

      if (bCaptureFilterSelected && selectedCaptureFilter)
         {
         CComPtr<IPin>        selectedCapturePin = NULL;
         CComPtr<IPin>        sampleGrabberOutputPin = NULL;

         /* Adds the capture filter to the Filter Graph. */
         WrapperFilterGraph.InsertFilter(selectedCaptureFilter, selectedCaptureFilterName, &selectedCapturePin);


         /* Allocate a MIL Sample Grabber filter instance. */
         hr = WrapperMilSampleGrabber.AllocateFilter();
         if (FAILED(hr))
            {
            MosPrintf(MIL_TEXT("\nERROR\n\n"));
            MosPrintf(MIL_TEXT("\"Matrox Sample Grabber\" filter creation failed.\n"));
            MosPrintf(MIL_TEXT("Make sure the filter is properly registered in \n"));
            MosPrintf(MIL_TEXT("MILConfig under \"Benchmarks and Utilities/DirectShow\"\n\n"));
            MosPrintf(MIL_TEXT("Press <Enter> to end.\n"));
            MosGetch();

            /* Release all remaining DirectShow interfaces. */
            selectedCaptureFilter = NULL;
            selectedCapturePin = NULL;
            WrapperMilSampleGrabber.ReleaseFilter();
            WrapperFilterGraph.UninitGraph();
            MsysFree(MilHostSystem);
            MappFree(MilApp);
            return 0;
            }
         WrapperMilSampleGrabber.SetDesiredMediaType(MEDIASUBTYPE_YUY2);

         /* Adds the MIL Sample Grabber Filter to the Filter Graph. */
         WrapperFilterGraph.InsertFilter(WrapperMilSampleGrabber.GetFilterInterface(), L"Mil Sample Grabber", &sampleGrabberOutputPin);

         /* Connects the filters added in this order:
          * "Source Filter" => "MIL Sample Grabber". */
         WrapperFilterGraph.Connect(selectedCapturePin, WrapperMilSampleGrabber.GetFilterInterface());

         /* Automatically connects the MIL Sample Grabber filter output pin
          * to any Renderer filter added to the graph:
          * "MIL Sample Grabber" => "User specified Renderer". */
         WrapperFilterGraph.Render(sampleGrabberOutputPin);

         /* Query the MIL Sample Grabber filter connection MediaSample Size. */
         MIL_INT sizeX = 0;
         MIL_INT sizeY = 0;
         WrapperMilSampleGrabber.GetSize(sizeX, sizeY);

         /* Use MIL to allocate the display and the corresponding buffer.
          * The display buffer size is based on the size of
          * the connection MediaType between source and "MIL Sample Grabber". */
         if(sizeX == 0 || sizeY == 0 )
            {
            MosPrintf(MIL_TEXT("\nERROR\n\n"));
            MosPrintf(MIL_TEXT("Invalid source size.\n"));
            MosPrintf(MIL_TEXT("Could not allocate a display buffer.\n\n"));
            MosPrintf(MIL_TEXT("Press <Enter> to end.\n"));
            MosGetch();

            /* Release all remaining DirectShow interfaces. */
            selectedCaptureFilter = NULL;
            selectedCapturePin = NULL;
            WrapperMilSampleGrabber.ReleaseFilter();
            WrapperFilterGraph.UninitGraph();
            MsysFree(MilHostSystem);
            MappFree(MilApp);
            return 0;
            }

         MbufAllocColor(MilHostSystem, 3, sizeX, sizeY, 8 + M_UNSIGNED, M_IMAGE + M_PROC + M_YUV16 + M_PACKED + M_DISP, &MilDisplayImage);
         MdispAlloc(MilHostSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_DEFAULT, &MilDisplay);
         MdispSelect(MilDisplay, MilDisplayImage);

         /* Sets the hook function for the "MIL Sample Grabber".
          * The hook function receives an Image Buffer MIL_ID pointing to
          * each frame grabbed by the capture Filter. */
         UserHookData.MilImageDisp = MilDisplayImage;
         UserHookData.ProcessedImageCount = 0;
         WrapperMilSampleGrabber.RegisterCallback((MANAGEDCALLBACKPROC)ProcessSample, &UserHookData);

         /* Release the pin interfaces that InsertFilter function returned. */
         selectedCapturePin = NULL;
         sampleGrabberOutputPin = NULL;
         }

      /* Release the selected capture filter interface. */
      if (selectedCaptureFilterName)
         {
         delete [] selectedCaptureFilterName;
         selectedCaptureFilterName = NULL;
         }

      selectedCaptureFilter = NULL;
      }

   /* If all filters are connected properly,
    * Media control starts and is stopped by user. */
   if (WrapperFilterGraph.IsInitialized() && bCaptureFilterSelected)
      {
      /* Starts the Graph.*/
      WrapperFilterGraph.StartGraph();

      MosPrintf(MIL_TEXT("Grabbing from selected source and copying buffer to MIL display.\n"));
      MosPrintf(MIL_TEXT("\nPress <Enter> to stop graph.\n\n"));

      MosGetch();

      /* Stops the Graph. */
      WrapperFilterGraph.StopGraph();
      }
   MosPrintf(MIL_TEXT("\nPress <Enter> to end.\n"));
   MosGetch();

   /* Release all remaining DirectShow interfaces. */
   WrapperMilSampleGrabber.ReleaseFilter();
   WrapperFilterGraph.UninitGraph();

   MdispFree(MilDisplay);
   MbufFree(MilDisplayImage);
   MsysFree(MilHostSystem);
   MappFree(MilApp);
   return 0;
   }

/* Hook function to receive the image buffer from the source filter. */
void ProcessSample(MIL_ID bufID, AM_MEDIA_TYPE* pmt, void* pUserData)
   {
   HookDataStruct* pHookData = (HookDataStruct*)pUserData;

   /* Gets the display image buffer from the hook data structure. */
   MIL_ID dispImage = pHookData->MilImageDisp;
   pHookData->ProcessedImageCount++;
   MosPrintf(MIL_TEXT("\rFrames processed: %d"), pHookData->ProcessedImageCount);

   /* Copy image from the capture filter to the MIL display image. */
   MbufCopy(bufID, dispImage);
   }