dshowsrcwrapper and wasapi uninitializes the COM library
Submitted by Youness Alaoui
Link to original bug (#592415)
Description
Hi,
So here's the situation, aMSN is a tcl/tk application which uses multiple different extensions for specific tasks, one of these extensions is called 'tkvideo' and uses dshow to capture video from webcam devices/other..
The problem now is that when gstreamer (specifically dshowaudiosrc/dshowvideosrc/wasapisrc elements) are used, then completely break tkvideo, and here's why :
tkvideo does a CoInitialize(NULL) when initialized then creates/uses COM objects...
dshowaudiosrc/dshowvideosrc call CoInitializeEx(NULL, COINIT_MULTITHREADED) in the _init and call CoUninitialize() in the dispose. The CoInitializeEx tries to initialize the COM library in a multithreaded model, which fails with RPC_E_CHANGED_MODE.. but it doesn't check the return value... The problem being that CoInitialize(NULL) initializes the COM library in a singlethreaded mode, and when calling CoInitialize or CoInitializeEx, if the threading model changes, the call fails...
later on when dispose is called, dshowaudiosrc/dshowvideosrc call CoUninitialize() which will uninitialize the COM library and free its resources.. but tkvideo is still using the COM library and may have COM objects allocated.. this causes it to fail, or crash...
wasapisrc does the same thing, but it uses CoInitialize(NULL), which means that the same problem would occur if any application creates a pipeline using both wasapisrc and dshowvideosrc for example.. you end up initializing COM once (the other initialization failing) but uninitializing it TWICE...
Also note that the fact that the CoUninitialize() is called in the dispose is bad, since dispose could be called more than once, it should instead be put in the finalize function of the gobject...
I have to solutions, first, use this :
HRESULT hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
if (hr == RPC_E_CHANGED_MODE)
hr = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
and then check if it failed or not for the second CoInitializeEx, if it failed, either fail to create the element, or just ignore it (a bit risky) but store a variable saying whether or not we should call CoUninitialize later on in the finalize...
second solution would be to do the same code as above, but do it in the class_init and completely remove any call to CoUninitialize()...
not checking for RPC_E_CHANGED_MODE after calling the second CoInitializeEx would mean that we know the COM library is initialized and we can use it, BUT if the main application that initialized it calls CoUninitialize at some point, we're risking a crash... so we could just cross our fingers and hope it never calls CoUninitialize (the other concurrency models possible are : COINIT_DISABLE_OLE1DDE and COINIT_SPEED_OVER_MEMORY, but will most probably never be seen in real life situations).
Note that wasapisrc should also be modified to use CoInitializeEx instead of CoInitialize... with the above solution(s) of course...
KaKaRoTo