// Optional encoding end function called every time a buffer is finished being compressed. // ---------------------------------------------------------------------------------------- static MIL_INT FrameEncodingEndFunction(MIL_INT HookType, MIL_ID HookId, IntPtr HookDataPtr) { // this is how to check if the user data is null, the IntPtr class // contains a member, Zero, which exists solely for this purpose if (!IntPtr.Zero.Equals(HookDataPtr)) { // get the handle to the FrameEndHookDataStruct object back from the IntPtr GCHandle hUserData = GCHandle.FromIntPtr(HookDataPtr); // get a reference to the FrameEndHookDataStruct object EncodingFrameEndHookDataStruct UserHookDataPtr = hUserData.Target as EncodingFrameEndHookDataStruct; // Frame end hook post processing. if (HookType == MIL.M_FRAME_END) { MIL_ID CompressedBufferId = MIL.M_NULL; MIL_INT CompressedDataPtr = MIL.M_NULL; MIL_INT CompressedDataSize = 0; // Increment a encoded frame counter UserHookDataPtr.EncodedImageCount++; // Retrieve the MIL_ID of the encoded buffer. MIL.MseqGetHookInfo(HookId, MIL.M_MODIFIED_BUFFER + MIL.M_BUFFER_ID, ref CompressedBufferId); // Retrieves the address of the encoded data. MIL.MbufInquire(CompressedBufferId, MIL.M_HOST_ADDRESS, ref CompressedDataPtr); // Retrieves the size in bytes of the encoded data. MIL.MbufInquire(CompressedBufferId, MIL.M_SIZE_BYTE, ref CompressedDataSize); // ----------------------------------------------------------------------------------------------- // Here you can do any action with the encoded data such as send buffer through a network stream. // If the processing done on the compressed data is long, it is recommended to copy the // buffer and to process it in a separate thread to avoid blocking the compressions flow. // ----------------------------------------------------------------------------------------------- } } return(0); }
// Main function. // // ---------------// static int Main(string[] args) { MIL_ID MilApplication = MIL.M_NULL; MIL_ID MilRemoteApplication = MIL.M_NULL; MIL_ID MilSystem = MIL.M_NULL; MIL_ID MilDigitizer = MIL.M_NULL; MIL_ID MilDisplay = MIL.M_NULL; MIL_ID MilImageDisp = MIL.M_NULL; MIL_ID[] MilGrabBufferList = new MIL_ID[BUFFERING_SIZE_MAX]; MIL_ID MilCompressContext = MIL.M_NULL; MIL_ID MilDecompressContext = MIL.M_NULL; MIL_INT LicenseModules = 0; MIL_INT MilSystemLocation = MIL.M_NULL; MIL_INT MilGrabBufferListSize; MIL_INT ProcessFrameCount = 0; MIL_INT NbFrames = 0; MIL_INT n = 0; double EncodingDesiredFrameRate = 0.0; double ProcessFrameRate = 0.0; MIL_INT SeqProcessFilePathSize = 0; StringBuilder SeqProcessFilePath = null; ProcessingHookDataStruct ProcessingUserHookData = new ProcessingHookDataStruct(); EncodingFrameEndHookDataStruct EncodingFrameEndUserHookData = new EncodingFrameEndHookDataStruct(); DecodingFrameEndHookDataStruct DecodingFrameEndUserHookData = new DecodingFrameEndHookDataStruct(); MIL_INT SeqSystemType = MIL.M_NULL; // Allocate defaults. MIL.MappAllocDefault(MIL.M_DEFAULT, ref MilApplication, ref MilSystem, ref MilDisplay, ref MilDigitizer, ref MilImageDisp); MIL.MsysInquire(MilSystem, MIL.M_OWNER_APPLICATION, ref MilRemoteApplication); MilSystemLocation = MIL.MsysInquire(MilSystem, MIL.M_LOCATION, MIL.M_NULL); if (MIL.MappInquire(MilRemoteApplication, MIL.M_PLATFORM_OS_TYPE, MIL.M_NULL) != MIL.M_OS_WINDOWS) { if (MilSystemLocation == MIL.M_REMOTE) { Console.WriteLine("The Distributed MIL server must run on a Windows system."); } else { Console.WriteLine("This example only works with a Windows system."); } Console.WriteLine("Press <Enter> to end."); Console.ReadKey(); MIL.MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, MilImageDisp); return(0); } // Inquire MIL licenses. MIL.MappInquire(MilRemoteApplication, MIL.M_LICENSE_MODULES, ref LicenseModules); if ((LicenseModules & MIL.M_LICENSE_JPEGSTD) != MIL.M_LICENSE_JPEGSTD) { Console.WriteLine("Need a Compression/Decompression license to run this example."); Console.WriteLine("Press <Enter> to end."); Console.ReadKey(); MIL.MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, MilImageDisp); return(0); } // Allocate the grab buffers and clear them. MIL.MappControl(MIL.M_DEFAULT, MIL.M_ERROR, MIL.M_PRINT_DISABLE); for (MilGrabBufferListSize = 0; MilGrabBufferListSize < BUFFERING_SIZE_MAX; MilGrabBufferListSize++) { MIL.MbufAllocColor(MilSystem, MIL.MdigInquire(MilDigitizer, MIL.M_SIZE_BAND, MIL.M_NULL), MIL.MdigInquire(MilDigitizer, MIL.M_SIZE_X, MIL.M_NULL), MIL.MdigInquire(MilDigitizer, MIL.M_SIZE_Y, MIL.M_NULL), 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC, ref MilGrabBufferList[MilGrabBufferListSize]); if (MilGrabBufferList[MilGrabBufferListSize] != MIL.M_NULL) { MIL.MbufClear(MilGrabBufferList[MilGrabBufferListSize], 0xFF); } else { break; } } MIL.MappControl(MIL.M_DEFAULT, MIL.M_ERROR, MIL.M_PRINT_ENABLE); // Free buffers to leave space for possible temporary buffers. for (n = 0; n < 2 && MilGrabBufferListSize > 0; n++) { MilGrabBufferListSize--; MIL.MbufFree(MilGrabBufferList[MilGrabBufferListSize]); } if (MilGrabBufferListSize == 0) { Console.WriteLine("!!! No grab buffers have been allocated. Need to set more Non-Paged Memory. !!!"); MIL.MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, MilImageDisp); Console.WriteLine("Press <Enter> to end."); Console.ReadKey(); return(1); } /* Initialize the User's processing function data structure only for Display. */ ProcessingUserHookData.MilDigitizer = MilDigitizer; ProcessingUserHookData.MilSeqContext = MIL.M_NULL; ProcessingUserHookData.MilImageDisp = MilImageDisp; ProcessingUserHookData.ProcessedImageCount = 0; ProcessingUserHookData.ProcessingOperation = ProcessingHookOperation.DISPLAY; // get a handle to the HookDataStruct object in the managed heap, we will use this // handle to get the object back in the callback function GCHandle hUserData = GCHandle.Alloc(ProcessingUserHookData); // Start the sequence acquisition. The preprocessing and encoding hook function // is called for every frame grabbed. MIL_DIG_HOOK_FUNCTION_PTR ProcessingFunctionDelegate = new MIL_DIG_HOOK_FUNCTION_PTR(ProcessingFunction); /* Start a digProcess to show the live camera output. */ MIL.MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize, MIL.M_START, MIL.M_DEFAULT, ProcessingFunctionDelegate, GCHandle.ToIntPtr(hUserData)); /* Print a message. */ Console.Write("\nH.264 IMAGE SEQUENCE COMPRESSION.\n"); Console.Write("---------------------------------\n\n"); Console.Write("Press <Enter> to start compression.\r"); Console.ReadKey(); /* Stop digProcess. */ MIL.MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize, MIL.M_STOP, MIL.M_DEFAULT, ProcessingFunctionDelegate, GCHandle.ToIntPtr(hUserData)); /* Inquire the dig process frame rate */ MIL.MdigInquire(MilDigitizer, MIL.M_PROCESS_FRAME_RATE, ref EncodingDesiredFrameRate); Console.Write("Grabbing frames at {0:0.00} frames/sec.\n", EncodingDesiredFrameRate); // Creates a context for the H.264 compression engine. Compression will be done // using hardware or software depending on the system hardware configuration. MIL.MseqAlloc(MilSystem, MIL.M_DEFAULT, MIL.M_SEQ_COMPRESS, MIL.M_DEFAULT, MIL.M_DEFAULT, ref MilCompressContext); // Specify the destination of the compressed file and the target container type. // The last argument specifies to generate an MP4 file. MIL.MseqDefine(MilCompressContext, MIL.M_SEQ_OUTPUT(0) + MIL.M_SEQ_DEST(0), MIL.M_FILE, (MilSystemLocation != MIL.M_REMOTE ? SEQUENCE_FILE : REMOTE_SEQUENCE_FILE), MIL.M_FILE_FORMAT_MP4); // Set the compression parameters. // Sets the compression parameters valid for any resolution under 1920X1080. // Any resolution higher than that will generate an warning that can be disabled using // MseqControl with M_SETTING_AUTO_ADJUSTMENT. See documentation for more details. MIL.MseqControl(MilCompressContext, MIL.M_CONTEXT, MIL.M_STREAM_BIT_RATE_MODE, MIL.M_VARIABLE); // MIL.M_VARIABLE or MIL.M_CONSTANT MIL.MseqControl(MilCompressContext, MIL.M_CONTEXT, MIL.M_STREAM_BIT_RATE, 5000); // 5 Mbps bit rate MIL.MseqControl(MilCompressContext, MIL.M_CONTEXT, MIL.M_STREAM_BIT_RATE_MAX, 5000); // 5 Mbps bit rate MIL.MseqControl(MilCompressContext, MIL.M_CONTEXT, MIL.M_STREAM_FRAME_RATE, EncodingDesiredFrameRate); // 60Hz frame rate. MIL.MseqControl(MilCompressContext, MIL.M_CONTEXT, MIL.M_STREAM_FRAME_RATE_MODE, MIL.M_VARIABLE); // Attempts to update the file header with the encoding frame rate // if lower than the specified frame rate. MIL.MseqControl(MilCompressContext, MIL.M_CONTEXT, MIL.M_STREAM_QUALITY, 100); // 1=best speed, 100=best quality MIL.MseqControl(MilCompressContext, MIL.M_CONTEXT, MIL.M_STREAM_PROFILE, MIL.M_PROFILE_HIGH); // MIL.M_PROFILE_BASELINE, MIL.M_PROFILE_MAIN, MIL.M_PROFILE_HIGH MIL.MseqControl(MilCompressContext, MIL.M_CONTEXT, MIL.M_STREAM_LEVEL, MIL.M_LEVEL_4_2); // MIL.M_LEVEL_1, MIL.M_LEVEL_1B, MIL.M_LEVEL_1_1, MIL.M_LEVEL_1_2, MIL.M_LEVEL_1_3, // MIL.M_LEVEL_2, MIL.M_LEVEL_2_1, MIL.M_LEVEL_2_2, // MIL.M_LEVEL_3, MIL.M_LEVEL_3_1, MIL.M_LEVEL_3_2, // MIL.M_LEVEL_4, MIL.M_LEVEL_4_1, MIL.M_LEVEL_4_2, // MIL.M_LEVEL_5, MIL.M_LEVEL_5_1 MIL.MseqControl(MilCompressContext, MIL.M_CONTEXT, MIL.M_STREAM_GROUP_OF_PICTURE_SIZE, 30); // Interval between I-Frame // Initialize the optional encoding end function data structure. EncodingFrameEndUserHookData.EncodedImageCount = 0; // get a handle to the HookDataStruct object in the managed heap, we will use this // handle to get the object back in the callback function GCHandle EncodingFrameEndUserHookDataHandle = GCHandle.Alloc(EncodingFrameEndUserHookData); // Register the encoding end function to the sequence context. MIL_SEQ_HOOK_FUNCTION_PTR FrameEncodingEndFunctionDelegate = new MIL_SEQ_HOOK_FUNCTION_PTR(FrameEncodingEndFunction); MIL.MseqHookFunction(MilCompressContext, MIL.M_FRAME_END, FrameEncodingEndFunctionDelegate, GCHandle.ToIntPtr(EncodingFrameEndUserHookDataHandle)); // Provide a sample image to initialize the encoding engine accordingly. MIL.MseqControl(MilCompressContext, MIL.M_CONTEXT, MIL.M_BUFFER_SAMPLE, MilGrabBufferList[0]); // Start the encoding process, waits for buffer to be fed for encoding. MIL.MseqProcess(MilCompressContext, MIL.M_START, MIL.M_ASYNCHRONOUS); // Display the type of compression used. Console.Write("Live image capture and compression to file using "); MIL.MseqInquire(MilCompressContext, MIL.M_CONTEXT, MIL.M_CODEC_TYPE, ref SeqSystemType); if (SeqSystemType == MIL.M_HARDWARE + MIL.M_QSV) { Console.WriteLine("Hardware acceleration."); } else // MIL.M_SOFTWARE + MIL.M_QSV { Console.WriteLine("Software implementation."); } // Set the sequence context id in the user hook data structure to start // feeding buffers for encoding in ProcessingFunction. // ProcessingUserHookData.MilSeqContext = MilCompressContext; ProcessingUserHookData.ProcessedImageCount = 0; ProcessingUserHookData.ProcessingOperation = ProcessingHookOperation.ENCODE; MIL.MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize, MIL.M_START, MIL.M_DEFAULT, ProcessingFunctionDelegate, GCHandle.ToIntPtr(hUserData)); // NOTE: Now the main() is free to perform other tasks while the compression is executing. // --------------------------------------------------------------------------------------- // Print a message and wait for a key press after a minimum number of frames. Console.WriteLine("Press <Enter> to stop.\n"); Console.ReadKey(); // Stop the processing. MIL.MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize, MIL.M_STOP + MIL.M_WAIT, MIL.M_DEFAULT, ProcessingFunctionDelegate, GCHandle.ToIntPtr(hUserData)); // Free the processing user data handle. hUserData.Free(); // Stop the encoding process MIL.MseqProcess(MilCompressContext, MIL.M_STOP, MIL.M_WAIT); // Make sure the hook handler function delegate is not prematurely garbage collected since // only MIL has a reference to it. GC.KeepAlive(FrameEncodingEndFunctionDelegate); // Free the encoding user data handle. EncodingFrameEndUserHookDataHandle.Free(); // Print statistics. MIL.MdigInquire(MilDigitizer, MIL.M_PROCESS_FRAME_COUNT, ref ProcessFrameCount); MIL.MdigInquire(MilDigitizer, MIL.M_PROCESS_FRAME_RATE, ref ProcessFrameRate); Console.WriteLine("{0} frames encoded at {1:0.00} frames/sec ({2:0.0} ms/frame).", ProcessFrameCount, ProcessFrameRate, 1000.0 / ProcessFrameRate); Console.WriteLine(); MIL.MseqInquire(MilCompressContext, MIL.M_SEQ_OUTPUT(0) + MIL.M_SEQ_DEST(0), MIL.M_STREAM_FILE_NAME_SIZE, ref SeqProcessFilePathSize); SeqProcessFilePath = new StringBuilder((int)SeqProcessFilePathSize); MIL.MseqInquire(MilCompressContext, MIL.M_SEQ_OUTPUT(0) + MIL.M_SEQ_DEST(0), MIL.M_STREAM_FILE_NAME, SeqProcessFilePath); Console.WriteLine("The video sequence file was written to:"); Console.WriteLine("{0}.", SeqProcessFilePath.ToString()); Console.WriteLine(); Console.WriteLine("It can be played back using any compatible video player."); // Free the grab buffers and sequence context. while (MilGrabBufferListSize > 0) { MIL.MbufFree(MilGrabBufferList[--MilGrabBufferListSize]); MilGrabBufferList[MilGrabBufferListSize] = MIL.M_NULL; } MIL.MseqFree(MilCompressContext); // Wait for a key to start the replay. Console.WriteLine("Press <Enter> to replay encoded sequence."); Console.ReadKey(); MIL.MseqAlloc(MilSystem, MIL.M_DEFAULT, MIL.M_SEQ_DECOMPRESS, MIL.M_DEFAULT, MIL.M_DEFAULT, ref MilDecompressContext); // Specify the destination of the compressed file and the target container type. // The last argument specifies to generate an MP4 file. MIL.MseqDefine(MilDecompressContext, MIL.M_SEQ_INPUT(0), MIL.M_FILE, (MilSystemLocation != MIL.M_REMOTE ? SEQUENCE_FILE : REMOTE_SEQUENCE_FILE), MIL.M_FILE_FORMAT_MP4); double outputFrameRate = 0.0; MIL.MseqInquire(MilDecompressContext, MIL.M_SEQ_INPUT(0), MIL.M_STREAM_FRAME_RATE, ref outputFrameRate); Console.WriteLine(); Console.WriteLine("Replaying file at {0:0.00} frames/second.", outputFrameRate); // Initialize the optional decoding end function data structure. DecodingFrameEndUserHookData.DecodedImageCount = 0; DecodingFrameEndUserHookData.MilImageDisp = MilImageDisp; // get a handle to the HookDataStruct object in the managed heap, we will use this // handle to get the object back in the callback function GCHandle DecodingFrameEndUserHookDataHandle = GCHandle.Alloc(DecodingFrameEndUserHookData); // Register the decoding end function to the sequence context. MIL_SEQ_HOOK_FUNCTION_PTR FrameDecodingEndFunctionDelegate = new MIL_SEQ_HOOK_FUNCTION_PTR(FrameDecodingEndFunction); MIL.MseqHookFunction(MilDecompressContext, MIL.M_FRAME_END, FrameDecodingEndFunctionDelegate, GCHandle.ToIntPtr(DecodingFrameEndUserHookDataHandle)); // Start the decoding process, waits for buffer to be fed for encoding. MIL.MseqProcess(MilDecompressContext, MIL.M_START, MIL.M_ASYNCHRONOUS); // Print a message and wait for a key press after a minimum number of frames. Console.WriteLine("Press <Enter> to stop.\n"); Console.ReadKey(); // Stop the play back. MIL.MseqProcess(MilDecompressContext, MIL.M_STOP, MIL.M_NULL); // Make sure the hook handler function delegate is not prematurely garbage collected since // only MIL has a reference to it. GC.KeepAlive(FrameDecodingEndFunctionDelegate); // Free the decoding user data handle. DecodingFrameEndUserHookDataHandle.Free(); MIL.MseqFree(MilDecompressContext); // Wait for a key to end. Console.WriteLine("Press <Enter> to end."); Console.ReadKey(); // Release defaults. MIL.MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, MilImageDisp); return(0); }