Range-based for with ATL Collections

ATL collection classes did not receive an update related to new C++11 range-based fors and other fancy features. Well it’s a pity because writing

for(auto&& pPin: AudioPinList)
  pPin-> //...

compared to

for(POSITION Position = AudioPinList.GetHeadPosition(); Position; AudioPinList.GetNext(Position))
{
  IPin* pPin = AudioPinList.GetAt(Position);
  pPin-> //...

is indeed a relief. Namespace std is, of course, good but ATL is still here as well. Here is a take on adding range-based for loops to ATL collection classes.

  • CRoIterativeCollectionT class is a traits-based uniform base for lists and arrays, adding (again, uniform) helpers to iterate through collection:
    • GetCountThat, GetThat, RemoveThat, RemoveFirst, RemoveLast
    • Find, FindThat, FindFirst, FindLast, FindFirstThat, FindLastThat
    • ForEach
    • begin, end implementing for range based for loops
  • CRoArrayT is a class extending CAtlArray, adding mentioned above through inheriting from CRoIterativeCollectionT
  • CRoListT is a class extending CAtlList, adding mentioned above through inheriting from CRoIterativeCollectionT
  • CRoAssignableListT, CRoAssignableArrayT, CRoAssignableMapT to inherit collections and allow assignment operators on those (through duplication of elements), esp. to be able to easily put the collections as members of classes, eligible for assignment operator copies
  • CRoFixedArrayT, CRoFixedMapT are compatible collection classes backed by stack memory only, no allocations (old stuff as is, was used somewhere in handler where immediate response assumed no memory allocation operations)
  • CRoMapT is essentially an thin extension of CAtlMap, however also adds GetPositions() and GetValues() methods, which can be used for range-based for loops

Download

Audio playback at non-standard rates in DirectShow

DirectShow streaming and playback in particular offers flexible playback rates for scenarios where playback is requested to take place slower or faster than real time. For a DirectShow developer, the outer interface is pretty straightforward:IMediaPosition::put_Rate takes playback rate and that’s it.

Playback rate. Must not be zero.

The playback rate is expressed as a ratio of the normal speed. Thus, 1.0 is normal playback speed, 0.5 is half speed, and 2.0 is twice speed. For audio streams, changing the rate also changes the pitch.

Even after taking out the case of reverse playback, which is not supported out of the box and requires some DirectShow magic to implement, there is a nasty problem from those who want to be able to change playback rate flexibly on the go.

Rates greater than one are faster than normal. Rates between zero and one are slower than normal. Negative rates are defined as backward playback, but in practice most filters do not support it. Currently none of the standard DirectShow filters support reverse playback.

The problem comes up when an audio-enabled file/stream is being played back and there is an audio renderer in the pipeline. The filter graph would connect and play excellently, but once you try to change playback rate too much, the request might fail unexpectedly with 0x8004025C VFW_E_UNSUPPORTED_AUDIO “Cannot play back the audio stream: the audio format is not supported.” error.

An application that “almost does everything right” is unable to do a small thing as simple as fast forward playback!

The root of the problem is in audio renderer. Requests to change playback rate propagate through filter graphs through IMediaSeeking interface and Filter Graph Manager sends the new rates through renderers upstream. Audio renderer rejects to accept the rates it does not support and this breaks the whole thing.

Earlier implementations had [supposedly? “But I cannot call SetRate with more than 2, it returns VFW_E_UNSUPPORTED_AUDIO.”] a limit of 50%..200% rate range, and since Vista the actual range is somewhat relaxed. Having no documentation reference, my educated guess is that actual playback rate limit is defined by ability of the renderer to resample the data into format accepted by underlying device. That is, a device taking up to 192 kHz audio could be used to play 44.1 kHz content at rates up to 435%.

The nasty part of the problem is that even though one might want to mute the audio part at such rates, or exclude audio substream at all, this is only possible with transition through stopped state (due to supposed changes in filter graph topology) and otherwise audio renderer blocks rate changing with the mentioned error code.

So, is there any way to fix VFW_E_UNSUPPORTED_AUDIO issue? with reuse of existing components and smooth user experience on the UI side? One of the approaches is to customize the behavior of standard audio renderer, DirectSound Renderer Filter.

Filter Graph Manager would use its IMediaSeeking/IMediaPosition interfaces directly, so the filter cannot be added into filter graph as is. Fhe following is the checklist for required updates:

  • IMediaSeeking needs to be intercepted to accept wide range of rates, to pass some of them transparently and fake those accepted in “muted” mode
  • IPin, IMemInputPin interfaces need to be intercepted to accept incoming media sample, to pass them through or suppress and replace with IPin::EndOfStream in “muted” mode

The mentioned tasks make it impossible to have standard audio renderer as a normal participant of the filter graph, however a wrapper COM object can achieve the planned just fine without a single line of code doing audio. The figure below shows how standard DirectSound renderer is different from its wrapper.

Wrapper

The complete list of tasks to do in the wrapper:

  • IPin::QueryPinInfo needs to properly report wrapper filter
  • IPin::EndOfStream needs to suppress EOS call in case we already “muted” artificially
  • IPin::NewSegment needs to replace rate argument with 1.0 before forwarding to real renderer in case we decided to “mute” the stream
  • IMemInputPin::Receive and IMemInputPin::ReceiveMultiple need to replace media sample delivery with an EOS in case we are muting the stream
  • IBaseFilter::EnumPins and IBaseFilter::FindPin should properly expose pin wrapper
  • IMediaSeeking::SetRate accepts any rate and decides on muting or transparent operation, then forward real or fake value to the real renderer managed internally
  • IMediaSeeking::GetRate reports accepted rate

As the list says, wrapper filter can accept any rate (including negative!) and decode on transparent playback or muted operation for unsupported or otherwise unwanted rates. No filter graph re-creation or stopping required when changing rates, and changing muting.

A DirectSound renderer filter added to the graph automatically or otherwise, as a part of normal graph construction needs to be replaced by the wrapper in the following way:

CLSID ClassIdentifier;
if(FAILED(FilterArray[nIndex]->GetClassID(&ClassIdentifier)))
    continue;
// NOTE: DirectSound Renderer Filter, CLSID_DSoundRender
//       http://msdn.microsoft.com/en-us/library/windows/desktop/dd375473%28v=vs.85%29.aspx
if(ClassIdentifier != CLSID_DSoundRender)
    continue;
const CComPtr<IPin> pInputPin = _FilterGraphHelper::GetFilterPin(pBaseFilter);
const CComPtr<IPin> pOutputPin = _FilterGraphHelper::GetPeerPin(pInputPin);
const CMediaType pMediaType = _FilterGraphHelper::GetPinMediaType(pInputPin);
const CStringW sName = _FilterGraphHelper::GetFilterName(pBaseFilter);
__C(FilterGraph.RemoveFilter(pBaseFilter));
CObjectPtr<CFilter> pFilter;
pFilter.Construct();
pFilter->Initialize(pBaseFilter);
__C(FilterGraph.AddFilter(pFilter, sName + _T(" (Substitute)")));
__C(FilterGraph.ConnectDirect(pOutputPin, pFilter->GetInputPin(), pMediaType));

CaptureClock: Utility to Check Video/Audio Capture Rates

Someone discovered the utility while browsing my public repository (the app prompts to post data back to the website, and the anonymous user accepted the offer and posted the report from this unpublished application), so I have to drop a few lines about the tool.

The idea is basically straightforward: live capture involves attaching time stamps to media samples, and there is a chance that the time stamps slide away causing unwanted effects on captured clip. The application captures video and audio simultaneously and tracks media sample time stamps, and compares them against system clock as well. Having it simply run for a few minutes one can see how the capture is doing and if any of the timings drift away. Being stopped it puts report onto clipboard and optionally posts it back to me online (no actually specific intent about this data, however if you want to share data for a device that does drift away, you are to only click once to send me the details).

CaptureClock operation

The output is on clipboard in tab-separated values (TSV) format:

Computer Name   PSI
Windows Version 6.1.7601 Service Pack 1
Video Device    Conexant's BtPCI Capture    @device:pnp:\\?\pci#ven_109e&dev_036e&subsys_18511851&rev_02#4&39c3dd91&0&08f0#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global
Audio Device    Stereo Mix (Realtek High Defini @device:cm:{33D9A762-90C8-11D0-BD43-00A0C911CE86}\Stereo Mix (Realtek High Defini

System Time Video Sample Count  Video Sample Time   Relative Video Sample Time  Audio Sample Count  Audio Sample Time   Relative Audio Sample Time
30439   907 30381   -57 304 30291   -147

Or you might prefer pasting it onto Excel:

CapptureClock Output on Excel

By the way, this is also an easy way to ensure devices are operational and check effective video frame rate.

Download links:

DirectShow Spy: ROT fix and evrprop.dll

A small issue appears to be affecting DirectShow applications with DirectShow Spy installed. As underlying COM base is non-standard, the spy implements a few hacks to run smoothly and to keep reference counting correct in particular. Under certain conditions, DirectShow-enabled ActiveX control hosted by Internet Explorer seems to be unable to put its graph onto Running Object Table (ROT). What happened next is that Spy assumed ROT operation to succeed, and compensated reference counting, which under bad assumption could cause E_UNEXPECTED error while creating a filter graph. This updated fixes the issue.

Another small improvement is that similarly to SDK proppage.dll, Spy registration UI also assists in registering another DLL – evrprop.dll, should it be there near the spy module.

Evrprop.dll registration

Download links

Bringing virtual DirectShow devices back to life with Skype 6

As mentioned before, new Skype 6 broke compatibility with all virtual DirectShow devices out there. Just oops, nothing works any longer if only it is not a full driver exposing virtual device through WDM.

Since quite some people are interested in details (Skype 6 Virtual Camera issues, Skype Client for Windows – SCW-3881 – Virtual cameras no longer work with Skype 6), here goes a bit of relevant information on the problem.

New Skype still uses DirectShow as API to interface capture devices and to discover such it enumerates them the way all decent applications do: Enumerating Devices and Filters. Presumably, there was a good reason to touch the video capture code and from moderately terrible it started being even worse. Skype guys decided that if a video device does not have a DevicePath property then it is not good enough for them.

The “DevicePath” property is not a human-readable string, but is guaranteed to be unique for each video capture device on the system. You can use this property to distinguish between two or more instances of the same model of device.

The documentation is unfortunately confusing in this part and perhaps developers thought they were doing the right thing, nevertheless multiple discussions online suggest a different approach.

Unfortunately, DevicePath is not a mandatory property. And it does not exist in virtual devices, and you cannot use it to distinguish between them. And Skype started – supposedly – skipping devices without it. And this is why all virtual devices were left overboard.

Virtual devices are not that rare. It might not only be a gate into IP camera, instead it might be a device that splits exclusive use type video camera device between applications, or something that adds an overlay onto captured image. That is, the whole class of devices failed to work with Skype from there. Including, of course, IP Video Source which is a virtual DirectShow device with the backend network connection into an IP camera or video encoder.

Adding missing DevicePath is the key to fix the problem and give Skype what it thinks is a must. Let’s hope we have a fix from Skype soon and there is no need in working things around any longer, but if someone needs a quick solution – it also exists (apart from rewriting everything into kernel mode driver, and other hackery).

Since the properties an application reads from IPropertyBag interface (which is in turn obtained from IMoniker when enumerating devices) are [partially] backed by system registry, it is sufficient to add “DevicePath” value into specific key of the registry and make the fake property available for those devices that don’t have kernel path.

The registry key is located under HKLM, SOFTWARE\Classes\CLSID\{860BB310-5D01-11d0-BD3B-00A0C911CE86}\Instance (note it’s SOFTWARE\Classes\Wow6432Node\CLSID... in 64-bit OS for 32-bit app space), where every subkey corresponds to a registered device (find yours there). Note that CLSID above is actually CLSID_VideoInputDeviceCategory.

Adding new value there will create a fake property on the property bag and improve compatibility with such picky software as Skype. Note that the value will be destroyed with re-registration of the device filter, and will have to be created once again.

DirectShow Spy: Easier Registration

Because DirectShow Spy is often a troubleshooting tool, one of its use scenarios is its being a drop-in module to quickly install on a system of interest in order to connect to graphs for troubleshooting purposes, such as to check topology and media types.

Its installation requires COM registration, and over time it changed gradually from simple to more and more complex step. Why? In Windows XP one had to open command prompt and regsvr32. With Vista’s UAC one needs a prompt, with privilege elevation, which opens typically in wrong directory, then UAC prompt. A relatively easy step became annoying multi-step operation. Then proxy/stub classes were moved into Windows SDK DLL…

Things are getting back to be easier with DirectShow Spy. It is given a special property sheet right there withing the DLL, to take care of all the important things:

  • checks registration status
  • buttons to Unregister/Register
  • per-user registration (not recommended though due to system wide class hooking)
  • automatically takes care of UAC prompt
  • place Windows SDK proppage.dll into the same directory near spy, and additional property page will help you to register this additional dependency

To invoke registration UI, start:

rundll32 DirectShowSpy.dll,DoRegistrationPropertySheetModal

from command line, or just have a .BAT file ready to do it for you.

Download links

dlinkddns.com@ and unique email addresses

This is a spam email. There is not so much special about, just the recipient address. “Single use” email addresses have an advantage of trackability of spam sources, because once you start receiving messages onto this alias you immediately know the source. Not to say that you can block them easily.

Google Mail offers a similarly useful option of an optional suffix on the email address which is automatically stripped on message delivery: foo+bar@gmail.com is delivered to foo@gmail.com and one can add bars to help oneself sorting and tracking messages.

I am not the one receiving spam onto registration email with dlinkddns.com: dlinkddns email details leaked?.

Bonus reading: Use of the “+” to track email abuse: Using gmail filters.