Search code examples
c++video-capturedirectshow

How to detect the presence of a PAL or NTSC signal using DirectShow?


Background

In order to record the composite-video signal from a variety of analog cameras, I use a basic USB video capture device produced by AverMedia (C039).

enter image description here

I have two analog cameras, one produces a PAL signal, the other produces an NTSC signal:

  1. PAL B, 625 lines, 25 fps
  2. NTSC M, 525 lines, 29.97 fps (i.e. 30/1.001)

Unfortunately, the driver for the AverMedia C039 capture card does not automatically set the correct video standard based on which camera is connected.

Goal

I would like the capture driver to be configured automatically for the correct video standard, either PAL or NTSC, based on the camera that is connected.

Approach

The basic idea is to set one video standard, e.g. PAL, check for signal, and switch to the other standard if no signal is detected.

By cobbling together some examples from the DirectShow documentation, I am able to set the correct video standard manually, from the command line.

So, all I need to do is figure out how to detect whether a signal is present, after switching to PAL or NTSC.

I know it must be possible to auto-detect the type of signal, as described e.g. in the book "Video Demystified". Moreover, the (commercial) AMCap viewer software actually proves it can be done.

However, despite my best efforts, I have not been able to make this work.

Could someone explain how to detect whether a PAL or NTSC signal is present, using DirectShow in C++?

The world of Windows/COM/DirectShow programming is still new to me, so any help is welcome.

What I tried

Using the IAMAnalogVideoDecoder interface, I can read the current standard (get_TVFormat()), write the standard (put_TVFormat()), read the number of lines, and so on.

The steps I took can be summarized as follows:

// summary of steps used to set the video standard, given a device moniker
// NOTE: declarations, error handling, cleanup, and details of device enumeration are omitted, for brevity
InitCaptureGraphBuilder(&pGraph, &pBuild);
pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap);
pGraph->AddFilter(pCap, L"Capture Filter");
pBuild->FindInterface(&PIN_CATEGORY_ANALOGVIDEOIN, &MEDIATYPE_AnalogVideo, pCap, IID_IAMAnalogVideoDecoder, (void**)&pDecoder);
pDecoder->put_TVFormat(AnalogVideo_PAL_B);  // or AnalogVideo_NTSC_M

The steps above work without actually running the graph.

The IAMAnalogVideoDecoder interface also defines a get_HorizontalLocked() method, which returns successful, but the output value does not appear to change, regardless of whether a camera is connected.

I can imagine that it may be necessary to run the graph in order to get updated information regarding e.g. horizontal sync, but that did not seem to make a difference, although I'm not sure my approach was correct.

Some observations

The dialog depicted below is a screenshot from the AMCap viewer software (Options->Video Device->Properties). This is not the same thing as the AmCap sample for DirectShow, supplied with the Windows SDK (although it maybe based upon that).

The "Signal Detected" value in this dialog changes when I (dis)connect a camera matching the specified standard. (Although the "Lines detected" value remains the same regardless of whether a camera is connected.)

property page

The "Signal Detected" value is actually what I am looking for. However, I could not find any mention of it in the DirectShow docs, nor in the property set for analog video decoder devices. Could this be related to the horizontal sync, somehow?

The dialog looks identical to the dialog that appears when I open the video device using ffmpeg:

ffmpeg -f dshow -show_video_device_dialog true -i video="..." ...

However, in this case the "Signal detected" value does not change when a camera is (dis)connected.

I suppose both programs produce this dialog using the filter property pages.

The AverMedia SDK does define an AVerGetSignalPresence() function. Not sure if that would do the job, but I would rather not introduce the dependency if it can be done using "pure" DirectShow.

UPDATE

After playing around with the capture device in GraphEdit, I noticed that the "Signal Detected" value is only updated if a (Video) Renderer is connected (and the graph is either running or paused):

graphedit


Solution

  • The mentioned property page is likely to pull the data using IAMAnalogVideoDecoder and get_HorizontalLocked method in particular. Note that you might be limited in receiving valid status by requirement to have the filter graph in paused or running state, which in turn might require that you connect a renderer to complete the data path (Video Renderer or Null Renderer, or another renderer of your choice).

    See also this question on Null Renderer deprecation and source code for the worst case scenario replacement.