I've written a filter that implements IStream (the COM interface not the C++ standard library class). That's all working well but my problem is that I'm not sure when (if ever) I can be sure than no further IStream commands will be sent so the stream behind IStream can be closed.
The simplest place to close the stream would be in Stop() on my filter but this is too early.
According to MSDN docs, the filter graph manager will call Stop() on filters in the graph in upstream order so my filter will get Stopped before an upstream mux filter which typically will use IStream to do any end of streaming fixup (e.g. the GDCL mp4 mux filter). I've verified in the debugger that Stop() on my filter is called and exits before Stop() is called on upstream filters (which could potentially result in further IStream calls to my filter).
The system Microsoft file writer filter seems able to work this out. During streaming the sink file written by the file writer can't be renamed or moved as you'd expect but the file can moved once streaming has stopped. How is the Microsoft file writer detecting that it's safe to close the file? Is it getting some sort of extra callback once all filters in the graph have stopped or listening for the end of graph state change to stopped with a plugin distributor? Does it close the file when the IStream interface is released and the reference count falls to zero?
It's been a while since I asked this question and I still haven't worked out how the MS file writer works out when to close its output file.
Here are some possible solutions, some better than others:
- Don't close the output stream until the filter is destroyed or removed from the graph. Clearly the MS file writer does not do this. The internal analyzer file writer filter in GraphStudioNext uses this approach cpp file, h file
- Set a timer in Stop() on the downstream filter which periodically checks whether the upstream filter is still active. As soon as the upstream filter is no longer active, Stop() has finished and there should not be any further IStream calls so the output stream can be closed. This should work but isn't guaranteed to close the output stream before the Stop() call on the graph has returned. UPDATE - It's probably not safe to assume that because a filter is stopped it will not generate further IStream calls. According to File Writer Filter Documentation, "... It supports IStream to allow reading and writing the file header AFTER the graph is stopped." [my emphasis]
- Close the stream when the last reference to the IStream interface is released. Likely to go wrong if there are any reference counting bugs on the IStream interface. Upstream filter may hang onto IStream reference until pin is disconnected and/or filter destroyed.
- Insert an extra unconnected dummy filter in the graph whose only purpose is to wait in its own Stop() function until the upstream filter is closed to notify the downstream filter so it can close its output stream. Seems like a dirty hack with possible side effects. Relies on the Stop() calls being preemptive among different renderers in the graph.
- In the downstream filter, respond to some other callback that happens after Stop() is called on the upstream filter but before Stop() on the graph returns. Would be ideal but I haven't found any mechanism to do this.
- UPDATE 2 : another possible idea. On a timer callback QueryInterface on the containing graph and close the file output stream with GetState() on the graph returns State_Stopped as this doesn't seem to happen until all filters have returned from Stop() and all streaming should have finished. UPDATE 3 : This appears to be the best solution using CreateTimerQueueTimer called with the flag WT_EXECUTELONGFUNCTION and TryEnterCriticalSection on a dedicated CRITICAL_SECTION in the callback to prevent re-entrancy and thread bloat. Though it doesn't guarantee the output stream closes before Stop on the graph returns it should close the file soon afterwards (potentially very soon if a fine grained timer is used). Requires some care to avoid deadlocks and race conditions; e.g. the timer callback should not cache an AddRef'd filter graph interface, shouldn't hold the filter lock when calling IMediaControl::GetState() on the filter graph, ensuring elsewhere in the code that the timer callback is definitely terminated before streaming restarts, the filter is paused, disconnected, removed from the graph etc. It could even be that the MS File Writer uses this technique too and the output file closes so soon after Stop() that it's not easily detectable.