Download the source code of this Article
Please read this article first: One of VC + DirectShow Image Processing for videos
Develop videorenderer Filter
Filter to do the following: accept the 24-bit RGB format image, which is obtained by the upper-level filter dismember the video, and process it into a 32-bit argb image, and then pass it to the external function for further processing.
The reason I want to filter is that almost all video filters accept the 24bit RGB format, so I don't have to worry about connection failure. 32bit argb can support MMX acceleration well, if MMX is used, I will introduce MMX in this article. However, it is basic to the same level as me. Calling external functions provides more flexibility, you don't have to worry about encapsulating the image processing function in the filter. You can add new processing functions when writing a program with higher capabilities and levels, and ensure timely processing.
How about filter? It's very easy to do. Similarly, writing a filter is easier than imagined. Let's look at it step by step.
Create a simple DLL project, set the name to VR, and delete VR. add vr to the dllmain function in CPP. H and VR. def two files, in VR. add the following code to Def to complete function export.
Library Vr
Exports
Dllmain private
Dllgetclassobject private
Dllcanunloadnow private
Dllregisterserver private
Dllunregisterserver private
Let's do something uncreative-filter registration, class factory definition, etc. Add it to VR. cpp. I copied it from the filter example of DirectShow, and then added and modified it.
# Include "stdafx. H" # Include "Vr. H" # Pragma comment (Lib, "strmbase. lib ") # Pragma comment (Lib, "winmm. lib ")// Setup data Const amoviesetup_mediatype sudippintypes = { & Mediatype_video, // majortype & Mediasubtype_null // minortype }; Const amoviesetup_pin sudippin = { L "input", // The pins name False, // is rendered False, // is an output pin False, // allowed none False, // allowed enabled & Clsid_null, // connects to filter Null, // connects to pin 1, // number of types & Sudippintypes // pin details }; Const amoviesetup_filter sudvrax = { & Clsid_lwvideorenderer, // filter CLSID /**/ L "lwvideorenderer", // string name /**/ Merit_normal, // filter merit 1, // Number of pins & Sudippin // pin details }; // List of class IDs and creator functions for the class factory. This // Provides the link between the OLE entry point in the DLL and an object // Being created. The class factory will call the static createinstance // Function when it is asked to create a clsid_videorenderer object Cfactorytemplate g_templates [] = { {L "lwvideorenderer "/**/ , & Clsid_lwvideorenderer /**/ , Cvideorenderer: createinstance , Null , & Sudvrax }, }; Int g_ctemplates = sizeof (g_templates)/sizeof (g_templates [0]); // Dllregisterserver // Used to register and unregister the filter Stdapi dllregisterserver () { Return amoviedllregisterserver2 (true ); } // Dllregisterserver // Dllunregisterserver Stdapi dllunregisterserver () { Return amoviedllregisterserver2 (false ); } // Dllunregisterserver Extern "C" bool winapi dllentrypoint (hinstance, ulong, lpvoid ); // Dllmain Bool apientry dllmain (handle hmodule, DWORD dwreason, lpvoid lpreserved) { Return dllentrypoint (hinstance) (hmodule), dwreason, lpreserved ); } // Dllmain |
After some replication, some simple business logic needs to be added. Let's first complete the definition of the filter class, and derive a new class from cbasevideorendeer. Rewrite the four functions to lay the basic function of the filter, as shown below, and add it to VR. h.
# Include <streams. h> // Callback class definition Class funcls {Public: Virtual void procfun (bitmapinfo * pbmpinfo, byte * pb) {return ;}; }; // Callback function pointer Definition Typedef void (callback * pprocfun) (bitmapinfo * pbmpinfo, byte * pb ); // {F81331DB-2E46-43e7-8709-BE57205D8914} Global identifier of the filter Static const guid clsid_lwvideorenderer = {0xf81331db, 0x2e46, 0x43e7, {0x87, 0x9, 0xbe, 0x57, 0x20, 0x5d, 0x89, 0x14 }}; // Filter class definition Class cvideorenderer: Public cbasevideorenderer { Public: // Create a process. Static cunknown * winapi createinstance (lpunknown, hresult *); // Constructor and constructor Cvideorenderer (lpunknown punk, hresult * phr ); ~ Cvideorenderer (); Public: // Check whether data in acceptable format is available Hresult checkmediatype (const cmediatype * PMT ); // Set the specific data format, such as the width and height of the video image Hresult setmediatype (const cmediatype * PMT ); // Submit the data to display and present the data Hresult dorendersample (imediasample * pmediasample ); PRIVATE: Bitmapinfo m_bmp Info; // Image Information Byte * m_pcopybuffer; // copy the buffer Uint m_pixelnum; // number of pixels Funcls * m_pfuncls; // callback class pointer Pprocfun m_ppf; // callback function pointer }; |
I mentioned above that an external function is called for processing when new data is received in the filter, So I defined a callback class (what I call) and a callback function pointer. In this way, the callback class can be used as a base class of the MFC View class to conveniently use the member variables in the MFC View class. The callback function pointer can be used to play multiple video files and use multiple cameras at the same time. This is what I felt necessary in use and was later modified to make the use of filter flexible enough. The following describes the specific implementation of functions in the filter class.
// ================================================ ================================== // Create a process.Cunknown * winapi cvideorenderer: createinstance (lpunknown punk, hresult * phr) { Return new cvideorenderer (punk, PHR ); } // ================================================ ================================== // Constructor Cvideorenderer: cvideorenderer (lpunknown punk, hresult * phr): cbasevideorenderer (clsid_lwvideorenderer, "LW video Renderer", punk, PHR) { M_pcopybuffer = NULL; M_pfuncls = NULL; M_ppf = NULL; M_pixelnum = 0; } // ================================================ ================================== // Destructor Cvideorenderer ::~ Cvideorenderer () { If (this-> m_pcopybuffer ){ Delete [] m_pcopybuffer; } } // ================================================ ================================== // Check the media type Hresult cvideorenderer: checkmediatype (const cmediatype * PMT) { Videoinfo * PVI; // Only accept videos If (* PMT-> formattype ()! = Format_videoinfo ){ Return e_invalidarg; } // Only accept the rgb24 format, that is, 1 byte for R, G, and B PVI = (videoinfo *) PMT-> Format (); If (isequalguid (* PMT-> type (), mediatype_video) & isequalguid (* PMT-> subtype (), mediasubtype_rgb24 )){ Return s_ OK; } Return e_invalidarg; } // ================================================ ================================== // Set the media type to obtain various information (width and height) of the image, which must be used to process the image Hresult cvideorenderer: setmediatype (const cmediatype * PMT) { Videoinfo * pvibmp; // bitmap info Header Pvibmp = (videoinfo *) PMT-> Format (); Memset (& m_bmp info, 0, sizeof (bitmapinfo); // Reset M_bmp info.bmiheader = pvibmp-> bmiheader; // Change to 32bit because I will process it as 32bit M_bmp info.bmiheader.bibitcount = 32; // Of course, the buffer size also changes M_bmp info.bmiheader.bisizeimage = m_bmp info.bmiheader.bisizeimage * 4/3; // Create a buffer for the new 32bit Image If (m_pcopybuffer) {Delete [] m_pcopybuffer ;} M_pcopybuffer = new byte [m_bmp info.bmiheader.bisizeimage]; M_pixelnum = m_bmp info.bmiheader.biwidth * m_bmp info.bmiheader.biheight; Return s_ OK; } // ================================================ ================================== // Process media sampling Hresult cvideorenderer: dorendersample (imediasample * pmediasample) { // Obtain the sampled data area pointer, that is, the data area pointer of the 24bit Image Byte * pb = NULL; Pmediasample-> getpointer (& Pb ); If (! PB ){ Return e_fail; } // Lock! Lock the data area I want to operate on to prevent errors caused by interruptions when half of the data is processed. // It is actually the type of the critical section that is often used in multi-threaded programming, // Use constructor and constructor to enter and exit the critical section // M_rendererlock is a member of cbasevideorenderer and can be inherited. Cautolock (& this-> m_rendererlock ); // Process a 24bit image into a 32bit Image Byte * pb32 = m_pcopybuffer; // pointer to the 32bit Buffer For (uint I = 0; I <m_pixelnum; I ++ ){ Pb32 [0] = Pb [0]; Pb32 [1] = Pb [1]; Pb32 [2] = Pb [2]; Pb32 [3] = 0xff; // 0xff is 255 Pb + = 3; Pb32 + = 4; } // If a callback class exists, perform callback processing. If (m_pfuncls ){ M_pfuncls-> procfun (& m_bmp info, m_pcopybuffer ); } // If a callback function exists, process it. If (m_ppf ){ M_ppf (& m_bmp info, m_pcopybuffer ); } Return s_ OK; } |
So far, a simple filter is complete, and the compilation is successful. Then, you can use regsvr32.exe for registration and go to graphedit.exe for testing. However, if you want to use it in a program, you will find that you cannot set a callback function or callback class. This filter is so useless that we don't get anything useful except the basic functions of the ibasefilter interface. Therefore, you have to write an interface for it so that we can set something. Writing an interface is not difficult. As long as there is an interface Example, anyone can compare and write one, And I copied one. Create an ivrcontrol. h file and add the following code.
// {244df760-7e93-4cf0-92f4-dcb79f646b7e} interface guid Static const guid iid_ivrcontrol = {0x244df760, 0x7e93, 0x4cf0, {0x92, 0xf4, 0xdc, 0xb7, 0x9f, 0x64, 0x6b, 0x7e }}; // Interface Definition Declare_interface _ (ivrcontrol, iunknown) { Stdmethod (getbmp info) (This _ // Method 1: Get Image Information Bitmapinfo ** ppbmp info) pure; Stdmethod (getpointer) (This _ // Method 2: Get the buffer pointer Byte ** ppb // pointer of the buffer pointer) pure; Stdmethod (setfuncls) (This _ // method 3: Set the callback class Funcls * pfuncls // callback class pointer) pure; Stdmethod (setfun) (This _ // Method 4: Set the callback function Pprocfun PPF) pure; }; |
After writing the interface, you need to implement it. Add # include "ivrcontrol. H" to VR. H, and use the interface as a base class of the filter class, as shown in the following figure:
Class cvideorenderer: Public cbasevideorenderer, public ivrcontrol |
Add interface functions and query interface functions to the cvideorenderer class:
// The query interface, which is generally not required, but the interface needs to be used here and is also reloaded Stdmethodimp nondelegatingqueryinterface (refiid riid, void ** GMM ); // Interface function Declare_iunknown; Stdmethodimp getbmp Info (bitmapinfo ** ppbmp info ); Stdmethodimp getpointer (byte ** ppb ); Stdmethodimp setfuncls (funcls * pfuncls ); Stdmethodimp setfun (pprocfun PPF ); |
Then add the specific implementation code of the above function to VR. cpp:
// ================================================ ================================== // Query interfaceStdmethodimp cvideorenderer: nondelegatingqueryinterface (refiid riid, void ** bp) { Checkpointer (GMM, e_pointer ); If (riid = iid_ivrcontrol ){ // Return interface. Here is the details: When an interface is returned, the reference count of the filter will be added, so the external program will release the interface after it is used up. Return getinterface (ivrcontrol *) This, GMM ); } Else { Return cbasevideorenderer: nondelegatingqueryinterface (riid, GMM ); } } // ================================================ ================================== // The specific implementation of the interface function is as follows, but the assignment is simple. Stdmethodimp cvideorenderer: getbmp Info (bitmapinfo ** ppbmp info) { * Ppbmp info = & this-> m_bmp Info; Return s_ OK; } Stdmethodimp cvideorenderer: getpointer (byte ** ppb) { * Ppb = m_pcopybuffer; Return s_ OK; } Stdmethodimp cvideorenderer: setfuncls (funcls * pfuncls) { M_pfuncls = pfuncls; Return s_ OK; } Stdmethodimp cvideorenderer: setfun (pprocfun PPF) { M_ppf = PPF; Return s_ OK; } |
I don't know if you have noticed that the interface is actually a virtual base class. Classes are everywhere in modern programming languages such as C ++, and there is nothing to be surprised about, but it is helpful for better understanding. Another, seemingly powerful interface may be easily implemented. It depends on the object, and its complexity may be hidden in the object.
We can see that the callback class and callback function pointer definitions are also used in the interface definition, So I move them together with the definition of filter CLSID to ivrcontrol. h file. When this filter is used, only ivrcontrol is enabled. h. Just include this file.
Good, we have written the filters in our imagination step by function. We have successfully completed the filter and compiled it in the release mode with more than 80 K, after UPX compression, it is more than 30 kb. In this way, the code looks like a lot, but I don't think it at all when I think about the code, because every function does very little, follow the logic rules, step by step to take a photo of the very easy.