i
i
i
i
i
i
i
i
448 16. Programming Stereopsis
Figure 16.7. The main features and data flow of the stereo-movie player application.
small modification to the stereo image display program will be all that is re-
quired to provide the visualization of the video using OpenGL pixel drawing
functions. Figure 16.7 illustrates the block structure and data flow for the
program.
This program uses the stereo-image display programs code as its start-
ing point. The application contains a message processing loop and it has a
small main window (as before). It also creates a child window to display the
contents of the RAM frame buffer using OpenGL. A timer is used to post
WM PAINT messages 50 times per second to the display window and hence
render the video frames in the RAM buffer. At this point in the code, if the
FilterGraph has not been built or is not running then no movie is playing the
RAM buffer will be empty and the window (or screen) will just appear blank.
Figure 16.8. The FilterGraph for the AVI stereo movie player. Only the custom
rendering filter at the end of the chain is unique to this player.
i
i
i
i
i
i
i
i
16.5. A Stereoscopic Movie Player 449
In response to a command to play a movie, the application program will use
DirectShow to build and run an appropriate FilterGraph. By combining the
FilterGraph (which also renders the audio) and the OpenGL display, we will
have an application program that takes a single AVI file with left-o ver-right
stereoscopic frames, encoded using any codec (so long as that codec is in-
stalled on the computer) and plays it back in either a window or by filling
the screen. Figure 16.8 shows the filter components of the DirectShow graph
that we need.
Next we turn to the design of the custom renderer filter.
// Although a GIUD for the filter will not be used we must still declare
// one because the base class requires it.
struct
__declspec(uuid("{C7DF1EAC-A2AB-4c53-8FA3-BED09F89C012}")) CLSID_OpenGLRenderer;
class CRendererOpenGL : public CBaseVideoRenderer
{
public: // Class constuctor and destructor
// pointer to application object
CRendererOpenGL(CMoviePlayer
*
pPres,
LPUNKNOWN pUnk,HRESULT
*
phr); // standard COM pointers
˜CRendererOpenGL();
public: // these methods must be overidden -
// see individual listings for details
// Is the Video Format acceptable?
HRESULT CheckMediaType(const CMediaType
*
pmt );
// Video format notification
HRESULT SetMediaType(const CMediaType
*
pmt );
// New video sample (THIS FILLS THE BUFFER
HRESULT DoRenderSample(IMediaSample
*
pMediaSample);
// USED BY OPENGL
private: // member variables
// pointer to a C++ object we will use to encapsulate the whole Filter graph
CMoviePlayer
*
m_pCP;
LONG m_lVidWidth; // Video/movie width
LONG m_lVidHeight; // Video/movie height
// Pitch is the number of bytes in the video buffer required to step to next row
LONG m_lVidPitch;
};
Listing 16.4. The derived class for the renderer filter. The member variables record
the dimension of the movie/video frames and the pitch specifies how the addresses in
RAM change from one row of pixels to the next. This allows the rows in an image to
be organized into memor y blocks that are not necessarily stored contiguously.
i
i
i
i
i
i
i
i
450 16. Programming Stereopsis
16.5.1 The Renderer Filter
If you arent familiar with DirectShow and its filter concept, now would be
a good time to read Chapter 15 and in particular Section 15.6, where we
describe a monoscopic version of this filter.
CRendererOpenGL::CRendererOpenGL( // constructor
// pointer to application object
CMoviePlayer
*
pPres,
LPUNKNOWN pUnk,
HRESULT
*
phr ) //
: CBaseVideoRenderer(__uuidof(CLSID_OpenGLRenderer), // configure base class
NAME("OpenGL Renderer"), pUnk, phr)
// initialize member variable
, m_pCP( pPres){
// simply notify - everything OK
*
phr = S_OK;
}
CRendererOpenGL::˜CRendererOpenGL(){ // do nothing
}
Listing 16.5. The video renderer class constructor and destructor. The class construc-
tor has very little work to do. It simply initializes the base class and one of the
member variables.
HRESULT CRendererOpenGL::CheckMediaType(const CMediaType
*
pmt){
// only allows RGB video sample formats
HRESULT hr = E_FAIL;
VIDEOINFO
*
pvi=0;
// Reject this option if not a video type
if(
*
pmt->FormatType() != FORMAT_VideoInfo ) return E_INVALIDARG;
pvi = (VIDEOINFO
*
)pmt->Format();
if( IsEqualGUID(
*
pmt->Type(), MEDIATYPE_Video)){
// Only accept RGB 24 bit formatted frames
hr = S_OK;
if( IsEqualGUID(
*
pmt->Subtype(), MEDIASUBTYPE_RGB24) ){; }
else hr = DDERR_INVALIDPIXELFORMAT;
}
return hr;
}
Listing 16.6. Check media type: return S OK when a suitable video format has been
enumerated.
i
i
i
i
i
i
i
i
16.5. A Stereoscopic Movie Player 451
To implement the filter, we derive a class, CRendererOpenGL,fromthe
base class
CBaseVideoRenderer and override three of its methods. List-
ing 16.4 presents the deriv ed class details.
By deriving the
CRendererOpenGL class from CBaseVideoRenderer,all
the standard behavior and interfaces which the base class exhibit, will be
present in the derived one. Listing 16.5 shows the classs constructor and
destructor methods.
To implement the OpenGL rendering filter, it only remains to override
the following three base class methods:
1.
CheckMediaType(). Listing 16.6 presents the code for this method.
Its only function is to determine whether the renderer can use an input
media sample in a particular format.
// gives us data about video
HRESULT CRendererOpenGL::SetMediaType(const CMediaType
*
pmt){
HRESULT hr = S_OK;
// BitmapInfo header format
VIDEOINFO
*
pviBmp = NULL;
// get the data
pviBmp = (VIDEOINFO
*
)pmt->Format();
// extract width and height
X = m_lVidWidth = pviBmp->bmiHeader.biWidth;
Y = m_lVidHeight = abs(pviBmp->bmiHeader.biHeight);
// calculate Pitch - must be even byte
m_lVidPitch = (m_lVidWidth
*
3 + 3) & ˜(3);
// if exists remove it since this is new
if(ScreenL != NULL)free(ScreenL); ScreenL=NULL;
if((ScreenL = (unsigned char
*
)malloc(3
*
X
*
Y)) == NULL){
hr = E_UNEXPECTED; // can’t do it
return hr;
}
// stereo video - left over right
if(ScreenL != NULL){
// calculate address of Right image
ScreenR =(ScreenL + ((Y/2)
*
X));
// Double height in file
if(bStereoDraw)Y /= 2;
}
return hr;
}
Listing 16.7. Set media type: extract information about the movies video stream and
allocation RAM buffers ready to hold the decoded frame images.
i
i
i
i
i
i
i
i
452 16. Programming Stereopsis
2. SetMediaType(). Our filter uses this method to allocate a RAM
buffer in which to store the video frames. Listing 16.7 presents the
code.
3.
DoRenderSample(). This does most of the work. It picks up a pointer
to the video sample being delivered by the DirectShow filter chain
and copies the video pixel data into the RAM buffer identified by the
pointer
ScreenL (see Listing 16.8).
That completes the implementations of the stereo renderer filter. Apart
from the custom rendering filter, all the other components in the FilterGraph
are the same as we encountered in the player discussed in Section 15.3.
HRESULT CRendererOpenGL::DoRenderSample( IMediaSample
*
pSample ){
HRESULT hr = S_OK;
BYTE
*
pSampleBuffer = NULL,
*
S = NULL;
UINT row,col,yy;
if( !pSample ) return E_POINTER;
hr = pSample->GetPointer( &pSampleBuffer );// get pointer to image pixels
if( FAILED(hr)){return hr;} // cannot get image pixels
// pointer to RAM buffer to be rendered by OpenGL
S=ScreenL;
if(X > 0 && Y > 0 && S != NULL){
// stereo drawing so the iamge is 2 times as high
if(bStereoDraw)yy=Y
*
2; else yy=Y;
// Copy the image - we need to do it pixel by
for(row = 0; row < yy; row++ ) {
// by pixel because the order of RGB in the video
BYTE
*
pBmpBufferOld = pSampleBuffer;
// stream is reversed. Versions of OpenGL > 1.0
for (col = 0; col < X; col++) {
// allow different byte orderings which would mean we
*
S++ = pSampleBuffer[2];
// could copy the samples one row at a time but
*
S++ = pSampleBuffer[1];
// because of the PITCH we cannot copy the whole
*
S++ = pSampleBuffer[0];
// image as a single block.
pSampleBuffer += 3;
}
pSampleBuffer = pBmpBufferOld + m_lVidPitch;
}}
return hr;
}
Listing 16.8. The DoRenderSample() method is overridden from the base class to
gain access to the video samples.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.47.163