Using WinCap for TV Capture

Using WinCap for TV Capture

As discussed in Chapter 6, when launched, WinCap presents a list of all available video capture devices connected to the system, including webcams, digital camcorders, digital VCRs, and TV tuners. When you select a TV tuner device from the list and then click the Select Device button, you ll see a second window open, as shown in Figure 7-2.

figure 7-2 the television dialog window that opens alongside the wincap window when a tv tuner is selected

Figure 7-2. The Television dialog window that opens alongside the WinCap window when a TV tuner is selected

In addition to the broadcast TV image, in the upper portion of the WinCap window, a second window, named Television, opens alongside it. This window is the equivalent of a TV remote control. You can punch numbers onto the keypad, hit the Enter button, and change channels. The Scan area of the dialog box has up and down arrows that allow you to scan through the channels, respectively incrementing and decrementing the channel number. In addition, you can turn closed captioning on or off by clicking in the CC check box, or you can mute the audio by selecting the Mute check box. Now let s take a look at how WinCap implements these features using DirectShow.

Selecting a TV Tuner Device

After WinCap is launched, the list of video capture devices is created with calls to CMainDialog::Init and CDeviceList::Populate, which fills it with all video capture devices known to DirectShow. Although any valid video capture device can be selected, in this chapter, we ll consider only what happens when a TV tuner is selected.

When a TV tuner device is selected and the Select Device button is clicked, the CMainDialog::OnSelectDevice method is invoked. This method immediately invokes the CMainDialog::InitGraph method, which is where the DirectShow Filter Graph Manager directly interacts with WinCap. The application maintains communication with the DirectShow Filter Graph Manager through a CGraph object. The CMainDialog object has a private variable, named m_pGraph, which is a CCaptureGraph object derived from the CGraph class, but it s specifically used to build a capture filter graph. CMainDialog::InitGraph creates a new CCaptureGraph object that in turn creates a new Filter Graph Manager object.

Next CMainDialog::OnSelectDevice calls CCaptureGraph::AddCapture Device, passing it the moniker for the video capture device. This method instantiates an object to manage the DirectShow filter and then adds it to the filter graph after clearing the filter graph of any other filters with a call to CCaptureGraph::TearDownGraph. At this point, CMainDialog::OnSelectDevice calls the CMainDialog::StartPreview method. This method builds a fully functional filter graph that will display a preview from the selected video capture device (in this case, the TV tuner) in the WinCap window. CMainDialog::StartPreview calls CCaptureGraph::RenderPreview. In Chapter 6, we covered the implementation of that method, but because we re going to trace a different path through the method, it is presented here again:

HRESULT CCaptureGraph::RenderPreview(BOOL bRenderCC) { HRESULT hr; OutputDebugString(TEXT("RenderPreview()\n")); if (!m_pCap) return E_FAIL; // First try to render an interleaved stream (DV). hr = m_pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Interleaved, m_pCap, 0, 0); if (FAILED(hr)) { // Next try a video stream. hr = m_pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, m_pCap, 0, 0); } // Try to render CC. If it fails, we still count preview as successful. if (SUCCEEDED(hr) && bRenderCC) { // Try VBI pin category, then CC pin category. HRESULT hrCC = m_pBuild->RenderStream(&PIN_CATEGORY_VBI, 0, m_pCap, 0, 0); if (FAILED(hrCC)) { hrCC = m_pBuild->RenderStream(&PIN_CATEGORY_CC, 0, m_pCap, 0, 0); } } if (SUCCEEDED(hr)) { InitVideoWindow(); // Set up the video window ConfigureTVAudio(TRUE); // Try to get TV audio going } return hr; }

In this case, when a TV tuner device has been selected, the first call to the ICaptureGraphBuilder2 method RenderStream will fail because the parameter MEDIATYPE_Interleaved won t match the stream type. The method then makes another call to RenderStream, this time with MEDIATYPE_Video, the type appropriate to TV tuners. This call should succeed (if things are working correctly), and a filter graph will be built, looking very much like the filter graph that we ve already examined, with the exception of the closed-captioning portion of the filter graph. That portion of the filter graph is created with a second call to RenderStream; this call is passed with PIN_CATEGORY_VBI as the first parameter, requesting that the VBI signal be used as the source for closed- captioning text. If this call fails (and it might, depending on the filters added to the filter graph by the initial call to RenderStream), a second RenderStream call is made, this time with PIN_CATEGORY_CC, which will attempt to use a filter pin with closed-captioning data as the source for the text to be rendered into the video stream. If either of these calls succeeds and there s a chance that neither will the components necessary to handle closed-captioned decoding and overlay into the video image are added to the filter graph.

Finally, after the capture graph has been built, a call is made to CCaptureGraph::ConfigureTVAudio. In this method, the TV audio stream is rendered, as shown here:

HRESULT CCaptureGraph::ConfigureTVAudio(BOOL bActivate) { if (!m_pCap) return S_FALSE; // Basically we have to grovel the filter graph for a crossbar filter, // then try to connect the Audio Decoder Out pin to the Audio Tuner In // pin. Some cards have two crossbar filters. // Search upstream for a crossbar. IAMCrossbar *pXBar1 = NULL; HRESULT hr = m_pBuild->FindInterface(&LOOK_UPSTREAM_ONLY, NULL, m_pCap, IID_IAMCrossbar, (void**)&pXBar1); if (SUCCEEDED(hr)) { // Try to connect the audio pins. hr = ConnectAudio(pXBar1, bActivate); if (FAILED(hr)) { // Search upstream for another crossbar. IBaseFilter *pF = NULL; hr = pXBar1->QueryInterface(IID_IBaseFilter, (void**)&pF); if (SUCCEEDED(hr)) { IAMCrossbar *pXBar2 = NULL; hr = m_pBuild->FindInterface(&LOOK_UPSTREAM_ONLY, NULL, pF, IID_IAMCrossbar, (void**)&pXBar2); pF->Release(); if (SUCCEEDED(hr)) { // Try to connect the audio pins. hr = ConnectAudio(pXBar2, bActivate); pXBar2->Release(); } } } pXBar1->Release(); } return hr; }

The CCaptureGraph::ConfigureTVAudio method searches the filter graph constructed by the RenderStream calls. It begins with a call to the ICaptureGraphBuilder2 method FindInterface. This method searches a filter graph, starting with a pointer to a filter in the graph, and searches either upstream or downstream from it for a filter with a matching interface identifier in this case, IID_IAMCrossbar. (Use LOOK_DOWNSTREAM_ONLY to search downstream.) That interface identifier should (in the case of the ATI Rage Video 8500 TV Tuner) return a pointer to the ATI TV Audio Crossbar filter within the filter graph. If that call succeeds, a call to the global function Connect Audio is made.

HRESULT ConnectAudio(IAMCrossbar *pXBar, BOOL bActivate) { // Look for the Audio Decoder output pin. long i = 0; HRESULT hr = FindCrossbarPin(pXBar, PhysConn_Audio_AudioDecoder, FALSE, &i); if (SUCCEEDED(hr)) { if (bActivate) // Activate the audio { // Look for the Audio Tuner input pin. long j = 0; hr = FindCrossbarPin(pXBar, PhysConn_Audio_Tuner, TRUE, &j); if (SUCCEEDED(hr)) { return pXBar->Route(i, j); } } else // Mute the audio { return pXBar->Route(i, -1); } } return E_FAIL; }

ConnectAudio uses the local function FindCrossbarPin to locate a pin of type PhysConn_Audio_AudioDecoder, that is, a physically connected audio decoder (which is visible in the filter graph). It then finds another pin on the crossbar PhysConn_Audio_Tuner, which represents the audio tuner portion of the crossbar, and then calls the Route method to connect the two pins. In a sense, the Route method virtually wires connections in the crossbar. When this connection is made, audio is rendered to the PC s speakers. To mute the audio, all that needs to be done is to break the connection with another call to Route.

Although it might seems as though the audio is being passed through DirectShow, the TV tuner card generally sends the audio directly to the computer s sound card (often with the help of a cross-connecting cable that takes an output on the TV Tuner and connects it to an input of the sound card), usually through Line In or CD Audio In pins. So while the crossbar filter is sending commands to the TV tuner card, the filter is not processing the audio data in any way.

Back in CCaptureGraph::ConfigureTVAudio, if the call to ConnectAudio fails for any reason, the method attempts to locate another audio crossbar in the filter graph and again calls ConnectAudio. This call is made to cover cases where there might be multiple audio crossbars in the filter graph, which could potentially happen. At that point, control returns to CCaptureGraph::RenderPreview, which returns to CMainDialog::SelectDevice, and we re all done. Video imagery will be sent to the upper portion of the WinCap window, and sound will come through the designated audio output device.

Selecting a TV Channel

When the TV tuner device is selected from the list of available video capture devices, the control interface, IAMTVTuner, which maintains communication with the TV tuner, is created with a call to CTunerProp::InitDevice and then stored in the local variable m_pTuner. At the same time, the Line 21 Decoder filter is also located, and a pointer to its control interface, IAMLine21Decoder, is placed in m_pLine21. These interfaces receive the messages that control various parameters, such as the decoding of closed captioning and the selection of a TV channel.

The keypad on the Television dialog box accepts clicks on the number pad as input, but it doesn t process them until the Enter button has been pressed. When that happens, this bit of code, inside CTunerProp::OnReceive Msg, is executed:

case IDC_CHAN_ENTER: // Submit new channel to the tuner if (m_pTuner) { HRESULT hr = m_pTuner->put_Channel(m_lNewChannel, -1, -1); if (SUCCEEDED(hr)) { m_lChannel = m_lNewChannel; } else { // Failed. Restore old channel. m_lNewChannel = m_lChannel; hr = m_pTuner->put_Channel(m_lNewChannel, -1, -1); } } m_bStartNewChannel = false; RedrawControl(IDC_CHANNEL_DRAW); return TRUE;

To change the channel on a TV tuner, you need to invoke only the put_Channel method on the IAMTVTuner interface. The first parameter passed in put_Channel is the channel number, and the second and third parameters, which are both set to -1 in this case, handle the video and audio subchannel information, which are useful only if you re adjusting the tuning on a satellite broadcast receiver.

Conversely, if you want to determine what channel the TV tuner is receiving, you can make a call to the IAMTVTuner interface method get_Channel, which will return the channel number (and subchannels, if they re relevant). Additionally, if you need to know the range of possible channels to ensure that you can reject any illegal channel request use get_ChannelMinMax, which will return the minimum and maximum legal channel numbers. Although the call returns a range of valid channel numbers, there s no guarantee that every channel in the range will contain a signal; some of the channels could be blank or just static.

(The idea of a channel number doesn t have a fixed meaning in some countries. Although Channel 5 has a distinct frequency associated with it in the United States, this isn t true around the world. Consult the DirectX SDK documentation topic on International Analog TV Tuning for more information on channel selection around the world.)

Finally, there s another IAMTVTuner interface method that can help you discover the capabilities of your tuner card. Some TV tuner cards cover only the range of normal TV channels, while others can also tune in radio stations in the AM and/or FM bands. If these capabilities are present, you ll want to present the appropriate interface controls to the user. When you make a call to GetAvailableModes, a bit field is returned, which can be logically compared against the following values: AMTUNER_MODE_TV indicates that a TV tuner is present, AMTUNER_MODE_FM_RADIO indicates that an FM radio tuner is present, and AMTUNER_MODE_AM_RADIO indicates that an AM radio tuner is present. Finally, AMTUNER_MODE_DSS indicates that a Digital Satellite Service (DSS) receiver is connected. These values are not mutually exclusive. A call to GetAvailableModes could indicate that several (or all) of these capabilities are present. As you can see in Figure 7-2, the Television dialog box offers radio buttons for TV, FM, and AM modes. The buttons are enabled if the matching capabilities are present on the tuner.

That brings to a close our exploration of the basic capabilities of the TV tuner. Capture to an AVI file, which we covered in Chapter 6 with respect to digital camcorders and digital VCRs, works in precisely the same way with a TV tuner, with one exception: audio capture. Because the TV tuner audio crossbar sends the audio signal directly to the computer s sound card, audio capture to an AVI file will require the addition of an audio capture filter and might require some user input (as is provided in WinCap) to select the appropriate capture device. Now we need to move along to explore the stream buffering capabilities offered by DirectShow (if you have Windows XP Service Pack 1 installed), which allow you to pause a live video or TV broadcast in mid-stream.



Programming Microsoft DirectShow for Digital Video and Television
Programming Microsoft DirectShow for Digital Video and Television (Pro-Developer)
ISBN: 0735618216
EAN: 2147483647
Year: 2002
Pages: 108
Authors: Mark D. Pesce

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net