Index Topic Contents | |||
Previous Topic: DirectShow C++ Class Library Next Topic: CAggDirectDraw Class |
Introduction to the DirectShow C++ Class Library
This article provides a general description of the Microsoft® DirectShow class library, the relationship of the base classes to the DirectShow Component Object Model (COM) interfaces that they implement, and describes the utility classes that are not directly associated with interfaces. This article does not provide low-level descriptions of each class, nor does it provide specific instructions on how to use them to build a filter or run the filter graph manager.
The DirectShow C++ class library can help you implement the required interfaces on filters that you write. Most base classes correspond directly to interfaces, while other utility classes allow integration of Microsoft Win32® functionality, such as critical sections and thread management.
Contents of this article:
Base Classes
Most of the base classes in the DirectShow class library implement DirectShow COM interfaces. These classes produce C++ objects that provide an IUnknown interface so external components can access the interfaces the objects support.
CBaseObject and CUnknown Classes
The CBaseObject class is the root of all base classes. It exists primarily to provide debugging assistance by keeping a count of all DirectShow objects that are active. All derived base class constructors provide a debugging object name as the first parameter and call the CBaseObject constructor. You can view the debugging object name sent to this base class on a debugging monitor.
All DirectShow classes that implement interfaces derive from a base class called CUnknown, which is derived from CBaseObject. CUnknown implements the INonDelegatingUnknown interface which, like the IUnknown interface, provides methods to request an interface, and to add or release references to that interface.
Why are there two interfaces that implement the services of IUnknown? Because of aggregation. Aggregation is the COM term for the combining of more than one object into a single larger object. Although filter graph objects, such as filters and pins, are rarely aggregated, the design is available for future extensibility and also for implementing plug-in distributors (PID), which are objects that are aggregated with the filter graph manager. In an aggregated object, the outer object (the one containing the other objects) uses the IUnknown interface to communicate outside the object. The IUnknown interface on the outer object passes out references to the IUnknown interfaces of its internal objects. That is, when an application calls the IUnknown interface on the outer object and asks for the interface belonging to one of its internal objects, the outer object calls the IUnknown interface of the internal object to retrieve the requested interface.
Because the internal objects must delegate IUnknown interfaces to the IUnknown of the outer object, the IUnknown interface of the internal object should not be accessed privately (that is, without going through the outer object's IUnknown interface). The internal object's IUnknown is reserved exclusively for communicating through the outer object. However, it is possible that objects will want to connect to other objects privately, without knowledge of the outer object. For example, pins on filters are likely to need to query interfaces on pins of other objects privately.
The INonDelegatingUnknown interface provides direct, private access to interfaces, regardless of whether or not the object is aggregated. Direct access is important in most of the communication between the DirectShow objects such as pins, allocators, and filters, and is the default method of communication. In fact, the base classes implement the IUnknown interface on nonaggregated objects (which includes almost every object in the filter graph) to call the nondelegating interface directly.
Base Classes that Implement Interfaces
The majority of classes in the DirectShow class library implement COM interfaces and can be categorized as follows:
- Filter base classes implement the IBaseFilter interface, and include CBaseFilter and classes derived from it.
- Pin classes implement the IPin interface, and include CBasePin and derived classes.
- Enumerator classes include CEnumPins and CEnumMediaTypes.
- Memory classes include CMediaSample, CBaseAllocator, CMemAllocator, and their derived classes.
- Control and position classes include CBaseFilter, CMediaPosition, CMediaControl, CBaseMediaFilter, CSourceSeeking, and CBaseStreamControl.
Filter Base Classes
The DirectShow stream architecture is based on filters and pins. Filters communicate with the filter graph manager and with the pins on the filter. Pins connect filters and handle transporting the data down the stream.
CBaseFilter is the base class for all filter classes. It implements the IBaseFilter interface, which specifies methods that allow the filter graph manager to create and enumerate pins, retrieve filter information, and notify the filter that it has been added to a filter graph. CBaseFilter also implements the IMediaFilter interface (from which IBaseFilter derives) to allow the filter to receive run, pause, and stop commands from the filter graph manager. This base class adds member functions to retrieve the pin count, retrieve pointers to individual pins, and retrieve the pin version.
The CBaseMediaFilter class also implements the IMediaFilter interface. However, because IMediaFilter is also implemented by CBaseFilter, this class is seldom used except to write a plug-in distributor (PID).
Several classes are derived directly from CBaseFilter. Each of these classes provides a base class for implementing a specific type of filter. These include:
- CSource, a base class for source filters.
- CTransformFilter, a base class for transform filters.
- CBaseRenderer, a base class for renderer filters.
The CSource filter class works in conjunction with the CSourceStream pin class to help create a source filter. Most of the work is done in the pin class, and CSource adds pin creation and deletion member functions. The CSourcePosition class implements a source filter.
The CTransformFilter class implements a transform filter. Derive your transform class from CTransformFilter if you want to make a copy of the data. The CTransInPlaceFilter class, derived from CTransformFilter, allows in-place transforms that do not copy the data. These transform filter classes work in conjunction with similarly named pin classes (for example, CTransformOutputPin and CTransformInputPin). Most member functions in the pin classes are implemented to call member functions in the transform filter class, so typically you need only to derive your filter from the filter class and override a few member functions to implement a transform filter.
CTransformFilter adds several member functions to those inherited from CBaseFilter. Some of these are pure virtual member functions that the derived class must override. One example is the CTransformFilter::Transform member function, which is called when the input pin receives a sample. This member function provides the core of the transform functionality. Other member functions to be overridden also involve implementations that are specific to the derived class, such as verifying media types on pins and allocating the correct amount of memory. Additionally, several CTransformFilter member functions are called at various points in the connection or streaming process; the derived class can override these to handle requirements such as adding or releasing references to interfaces.
The CVideoTransformFilter class derives from the CTransformFilter class and is used as a base class for filters that can affect the quality of a rendered video by dropping frames when the video renderer sends quality-control messages. This class is primarily used by video decompressors in the DirectShow run time.
The CBaseRenderer class and its derived class, CBaseVideoRenderer, are the base filter classes that implement a video renderer filter. The video renderer filter used in DirectShow is derived from CBaseVideoRenderer. There are other renderer classes that work in conjunction with these classes but are not derived from CBaseFilter. These classes are:
- CRendererInputPin
- CBaseControlVideo and its base class CBaseBasicVideo
- CBaseControlWindow, and its base classes CBaseVideoWindow and CBaseWindow
- CAggDirectDraw
- CAggDrawSurface
The following illustration shows all the classes that support renderers that are not derived from either CBaseFilter or CBasePin.
Pin Base Classes
Pins have a greater share of the work than filters. A pin must expose methods so that the filter graph manager can connect it with a pin on another filter. Pins also expose methods so that connected pins can negotiate what media type they will pass between them, and which pin will provide the shared memory allocator for transporting the media sample. Additionally, the output pin is responsible for passing each media sample to its connected input pin; the input pin is responsible for receiving it. Finally, pins must support interfaces so that quality-control messages and position information can be passed through the stream from pin to pin.
The following illustration shows the pin classes. All pin classes are derived from CBasePin, a base class derived from CUnknown.
CBasePin implements the IPin interface. The IPin interface specifies methods for connecting to other pins, negotiating the media type to be used with the connected pin, querying internal connections on the pin, and informing the pin of stream activity.
Besides implementing the IPin methods, CBasePin also implements IQualityControl methods so that quality-control messages can be passed through the filter graph from one pin to the next. Quality-control messages allow a filter, such as a renderer, to request another filter to adjust its sample rate. Typically, quality-control messages travel upstream from renderer to source filter. However, in cases such as a video capture filter, the source filter (for example, a VCR reader) can send quality-control messages downstream to the renderer filter to adjust its rate.
The CBasePin class provides several virtual member functions that can be overridden to provide handling of the connection, media type negotiation, and disconnection processes. Two base classes derive from CBasePin to provide default handling for many of these tasks:
- CBaseOutputPin implements an output pin.
- CBaseInputPin implements an input pin.
CBaseOutputPin is the base class for the CTransformOutputPin and CSourceStream classes. Likewise, CBaseInputPin is the base class for the CTransformInputPin class. Before looking at these derived base pin classes, it is helpful to understand the basic model the CBaseOutputPin and CBaseInputPin classes use.
In the connection and transport model used by two pins, the input pin supports the IMemInputPin interface so that it can receive a media sample. The CBaseInputPin class implements the IMemInputPin interface. Also, one of the two pins must supply a shared memory allocator object, which is an object that contains the IMemAllocator interface that generates media sample objects passed between pins. An IMemInputPin method, implemented by the CBaseInputPin class, supplies this allocator object, implemented by the CMemAllocator class. The connected output pin also has the option of supplying its own allocator; if this is the case, it notifies the input pin (through another IMemInputPin method) of the final decision of which allocator is used.
The CBaseOutputPin class provides extra member functions to set the size and count of samples in the allocator, retrieve a media sample from the allocator, deliver that media sample to the connected input pin, and deliver end-of-stream and end-flush messages downstream. It also implements many of the IPin methods.
CPullPin is a class that is used on the input pin of a parser filter. It is derived from the CAMThread class as shown in the following illustration.
A parser filter pulls information from the disk, using the asynchronous file reader filter, or from the Internet, using the URL moniker filter. CPullPin works with the IAsyncReader interface, which is implemented on the source reader filter upstream. CPullPin starts the thread, pulls data from the upstream filter, and then pushes the data downstream. That is, it can simply call its own IMemInputPin::Receive method after pulling the sample from the source (or perform the equivalent routines elsewhere).
Enumerator Base Classes
An enumerator is an interface that provides methods for traversing a list of elements. Enumerators are used in COM programming, and the DirectShow model follows the COM model in enumerating objects. Two enumerator classes are provided in the class library: CEnumPins, which implements the IEnumPins interfaces, and CEnumMediaTypes, which implements the IEnumMediaTypes interface. Two other DirectShow enumerator interfaces, IEnumFilters and IEnumRegFilters, are not represented by base classes because they are implemented only by the filter graph manager.
The CEnumPins class creates an enumerator when the IBaseFilter::EnumPins method is called. The enumerator returned by this method is a pointer to the IEnumPins interface, which is implemented by the CEnumPins class. The CEnumPins member functions can then be called to retrieve pointers to each of the pins on the filter, which this enumerator accomplishes by calling the CBaseFilter::GetPin member function on the filter. The filter must override the base class CBaseFilter::GetPin member function to supply the enumerator with the next pin in the list each time it is called.
The CEnumMediaTypes class creates an enumerator when the IPin::EnumMediaTypes method is called. Pins store a list of the media types that they support. During negotiation of the media type, one pin typically calls the EnumMediaTypes method on its connected pin, retrieves the enumerator, and uses it to select a media type. Both of these enumerator classes support the Next, Skip, Reset, and Clone methods familiar to COM programmers. The media type enumerators call the CBasePin::GetMediaType member function, which must be overridden by the derived pin class, to return the next media type in a list of media types accepted by the pin.
Enumerators operate as threads, and must have synchronized access to the pin media type list. For this reason, the classes that implement enumerators inherit (through multiple inheritance) from the CCritSec class, which provides critical section management. For more information about the CCritSec class, see Win32 Classes.
Transport Base Classes
Transport classes share memory between pins and pass media samples using that memory. DirectShow provides four classes to help implement shared memory transports:
CBaseAllocator is a class that provides member functions to implement the IMemAllocator interface, as shown in the following illustration.
The IMemAllocator interface on the input pin specifies methods to set the number and size of the buffers to allocate, allocates that memory, frees that memory, and returns a single buffer that contains an IMediaSample interface. The output pin connected to the input pin calls the IMemAllocator methods. CBaseAllocator provides the member functions Alloc and Free that are called from the Commit and Decommit methods. Derived classes override the Alloc and Free member functions to provide their own routines to allocate and free memory.
Because CBaseAllocator performs very little implementation by itself, most pins use the CMemAllocator class, which is derived from CBaseAllocator. CMemAllocator overrides the CBaseAllocator::Free member function to provide allocation of media samples based on system memory. It provides its own member function, called ReallyFree, to be called when the allocator is finally released.
CMediaSample is a class that contains the media sample data and also provides member functions to access properties on the media sample, such as data type or beginning and ending time stamps. This class implements the IMediaSample interface, which provides the method specification. CImageSample derives from CMediaSample and is used by the video renderer when the renderer's allocator is being used. It uses all the CMediaSample interface methods and adds two methods to set and retrieve the DIBSECTION information. This makes it easy for the renderer to cast the CMediaSample pointer it receives from an upstream filter to a CImageSample pointer, and obtain a handle to the bitmap of the video frame.
Media Control and Positioning Classes
Media control interfaces pass commands such as Run, Stop, or Pause from an application through the filter graph manager to the individual filters. From the filter's perspective, the only control interface necessary is IMediaFilter, which exposes methods to accept and implement these commands. The CBaseFilter class implements this interface. All other interfaces that expose media control methods are handled by the filter graph manager and are therefore already implemented. Although a CMediaControl class exists and implements the IMediaControl interface, it is not often used because the filter graph manager is responsible for this functionality. The following illustration shows the relationship between these classes and interfaces.
Media positioning interfaces start the media stream at a specified position, play the stream for a specified period of time, or change the rate of the media stream. The IMediaPosition interface is the primary interface supporting this functionality. The CMediaPosition class implements this interface and serves as a base class for two other classes: CPosPassThru and CSourcePosition.
Typically, the filter graph manager calls the IMediaPosition interface on the renderer filters when it wants to position the media stream. The renderer acknowledges the sample times that it will be expected to display and then passes the media positioning data upstream, destined for a seekable filter, such as a source file filter, that can provide the properly positioned source stream. To pass that information upstream, output pins must be able to receive the positioning information.
The CPosPassThru class implements the IMediaPosition interface and the IMediaSeeking interface on the output pins of filters and, for the most part, does nothing but call the corresponding interface on the output pin of the next upstream filter, thereby passing through the positioning data. IMediaSeeking is different than IMediaPosition in that it allows the media stream to be seeked to units other than time, such as frames, samples, or indexed fields in an MPEG format. The CRendererPosPassThru class, implemented on a video renderer, sets the start and end reference times on individual samples, so that samples can be queried at any time for this information. This is helpful when dealing with seeking using IMediaSeeking, which seeks to media time, and does not keep track of the sample's reference time.
The reason for serially informing every filter in the graph of the new position is to allow filters that might be concerned with media positioning to be prepared for the new position. Certain stream splitters, for example, might be splitting off streams with media positions relative to the main media stream. This is why the filter graph manager does not simply call the source filter's IMediaPosition or IMediaSeeking interface directly.
CSourcePosition is the class that helps the source filter implement its IMediaPosition interface.
The CSourceSeeking class helps the source filter implement its IMediaSeeking interface. This class enables a source filter to handle calls that change the start and stop positions in the media stream, and the playback rate.
The CBaseStreamControl class helps the source filter implement its IAMStreamControl interface. This class is used primarily by capture filters. The following illustration shows the relationship between CBaseStreamControl and the interfaces from which it inherits.
Clock Base Classes
DirectShow provides two classes, CBaseReferenceClock and CSystemClock to help implement clocks in the filter graph. The following illustration shows the relationship between these classes and the interfaces they implement.
CBaseReferenceClock implements IReferenceClock, and so provides the ability to return the correct reference time when requested, and to advise registered objects of specific times or time intervals through event notification and semaphores.
CSystemClock implements a system clock that provides time information and timing signals to an application. It uses the CBaseReferenceClock base class to provide most of that functionality, overriding the actual time calls.
Utility Classes
The DirectShow SDK includes several utility classes that provide C++ class encapsulation of many of the required Win32 functions, multimedia data structures, and object list and queue manipulation. These classes are briefly described in this section.
Win32 Classes
DirectShow implements several classes to handle Win32 threads, events, and critical sections. These include the following classes.
CAMEvent CCritSec CAutoLock CAMThread CMsgThread CMsg The following diagram illustrates these classes.
CAMEvent handles a Win32 event as a C++ object. The methods in this class allow events to be put into the signaled state or reset to a nonsignaled state, and also allow a caller to block until an event is signaled. Events can also be cast to handles and passed to the Win32 WaitForMultipleObjects function.
CCritSec handles a Win32 critical section as a C++ object to provide intraprocess synchronization. Methods of this class allow you to create, lock, and unlock a critical section.
CAutoLock holds a critical section (a CCritSec object) for the scope of a block or function. The critical section is locked in the constructor and unlocked in the destructor.
CAMThread provides an abstract worker thread class enabling creation, synchronization, and communication with a worker thread.
CMsgThread provides support for a worker thread to which requests can be posted asynchronously instead of being sent directly. Messages, in the form of a CMsg object, can be posted to a CMsgThread object.
CMsg creates an object containing a message to be passed to a CMsgThread object.
List and Queue Classes
DirectShow implements the CBaseList, CGenericList, and COutputQueue classes for handling lists and queues as illustrated in the following diagram.
CBaseList represents a linked list data structure of typeless pointers to objects derived from CBaseObject.
CGenericList implements a template class derived from CBaseList that calls CBaseList member functions and adds type checking for the type specified in the template.
COutputQueue supports the queuing of media samples from the output pin of a filter. The output pin calls member functions of this class instead of calling methods on the connected input pin to receive the media sample. The output pin is then free to continue without blocking, while the COutputQueue class handles the passing of the media samples downstream.
Multimedia Data Type Classes
DirectShow implements the CMediaType, CRefTime, and FOURCCMap multimedia data type classes as shown in the following illustration.
CMediaType provides a C++ class object containing the media type data structure and methods that provide access to each of the members of the structure.
CRefTime provides a C++ class object containing the methods used to access the reference time, and operators used to perform Boolean tests or arithmetical operations on two CRefTime objects.
FOURCCMap provides conversion between the older-style FOURCC media tags used to identify and register media types and the GUID media subtypes used by DirectShow.
COM Classes
COM interface classes in DirectShow fall into two groups: object creation and interface implementation. Class factory classes are provided for object creation, and other classes are provided to implement existing COM interfaces.
The COM utility classes include the following.
CClassFactory CFactoryTemplate CPersistStream CBasePropertyPage The following illustration shows the relationship between the COM classes and the interfaces they implement.
CClassFactory and CFactoryTemplate are implemented by the base classes to handle automatic instantiation of filters, pins, and other DirectShow COM objects. These classes provide a scaffolding for object construction which wraps the actual COM elements required to construct an object. CPersistStream and CBasePropertyPage help with implementing COM persistent storage and property page interfaces.
CClassFactory, located in dllentry.cpp, inherits from CBaseObject and implements the COM IClassFactory interface. This interface is used by CoCreateInstance, which instantiates a COM object by calling IClassFactory::CreateInstance, which, in turn, calls the static CreateInstance member function in your derived class.
The base classes use CFactoryTemplate to provide CClassFactory with a template containing the CLSID of your object and a pointer to the static CreateInstance function for your object class.
CPersistStream implements COM IPersistStream for the storage and retrieval of filter properties in a saved filter graph. This enables a stored filter graph to have filters set to predefined property values. This class also provides a special member function to handle versioning of data in a stream.
CBasePropertyPage implements the COM IPropertyPage interface, which provides a framework for a property page associated with a filter.
Debugging Classes
DirectShow provides many debugging functions and macros as described in the Debugging reference section. It also includes three classes that aid in debugging filter development:
CDispBasic CDisp CGuidNameList The following diagram illustrates these classes.
CDispBasic converts the m_PString data member to the proper string size.
CDisp provides a constructor that sets the CDispBasic class's m_PString data member to a string describing some relevant debugging information about the object used as a parameter to the constructor. For example, when constructed with an IPin pointer, m_PString returns the name of the pin; when constructed with a CLSID, m_PString returns a string representation of it, and so on. The class also provides an LPCTSTR cast operator that returns the value of m_PString, so the class can simply be cast as an LPCTSTR value to return the string when constructed.
CGuidNameList implements an array of globally unique identifier (GUID) names in the Uuids.h include file. This enables you to retrieve the GUID name for a media type, for example.
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.