VLGETFRONTIERMSC(3dm) VLGETFRONTIERMSC(3dm)
vlGetFrontierMSC - get the frontier MSC associated with a particular
VL_MEM node
#include <dmedia/vl.h>
stamp_t vlGetFrontierMSC(VLServer server, VLPath path, VLNode memNode);
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
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
Page 1
VLGETFRONTIERMSC(3dm) VLGETFRONTIERMSC(3dm)
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
vlDMBufferGetFilledByNodevlGetFilled(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;
Page 2
VLGETFRONTIERMSC(3dm) VLGETFRONTIERMSC(3dm)
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;
}
Page 3
VLGETFRONTIERMSC(3dm) VLGETFRONTIERMSC(3dm)
}
/* 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.
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.
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
Page 4
VLGETFRONTIERMSC(3dm) VLGETFRONTIERMSC(3dm)
pages.
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.
dmGetUST(3dm), vlGetUSTMSCPair(3dm), vlGetFilled(3dm)
PPPPaaaaggggeeee 5555 [ Back ]
|