This article shows how a problem (hangs on play/pause) with a DirectShow-based application was corrected.
There is an application which uses a custom video mixer based upon DirectShow. In deployment, the application would occasionally hang as the user moved a track bar to see different parts of the video.
The application is written in VB.Net with various C# components. The final layer connecting the application to DirectShow is the open-source DirectShow.NET library. The application is part of an established product with a substantial and growing customer base. A workable solution to the problem was required to preserve data compatibility with that customer base. (“Rewrite the application in C++ “ was not considered an economically viable option.)
Engineering problems must be solved within some constraints; otherwise anybody could do it…
The video mixer uses a call back on each frame display and raises an event to be handled by the main program thread. This updates the track bar and position indicators. The raised event runs in a worker thread, which cannot do very much in the main program. The main program uses Windows Forms which is not thread-safe.
VB.Net provides the “INVOKE” statement to handle this sort of problem – but it does not always work in this situation. Under rare timing conditions, the program would hang on the INVOKE statement. This would happen when the user manually adjusts the track bar.
"Rare timing conditions" in practice, meant about 20 minutes of video editing. This was not considered acceptable for the product.
The problem was solved by using the "PostMessage" function with custom messages to indicate video positioning and overlay requirements. A override to the standard "WNDPROC" method intercepts the custom messages in the main thread, and calls the appropriate event handlers.
Private Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As Integer) As Integer
Private Const MSG_FRAME_BEFORE_CHANGE As Integer = &H4005
Private Const MSG_FRAME_AFTER_CHANGE As Integer = &H4006
Private hMainWindow As IntPtr = Me.Handle
Private Sub NotifyFrameAdvance(MsgCode As Integer)
PostMessage(hMainWindow, MsgCode, IntPtr.Zero, IntPtr.Zero)
Protected Overrides Sub WndProc(ByRef wMsg As System.Windows.Forms.Message)
Dim msgID As Integer = wMsg.Msg
Select Case msgID
Private Sub OnVideoPositionChange(ByVal sender As Object, ByVal e As System.EventArgs) Handles VMIXER.OnVideoPositionChange
Private Sub OnVideoOverlay(ByVal sender As Object, ByVal e As System.EventArgs) Handles VMIXER.OnVideoOverlay
If CurrentTab <> Tabs.VideoOverlay Then
Points of Interest
It appears that the main thread which calls the video “Play”, “Pause”, and “Stop” methods waits on an event in DirectShow before proceeding. That event must be cleared by a worker thread in DirectShow – the same worker thread that does the call back on each frame display.
The WinForms INVOKE processing appears to require a main thread that is active to receive the signal, and does not complete until the main thread acknowledges the signal. If the main thread calls “Pause” at exactly the time a worker thread is starting the call back, the main thread cannot acknowledge the INVOKE, and the worker thread cannot proceed to clear the event upon which the main thread is waiting.
The main thread and the worker thread are both waiting for each other to release something. This appears to be the reason for the hang.
Several liters of caffeinated beverages (and many days of tracing thread logs) were required to devise this theory.
The solution worked.
August 2014, Initial publication