
| Support This Site | I work on this site in my off hours. Please help me to push aside my day job and work on it more by supporting the site in one of these ways: |
![]() | Use your credit card or PayPal to donate in support of the site. |
![]() | Use this link to Amazon—you pay the same, I get 4%. |
![]() | Learn Thai with my Talking Thai-English-Thai Dictionary app: iOS, Android, Windows. |
![]() | Experience Thailand richly with my Talking Thai-English-Thai Phrasebook app. |
![]() | Visit China easily with my Talking Chinese-English-Chinese Phrasebook app. |
![]() | I co-authored this bilingual cultural guide to Thai-Western romantic relationships. |
| Submit This Site | Like what you see? Help spread the word on social media: | |||
| ||||
Note: Updated Lurker's Guide available (but not this page!)This page belongs to the old 1990s SGI Lurker's Guide. As of 2008, several of the Lurker's Guide pages have been updated for HDTV and for modern OS platforms like Windows and Mac. This particular page is not one of those, but you can see what new stuff is available here. Thanks!
By Michael Portuesi.
Because the VL supports so many different pieces of video hardware, you might be tempted to think that for a given task, such as displaying incoming video on the workstation screen, that you may write one piece of code for all the devices VL supports. Unfortnately you cannot, and nowhere is this more apparent than when you are programming video display using Galileo (ev1) and its relatives.
With most video devices that support a full rate video path to memory, you can create a video to memory path and display the video in your application using OpenGL. Your application wakes up whenever the transfer buffer is full, retrieves a frame from the buffer, and draws the frame to the display.
Galileo does not offer you this option; its video to memory path does not support a full-rate stream into memory. Instead, it offers a VL screen node. You create a video to screen path using the VL, set some controls on the path to tell the screen node the X window it should use and where the video should be located, then call vlBeginTransfer() to display the video to the user.
The code to display video using ev1 differs from other devices not only in the path setup, but in the maintenance work your application must perform. Using ev1, the display work is handled automatically; your application need never service a transfer buffer. But your application must keep the video positioned and sized appropriately, and update its position if the user moves the window. This task is the central topic of this document.
The ev1 video device (in all its forms) provides the programmer with three screen drain nodes. Here's how they are described by vlinfo:
lipstick.esd.sgi.com % vlinfo name of server: number of devices on server: 2 device: ev1 1 nodes = 14
[entries omitted]
Screen Drain A, type = Drain, kind = Screen, number = 0 Screen Drain B, type = Drain, kind = Screen, number = 1 Screen Drain C, type = Drain, kind = Screen, number = 2
[entries omitted]
However, in practical use, you will likely use only the first two nodes - numbers 0 and 1. Node 1 can be divided into two half-sized 12-bit screen displays. When you divide screen drain node 1, the second of the two half-displays is denoted by node number 2. This special "half size" mode is not discussed in this document, and judging from the restrictions on its use over and above those discussed here, it seems inappropriate at best for general-purpose application software.
By far, the biggest headache facing the ev1 programmer is simply positioning the video at the right place on the screen. The video drain doesn't render into the framebuffer, as does all other visual display on the system. Instead, the ev1 device provides a hardware-generated "overlay" which is merged with the contents of the frame buffer. As such, all attributes of the video display including screen position and size are controlled through the VL, rather than through X or OpenGL.
To make matters worse, the ev1 hardware imposes a raft of restrictions on where and and under what circumstances the video overlay can be positioned. None of them are particularly friendly to the application developer. Here is a list of the problems:
VL_ORIGIN control immediately after it sets it, and then moves its X window to where the video landed! See below for advice on how to handle this situation in a better, if not totally, acceptable way.VL_ORIGIN control after setting it and check to see if it is in the same spot you asked for. A civilized application program must allow its windows to go where the user places them. We can forgive videoin due to its simplicity. But a better solution for most applications is to put the window where the user wants it, but to post a notifier if the ev1 video display cannot be placed to match the window location. This might disappoint the user, but nevertheless it still leaves them feeling like they have control over the windows on their screen.
void installStructureNotifyHandler()
{
if (handlerActive) return;
Widget shell = _WidgetDisplayingVideo;
while (!XtIsShell(shell)) {
XtAddEventHandler(shell, StructureNotifyMask,
FALSE, &structureNotifyHandler,
(XtPointer) this);
shell = XtParent(shell);
}
XtAddEventHandler(shell, StructureNotifyMask,
FALSE, &structureNotifyHandler,
(XtPointer) this);
handlerActive = True;
}
void removeStructureNotifyHandler()
{
if (!handlerActive) return;
Widget shell = _WidgetDisplayingVideo;
while (!XtIsShell(shell)) {
XtRemoveEventHandler(shell, StructureNotifyMask,
FALSE, &structureNotifyHandler,
(XtPointer) this);
shell = XtParent(shell);
}
XtRemoveEventHandler(shell, StructureNotifyMask,
FALSE, &structureNotifyHandler,
(XtPointer) this);
handlerActive = False;
}
void structureNotifyHandler(Widget w, XtPointer client_data,
XEvent* event, Boolean*)
{
switch (event->type) {
case ConfigureNotify:
// we've been moved around onscreen. update our video display
if (XtIsRealized(w)) {
//
// figure out dimensions and screen coordinates of the
// video widget
//
Position x, y;
Position rootx, rooty;
Dimension widgetW, widgetH;
XtVaGetValues(w,
XmNx, &x,
XmNy, &y,
XmNwidth, &widgetW,
XmNheight, &widgetH,
NULL);
XtTranslateCoords(XtParent(w), x, y, &rootx, &rooty);
//
// armed with root x and root y, we now calculate the
// placement of the video overlay.
//
if (!displayPath) return;
// determine size of video display.
VLControlValue val;
vlGetControl( svr, displayPath, src, VL_SIZE, &val );
int sizeW = val.xyVal.x;
int sizeH = val.xyVal.y;
// divide by current ZOOM setting. assume that zoom
// control is 1/n.
vlGetControl( svr, displayPath, drn, VL_ZOOM, &val );
sizeW /= val.fractVal.denominator;
sizeH /= val.fractVal.denominator;
//
// find the origin coordinates of the video overlay,
// taking into account that the video overlay cannot
// go even partially off the screen.
//
int originX = rootx + ((Position) (widgetW / 2) -
(Position) (sizeW / 2));
int originY = rooty + ((Position) (widgetH / 2) -
(Position) (sizeH / 2));
// did we go off the screen? if so, make adjustments.
Dimension screenWidth = WidthOfScreen(XtScreen(w));
Dimension screenHeight = HeightOfScreen(XtScreen(w));
if (originX < 0) { // left edge
originX = 0;
}
if (originY < 0) { // top edge
originY = 0;
}
if (originX + sizeW > screenWidth) { // right edge
originX = screenWidth - sizeW - 1;
}
if (originY + sizeH > screenHeight) { // bottom edge
originY = screenHeight - sizeH - 1;
}
// place the video overlay.
val.xyVal.x = originX;
val.xyVal.y = originY;
vlSetControl(svr, displayPath, drn, VL_ORIGIN, &val);
// verify the video was placed where we want it.
checkVideoOverlayLocation(originX, originY);
}
break;
default:
break;
}
}
VL_ORIGIN shouldn't just check to see that the origin is different than you asked - it should try to determine whether the problem is due to overlapping video, or bumping against the edge of the display. You may not want to complain to the user or take drastic action if the video is merely running against the edges of the screen.
void checkVideoOverlayLocation(Position originX, Position originY)
{
VLControlValue actualVal;
vlGetControl( svr, displayPath, drn, VL_ORIGIN, &actualVal);
//
// Collisions between video overlays occur only when they are
// on the same raster lines (horizontal), so comparing just the
// y-value is sufficient to detect these cases.
//
// We do not worry about offscreen placement,
// because that is taken into account elsewhere.
//
if (actualVal.xyVal.y != originY) {
//
// video overlay didn't position properly. Your app should
// take some sort of action here. Keep in mind that if you
// post an error dialog, you should also set a state flag so
// that you do not post the dialog more than once. This
// code will (unfortunately) get called multiple times,
// because the event handler for ConfigureNotify events is
// installed on several widgets in the hierarchy.
//
}
//
// Compare the x values to determine
// if the right edge of the display
// cannot go past pixel 1271
//
if (actualVal.xyVal.x != originX) {
// your app might want to do something here
}
}
VL_ZOOM control, you also need to set the VL_SIZE control at the same time to guarantee the video displays properly. Yet another unpleasant side effect of the ev1 video overlay are the "jailbars" which appear when the window is larger than the video, or when the user moves around on the screen a window containing ev1 video.
The "jailbars" happen when you assign an X window to the video node by setting the VL_WINDOW control on the path. The graphics hardware ignores the framebuffer contents for that region of the display, replicating the last value that was in its hardware registers. That last value is usually the pattern for the window manager border, which when replicated looks like a row of brass bars on the display.
If the window is larger than the video overlay, the regions of the window above and below the video overlay which don't contain video information will display jailbars. There is no way around this. There are a few ways to deal with the problem:
void createJailBarWidgets()
{
//
// top widget
//
topArea = XtVaCreateManagedWidget
( "topArea",
xmDrawingAreaWidgetClass,
form,
XmNtopAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_NONE,
XmNleftAttachment, XmATTACH_FORM,
XmNrightAttachment, XmATTACH_FORM,
XmNrightPosition, 0,
XmNtopOffset, 0,
XmNbottomOffset, 0,
XmNleftOffset, 0,
XmNrightOffset, 0,
XmNwidth, topAreaWidth,
XmNheight, topAreaHeight,
XmNbackground, BlackPixelOfScreen(XtScreen(form)),
(XtPointer) NULL );
//
// bottom widget
//
bottomArea = XtVaCreateManagedWidget
( "bottomArea",
xmDrawingAreaWidgetClass,
form,
XmNtopAttachment, XmATTACH_NONE,
XmNbottomAttachment, XmATTACH_FORM,
XmNleftAttachment, XmATTACH_FORM,
XmNrightAttachment, XmATTACH_FORM,
XmNtopPosition, 0,
XmNbottomPosition, 0,
XmNrightPosition, 0,
XmNtopOffset, 0,
XmNbottomOffset, 0,
XmNleftOffset, 0,
XmNrightOffset, 0,
XmNwidth, bottomAreaWidth,
XmNheight, bottomAreaHeight,
XmNbackground, BlackPixelOfScreen(XtScreen(form)),
(XtPointer) NULL );
}
void placeJailBarWidgets(Dimension widgetW, Dimension widgetH,
Position originX, Position originY,
Position rootY, Dimension videoH)
{
Dimension bottomAreaWidth, bottomAreaHeight;
Dimension topAreaWidth, topAreaHeight;
bottomAreaWidth = topAreaWidth = widgetW;
int newTopHeight = ((int) originY) - ((int) rooty);
if (newTopHeight < 0) {
topAreaHeight = 0;
} else {
topAreaHeight = newTopHeight;
}
int newBottomHeight = ((int) widgetH) -
(((int) originY) - ((int) rooty) + ((int) videoH));
if (newBottomHeight < 0) {
bottomAreaHeight = 0;
} else {
bottomAreaHeight = newBottomHeight;
}
XtVaSetValues(topArea,
XmNheight, topAreaHeight,
(XtPointer) NULL);
XtVaSetValues(bottomArea,
XmNheight, bottomAreaHeight,
(XtPointer) NULL);
}
IMPACT Graphics do not display jailbars when used with ev1. But unless you know your application will run only on IMPACT, you need to worry about jailbars.
Finally, once a window is used with the ev1 video display node, it cannot be used for anything else. You cannot render into the window using X or GL drawing operations, even if you destroy the ev1 path which was displaying into the window. Once a window is in jail, it's a life sentence. The only surefire way to "get out of jail" is to destroy the window and create one anew.
There is a very common problem encountered by people who try to display ev1 video within an X or Motif program. If you call vlBeginTransfer() before the window intended to display it is fully created and mapped (shown) to the screen, the video will not display.
This problem occurs because the X window system is asynchronous. The X server does not immediately create and display windows when the application invokes the appropriate calls. Instead, the requests are queued up for the server, which performs them at some arbitrary point in the future. If you start the transfer immediately after calling a function like XtManageChild() or XMapWindow(), the video display won't work.
There are two ways to deal with the problem.
StructureNotifyMask. When you receive a MappingNotify event, the X server has mapped the window to the display, and it is safe to start the video transfer.switch statement in the StructureNotifyHandler sample function shown above. This is the preferred, though the more complicated, method of dealing with the problem.XSync() after you make the call to display the window. This will force all outstanding requests to be sent to the X server, and XSync() will wait until the server has processed them.When your code receives a VLStreamAvailable event for the display path, your code should do the following:
vlSetupPaths() to set the path up once again:vlSetupPaths(svr, &displayPath, 1, VL_SHARE, VL_SHARE);VL_ORIGIN, VL_SIZE and VL_ZOOM controls for the path, since the previous app using this path have left it in an unknown state.vlBeginTransfer() to restart the video transfer:vlBeginTransfer(svr, displayPath, 0, NULL); | Support This Site | I work on this site in my off hours. Please help me to push aside my day job and work on it more by supporting the site in one of these ways: |
![]() | Use your credit card or PayPal to donate in support of the site. |
![]() | Use this link to Amazon—you pay the same, I get 4%. |
![]() | Learn Thai with my Talking Thai-English-Thai Dictionary app: iOS, Android, Windows. |
![]() | Experience Thailand richly with my Talking Thai-English-Thai Phrasebook app. |
![]() | Visit China easily with my Talking Chinese-English-Chinese Phrasebook app. |
![]() | I co-authored this bilingual cultural guide to Thai-Western romantic relationships. |
| Copyright | All text and images copyright 1999-2023 Chris Pirazzi unless otherwise indicated. |