/* videoport.h - uncompressed video */ #include #include #define max(a,b) ( ((a)>(b)) ? (a) : (b)) #define min(a,b) ( ((a)<(b)) ? (a) : (b)) #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* field types. * for precise definitions, please see the Lurker's Guide page: * "Definitions: F1/F2, Interleave, Field Dominance, and More" */ #define FIELD_F1 1 #define FIELD_F2 2 typedef struct videofield { /* for app use: */ void *pixels; stamp_t ust; stamp_t msc; int fieldtype; /* (FIELD_...) */ /* for internal use: */ VLInfoPtr info; int idx; } videofield; #define VIDEOPORT_DIRECTION_VM 0 /* vid to mem, input, capture, record */ #define VIDEOPORT_DIRECTION_MV 1 /* mem to vid, output, laydown, playback */ struct _videoport; typedef struct _videoport *videoport; /* error tokens */ #define VIDEOPORT_SUCCESS 0 #define VIDEOPORT_DEVICE_BUSY 1 /* some resource in use; can't open */ #define VIDEOPORT_MALLOC_FAILED 2 /* flags argument to videoport_open() */ #define VIDEOPORT_ADVISE_NOACCESS 1 /* * opens video, set to full-size, full-rate VL_PACKING_YVYU_422_8 fields. * * direction: pass in VIDEOPORT_DIRECTION_* * * buf_fields: capacity, in fields, of video buffer. buf_fields & ~0x1 * needs to be enough fields to ride over the worst case delay between * calls to videoport_open(), videoport_wait_for_space_or_data(), * videoport_checkfdset() or videoport_setfdset(). * for info on this delay see the lurker page: * "Seizing Higher Scheduling Priority" * * flags: * use VIDEOPORT_ADVISE_NOACCESS if you won't be touching buffer with CPU * * videoport_open() mallocs and returns a _videoport for you, or NULL on error * videoport_open() sets oserror() to a VIDEOPORT_ token on error * videoport_close() frees the _videoport for you. * * XXX videoport assumes F1 dominance. see XXX in videoport.c for more info. */ videoport videoport_open(int direction, int buf_fields, int flags); void videoport_close(videoport me); /* * VIDEOPORT_DIRECTION_VM: returns number of fields of data available now * VIDEOPORT_DIRECTION_MV: returns number of fields of space available now */ int videoport_available_field_count(videoport me); /* * VIDEOPORT_DIRECTION_VM: * call videoport_get_one_field() to get some data (a videofield *) * read videofield->ust, videofield->msc and videofield->fieldtype * read the data out of videofield->pixels * call videoport_put_one_field() to release the space * VIDEOPORT_DIRECTION_MV: * call videoport_get_one_field() to get some space (a videofield *) * read videofield->ust, videofield->msc and videofield->fieldtype * write data into videofield->pixels * call videoport_put_one_field() to release the data * * videoport_get_one_field() * returns NULL if there's no data or space to get. * returns a videofield * with the pixel pointer, ust, msc, fieldtype. * do not free the videofield * returned. * the UST and MSC are accurate assuming no overflow or underflow. * the field type is always accurate. * first field after videoport_open() will be an F1 field * XXX assumes F1 dominance. see XXX in videoport.c for more info. * field type will always alternate between F1 and F2. * field->pixels contains video data at VL_OFFSET of (0,0): * see "Hints for Vid-to-Mem Applications" * videoport_put_one_field() * pass in the videofield * which you want to put. */ videofield *videoport_get_one_field(videoport me); void videoport_put_one_field(videoport me, videofield *field); /* * sets the fillpoint, in fields. see: * - videoport_setfdset() and * - videoport_wait_for_space_or_data(). */ void videoport_setfillpoint(videoport me, int fillpoint); /* * you must call videoport_setfdset() before the select() in your main loop. * it will fill in the fds that it wants to select on. if it needs to * wake up at a UST which is less than *wakeust, it will modify *wakeust * to that UST. * * you must call videoport_checkfdset() after the select() in your main loop, * so that the videoport can react to new data and space that has become * available. * * a videoport has a fillpoint value in fields (default 1 field). * you can set the fillpoint with videoport_setfillpoint(). * your select will unblock, at the latest, when: * * videoport_available_field_count(p) >= fillpoint * * your select() may unblock earlier due to videoport's internals, * so when you unblock, you must call videoport_available_field_count() * or check the return value of videoport_get_*() to make sure there * is data/space. * * videoport_setfdset() will not cause your select() to hard spin if there * are < fillpoint of fields available. videoport_setfdset() * will cause your select() to hard spin if there >= fillpoint fields * available. */ void videoport_setfdset(videoport me, fd_set *readset, fd_set *writeset, fd_set *exceptset, stamp_t *wakeust); void videoport_checkfdset(videoport me, fd_set *readset, fd_set *writeset, fd_set *exceptset); /* * if you have no select loop and you just want to block until at * least the fillpoint of space or data becomes available, call this. * you can set the fillpoint (default 1 field) with videoport_setfillpoint(). * videoport_wait_for_space_or_data() will call videoport_setfdset(), * select(), and videoport_checkfdset() as many times are are necessary * to get the fillpoint worth of data/space (including zero times). */ void videoport_wait_for_space_or_data(videoport me); /* struct _videoport ---------------------------------------------------- */ /* totally non-encapsulated and proud of it! */ typedef struct _videoport { /* === public interface -- read these freely */ /* these were passed into videoport_open() */ int direction; /* VIDEOPORT_DIRECTION_... */ int buf_fields; /* time spacing of fields */ double ust_per_msc; /* image size */ int xsize, ysize; /* bytes per pix: fixed for now */ int bytes_per_pixel; /* maximum number of valid bytes in a videofield: this includes * bytes_per_pixel*xsize*ysize plus any extra padding the VL includes. */ int max_valid_bytes_per_field; /* === sorta public interface -- you shouldn't read these unless you have * to make some backdoor VL call. */ VLServer svr; VLPath path; VLNode drn, src, mem, vid; VLBuffer buffer; /* === private interface -- you'll get in trouble if you read these * without reading the source code. use the videoport_ functions instead. */ int event_fd; int buffer_fd; int fillpoint; /* in fields */ stamp_t expected_fmsc; int current_field_type; /* NOT the same as returned by videoport_get_*() */ int vl_xfrsize; /* VL transfersize to pass to vlGetNextFree() */ videofield bogusfield; /* used by unvideoport.c */ int maxbuffered; /* * with wonderful VLbuffers, we can only free the oldest valid data (VM) * or make the oldest free space valid (MV). that is: * we can only vlPutFree() the oldest data we vlGetNextValid()ed (VM), and * we can only vlPutValid() the oldest data we vlGetNextFree()d (MV). * the fields which we have "got" but not yet "put" are in the * "user region." uncompressed video disk programs which use * asynchronous I/O read or write slices of the user region simultaneously * on different file descriptors. these I/Os often complete in a different * order than the order in which they were issued. therefore, these * programs want to "put" items out of the user region in a different * order in which they "got" them into the user region. * * the videoport functions sit on top of the VL get/put calls and present * an improved buffering interface whose "put" calls take an argument * saying which field you want to "put." there are two user regions: * a contiguous "lib region" containing the fields which videoport * got from VL but has not yet put to VL, and a possibly non-contiguous * "app region" containing the fields which the app got from videoport * but has not yet put to videoport. * * underneath the covers, videoport uses the following private data * structures to maintain these regions. * * q is a simple ring buffer with guard location. * - q has nlocs locations but holds at most nlocs-1 items. * - so nlocs-1 == me->buf_fields * - [uv]head and tail are always >=0 and = vhead and i < tail) mod nlocs, then * { * q[i] is in the lib region. * if (q[i].info != NULL) * q[i] is in the app region (the app is still using q[i]) * else * app has done a put on q[i] but videoport cannot put it to VL yet * * an invariant maintained everywhere except in the middle of * _videoport_put() is that q[vhead].info != NULL. * } * else * q[i] is not in the lib region, so is total garbage * (might as well be NULL) */ int nlocs; videofield *q; int vhead; int uhead; int tail; } _videoport; /* error handling -------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * you will of course want to substitute better error handling. */ void error_exit(char *format, ...); void perror_exit(char *format, ...); void error(char *format, ...); void perror2(char *format, ...); /* * VNC -- "VL NULL Check". Wraps a VL call that returns NULL * on error. */ #define VNC(call) \ { \ if ( (call) == NULL ) \ error_exit("VL error (%s) for call \"%s\"", \ vlStrError(vlErrno), #call); \ } /* * VC - "VL Check". Wraps a VL call that returns <0 on error. */ #define VC(call) \ { \ if ( (call) < 0 ) \ error_exit("VL error (%s) for call \"%s\"", \ vlStrError(vlErrno), #call); \ } /* ** OC -- "OS Check". Wraps a Unix call that returns negative on error. ** Prints and translates oserror() */ #define OC(call) \ { \ if ( (call) < 0 ) \ perror_exit(#call); \ } /* ** ONC -- "OS Null Check". Wraps a Unix call that returns NULL on error. ** Prints and translates oserror() */ #define ONC(call) \ { \ if ( (call) == NULL ) \ perror_exit(#call); \ } /* ** OSIGC -- "OS SIG_ERR Check". Wraps a Unix call that returns SIG_ERR ** on error. */ #define OSIGC(call) \ { \ if ( (call) == SIG_ERR ) \ perror_exit(#call); \ } /* * VPNC -- "videoport NULL Check". Wraps a videoport call that returns NULL * on error. Prints oserror() numerically. */ #define VPNC(call) \ { \ if ( (call) == NULL ) \ error_exit("videoport error %d for call \"%s\"", \ oserror(), #call); \ } /* ** NC -- "NULL Check". Wraps a statement to check for NULL. No error print. */ #define NC(call) \ { \ if ( (call) == NULL ) \ error_exit("\"%s\"", #call); \ } /* utility ------------------------------------------------------------ */ typedef struct _diskperformance { stamp_t oldestust; off64_t oldestcompleted; stamp_t lastreport; } _diskperformance; typedef _diskperformance *diskperformance; diskperformance diskperformance_open(void); void diskperformance_close(diskperformance me); void diskperformance_datapoint(diskperformance me, off64_t bytescompleted, videoport port);