vlGetFrontierMSC(3dm)
vlGetFrontierMSC - get the frontier MSC associated with a particular VL_MEM node
As shipped in IRIX 6.5.15. Unchanged since IRIX 6.5.
NAME vlGetFrontierMSC - get the frontier MSC associated with a particular VL_MEM node SYNOPSIS #include <dmedia/vl.h> stamp_t vlGetFrontierMSC(VLServer server, VLPath path, VLNode memNode); PARAMETERS server expects the VL server to use path expects the VLPath for which you want a frontier MSC memNode expects the VL_MEM VLNode on the VLPath for which you want a frontier MSC DESCRIPTION vlGetFrontierMSC(3dm) returns the frontier MSC associated with the buffer for the given VLNode, for a given path that contains a VL_MEM node. This quantity is used to detect underflow and overflow in a buffer, and it is also used (along with vlGetUSTMSCPair(3dm)) to compute the time at which fields/frames came in or went out of the machine. This function only returns useful information for a path under the following circumstances: the path must have a VL_MEM node and a VLBuffer or DMbufferpool. The path must already be set up (see vlSetupPaths(3dm)). The path's transfer must already have begun (see vlBeginTransfer(3dm)). This function is currently only supported for VL paths in continuous mode (see vlBeginTransfer(3dm)). This function is currently only supported for VL paths whose VL_RATE is the maximum rate as indicated by VL_TIMING. A "media stream sample" is one item in a VLBuffer or DMbufferpool. Therefore, if the VL_MEM node in the given path has its VL_CAP_TYPE control set to VL_CAPTURE_INTERLEAVED (frames), a media stream sample is a frame. For all other settings of VL_CAP_TYPE, a media stream sample is a field. Whenever any VL path is open in continuous mode, the device is continuously trying to dequeue media stream samples from the path's buffer for output, or to enqueue media stream samples onto the path's buffer for input. The device keeps a counter called the "device MSC" (device media stream count) which increments by one every time the device attempts to enqueue or dequeue a media stream sample (it increments whether or not the attempt is successful). The device MSC establishes a numbering scheme for all of the media stream samples in the path's Buffer. This is the same numbering scheme used by the function vlGetUSTMSCPair(3dm). The application does not access the device MSC directly. Instead, the application accesses a quantity called the "frontier MSC," which is equal to: for input paths: device MSC - vlGetFilled(buffer) for output paths: device MSC + vlGetFilled(buffer) If you are using one of the DMbuffer APIs, then substitute vlDMBufferGetFilledByNode(3dm) or vlGetFilledByNode(3dm) for vlGetFilled(3dm). If the given buffer is not underflowing and it is not overflowing, then the frontier MSC can be thought of in this way: 1. for input paths, the frontier MSC is the MSC of the next item the application is about to remove from the path's buffer using vlGetNextValid(3dm), vlDMBufferGetValid(3dm), or vlEventRecv(3dm). 2. for output paths, the frontier MSC is the MSC of the next item the application is about place into the path's buffer using vlPutValid(3dm), vlDMBufferPutValid(3dm), or vlDMBufferSend(3dm) (thus, it is not the MSC of an item currently in the buffer). vlGetFrontierMSC(3dm) and vlGetUSTMSCPair(3dm) can be used together to compute the time at which a given media stream sample came in or went out of the machine, in the following way: /* input: find the UST at which the next field/frame that we dequeue from our buffer came in the jack of the machine. output: find the UST at which the next field/frame that we enqueue on our buffer will go out the jack of the machine. in both cases, the desired field/frame is the one whose MSC is the frontier MSC. this code would also work if we wanted a UST for an MSC other than the frontier MSC. */ double ust_per_msc; USTMSCpair pair; stamp_t frontier_msc; stamp_t desired_ust; ust_per_msc = vlGetUSTPerMSC(server, path, memNode); vlGetUSTMSCPair(server, path, video_node, video_port, memNode, &pair); frontier_msc = vlGetFrontierMSC(server, path, memNode); desired_ust = pair.ust + (frontier_msc - pair.msc)*ust_per_msc; It is imperative that the path's buffer not be underflowing and that it not be overflowing when this code segment is executed. Otherwise the results are undefined. To understand why, and to understand how to use the frontier MSC to detect underflow or overflow, consider this: When an input buffer is overflowing, this means that the device has attempted to place one or more items on the buffer and failed. Therefore, the device MSC has incremented one or more times, but the result of vlGetFilled(3dm) (or equivalent, for DMbuffers) stays the same (since no new items actually go into the buffer). Therefore, the frontier MSC is incrementing continuously, and any measurement made using the frontier MSC is not valid because the frontier MSC could change at any time. Similarly, when an output buffer is underflowing, this means that the device has attempted to remove one or more items from the buffer and failed. So again, the device MSC increments but the result of vlGetfilled(3dm) (or equivalent, for DMbuffers) stays the same. And thus the frontier MSC increments continuously. When a buffer is neither underflowing nor overflowing, the frontier MSC remains constant. This is because every time the device enqueues or dequeues an item (causing the device MSC to increment), the result of vlGetFilled(3dm) (or equivalent, for DMbuffers) increments or decrements to compensate. In this situation, the following invariant exists: the frontier MSC only changes when the application enqueues or dequeues data. It changes by exactly N when the application enqueues or dequeues N items from the buffer. An application can use this behavior to detect buffer underflows and overflows as soon as they happen, and to measure the exact length of the condition. This can be done with a simple addition to a program's main data-reading or data-writing loop: /* the input case */ { stamp_t newmsc, oldmsc=-1; /* this is your main data-reading loop, not a special one */ while (1) { info = vlGetNextValid(server, buffer); ... vlPutFree(server, buffer); newmsc = vlGetFrontierMSC(server, path, memNode); if (oldmsc > 0 && newmsc != oldmsc + 1) printf("we overflowed by %lld MSCs!\n", (newmsc-oldmsc) - 1); oldmsc = newmsc; } } /* the basically identical output case */ { stamp_t newmsc, oldmsc=-1; int transfer_size = vlGetTransferSize(server, path); /* this is your main data-writing loop, not a special one */ while (1) { info = vlGetNextFree(server, buffer, transfer_size); ... vlPutValid(server, buffer); newmsc = vlGetFrontierMSC(server, path, memNode); if (oldmsc > 0 && newmsc - oldmsc != 1) printf("we underflowed by %lld MSCs!\n", (newmsc-oldmsc) - 1); oldmsc = newmsc; } } In these code segments, the call to vlGetFrontierMSC(3dm) can appear before or after the call to vlPutFree(3dm)/ vlPutValid(3dm), and all underflows and overflows will eventually be detected. But if one wishes to use the snapped frontier MSC along with vlGetUSTMSCPair(3dm), then it is imperative that the call to vlGetFrontierMSC(3dm) appear after the call to vlPutFree(3dm)/ vlPutValid(3dm), as shown above. This is because these "put" calls take the buffer out of any possible underflow or overflow. You can replace the VLBuffer functions in the code above with DMbuffer functions. UST/MSC works the same way. The frontier MSC is a full 64-bit counter, so you do not have to worry about its value wrapping in practical code. Furthermore, the frontier MSC never takes on a negative value. RETURN VALUE If successful, vlGetFrontierMSC(3dm) returns a positive frontier MSC. A value of (stamp_t)-1 indicates an error, and vlGetErrno(3dm) can be called to return the error code. NOTE Do not confuse the frontier MSC returned by vlGetFrontierMSC(3dm) or the UST/MSC pair returned by vlGetUSTMSCPair(3dm) with the UST/MSC stamps contained in a DMbuffer, returned by dmBufferGetUSTMSCpair(3dm) for a video to memory VLPath. The UST and MSC values returned by dmBufferGetUSTMSCpair(3dm) refer to that particular buffer, whereas the frontier MSC and UST/MSC pair returned by vlGetFrontierMSC(3dm) and vlGetUSTMSCPair(3dm) have different properties, described in their man pages. CAVEATS See vlGetUSTPerMSC(3dm) for an important note about how far you can extrapolate from the UST in a UST/MSC pair. For some VL devices, there is a short initial period (up to ten field times) in the lifetime of a transfer during which no frontier MSC is available. This period begins when the application calls vlBeginTransfer(3dm) and ends when the device clocks in or out its first media stream sample from the application's buffer. An attempt to call vlGetFrontierMSC(3dm) during this period will block the application until the end of the period, when a valid frontier MSC is available. An application that does not wish to block may determine whether the period has ended by watching for the first change in vlGetFilled(3dm) (the first increment for input or the first decrement for output), or by waiting for the first VLTransferComplete, VLOddVerticalRetrace, VLEvenVerticalRetrace, or VLFrameVerticalRetrace event after vlBeginTransfer(3dm). If you are using vlGetFrontierMSC(3dm) to detect underflow/overflow as shown above, you cannot use vlGetLatestValid(3dm). vlGetLatestValid(3dm) dequeues an unknown number of media stream samples from a VLBuffer, and thus you have no idea what jump in the frontier MSC to expect. A simple workaround is to implement vlGetLatestValid(3dm) yourself using vlGetFilled(3dm) and vlGetNextValid(3dm) (this is how the VL version is implemented anyway), keeping track of the number of items dequeued. SEE ALSO dmGetUST(3dm), vlGetUSTMSCPair(3dm), vlGetFilled(3dm)