OK, I'm planning to give this AstroDMx a try since it works out of the box, I still have a couple questions about it though.
On mine (sorry about all the commented out crap):
----------------------------------------------
/*
I found AstroDMx and quit working on this (for now) about 2/1/2023
Maybe this is generating images until the 10 seconds is up, when there's no
error.
After dem3 I start seeding out images of odd sizes and attaching times to good
ones.
Working, but 640x480 (sort of) 16 bit color. I see in the output:
UncompressedFormat(2)
bits per pixel: 16
FrameDescriptor(2)
size: 3264x2448 (right)
There are lots of others, this would do.
.....................
This is a playground, it can be changed.
copied from the web page at
file:///var/www/html/apiref/index.html
Also see
https://libuvc.github.io/libuvc/
*/
#include "libuvc/libuvc.h"
#include <stdio.h>
#include <time.h>
#include <unistd.h> // for sleep
//#include "/usr/programs/c/libuvc/01-23/libuvc/src/ctrl.c"
// res = uvc_start_streaming(devh, &ctrl, cb, 12345, 0); // called later
// 4 params
// doesn't have to exist?
// guess, we're passing device handle, pointer to something, code block, frame
// number, some zero
// skip for now -->
// res = uvc_start_streaming(NULL,NULL,NULL,NUL,NULL);L
// printf("%05d", zipCode); // leading zero example
int icount = 0; // image count
int hres = 3264; // from spec sheet
int vres = 2448;
int fsize = 0;
// segfaults
void idump(void) { // image dump
char fname[40];\
fname[0] = '\0';
snprintf(fname,40,"%05d.dat",icount);
printf("%s\n",fname);
icount++;
}
// size_t write(int fd, const void *buf, size_t count);
void writefile(uvc_frame_t *inp) {
char fname[40];
int fd;
int bytes=0;
// int fsize = inp->width * inp->height * 3;
snprintf(fname,40,"%05d.data",icount);
printf("writefile writing %s\n",fname);
FILE *opf = fopen(fname,"w");
fd = fileno(opf);
bytes = (int) write(fd,inp->data,fsize);
printf("wrote %i bytes\n",bytes);
fclose(opf);
icount++;
};
// useful stuff in: typedef struct uvc_frame {
/*
What I could do is create a struct/record type then malloc space to store a
new one when one comes along. The size seems to be somewhat random, so just
skip frames that are a different size. Use the sequence numbers and whatever
else is useful. But long term storage will be on sd/hard drive anyway.
I could also just do a gettimeofday and not use the times built into
libuvc.
*/
// use rgb instead of bgr? Done, names not changed
// want UVC_FRAME_FORMAT_RGB or UVC_FRAME_FORMAT_UNCOMPRESSED
/* Confusing but there's uvc_frame and uvc_frame_desc, they have some of the
same fields, or almost. Just almost, see fields.txt. There's width/height and
wWidth/wHeight, that's about the closest to exact.
*/
void cb3(uvc_frame_t *frame, void *ptr) { // callback
int fmt; // test;
int bpl;
uvc_frame_t *bgr;
uvc_error_t ret;
// image time is struct timeval,usec resolution, ok
// struct timeval tim;
// idump();
/* We have access, do a couple things , see
file:///var/www/html/apiref/structuvc__frame__desc.html
Well no,
uvc_frame_t' {aka 'struct uvc_frame'} has no member named 'dwMinBitRate'
We really want uvc_still_frame_desc?
I'm also interested in the times in
file:///var/www/html/apiref/structuvc__frame.html for timeofday of frame
capture related to how I offset copies. That's in uvc_frame Struct Reference
*/
// printf("Min bit rate: i\n",frame->dwMinBitRate);
printf("data bytes %i\n",frame->data_bytes); // data bytes 7128576 once
printf("Library owns data: %i\n", frame->library_owns_data); // yes
printf("uvc_frame_format (enum) %i\n", frame->frame_format); // 3
printf("data bytes %i\n",frame->data_bytes); // 15980544 (15 mb)
printf("sequence %u\n",frame->sequence); // frame # (working)
// if (frame->width == hres) {
// break;
// get one of these working!!
// tim = frame->capture_time;
// printf("time: m%s\m",ctime(tim.tv_sec)); // segfaults
printf("time: %s\n",ctime(&frame->capture_time.tv_sec));
// always reads dates in 1969
printf("time_t: %lu\n",frame->capture_time.tv_sec);
// always reads 0 for time_t
printf("time as int %i\n",(int) frame->capture_time.tv_sec); // also 0
/*
Header shows both timespec and timeval structs, misprint?
struct timeval capture_time; and struct timespec capture_time_finished;
*/
/* We'll convert the image from YUV/JPEG to BGR, so allocate space */
bgr = uvc_allocate_frame(frame->width * frame->height * 3);
printf("Image is %i x %i\n",frame->width, frame->height);
if (!bgr) {
printf("unable to allocate bgr frame!");
return;
}
/* Do the BGR conversion */
/*
uvc_error_t uvc_any2bgr(uvc_frame_t *in, uvc_frame_t *out);
*/
printf("buf %i\n",frame->data_bytes); // uuvc buffer size
fsize = frame->data_bytes;
fmt = (int) frame->frame_format;
printf("Data format %i\n",fmt); // it's 3
bpl = frame->step;
printf("Bytes per line %i\n",bpl);
// ret = uvc_any2bgr(frame, bgr);
ret = uvc_any2rgb(frame, bgr); // now RGB, not BGR
if (ret) {
uvc_perror(ret, "uvc_any2bgr error");
uvc_free_frame(bgr);
return;
}
writefile(bgr); // actually rgb of course
uvc_free_frame(bgr);
// } // indent if this works (doesn't)
} // end of cb3
void cb2(uvc_frame_t *frame, void *ptr) { // 2 is old now
int fmt; // test;
int bpl;
uvc_frame_t *bgr;
uvc_error_t ret;
// image time is struct timeval,usec resolution, ok
// struct timeval tim;
// idump();
/* We have access, do a couple things , see
file:///var/www/html/apiref/structuvc__frame__desc.html
Well no,
uvc_frame_t' {aka 'struct uvc_frame'} has no member named 'dwMinBitRate'
We really want uvc_still_frame_desc?
I'm also interested in the times in
file:///var/www/html/apiref/structuvc__frame.html for timeofday of frame
capture related to how I offset copies. That's in uvc_frame Struct Reference
*/
// printf("Min bit rate: i\n",frame->dwMinBitRate);
printf("data bytes %i\n",frame->data_bytes); // data bytes 7128576 once
printf("Library owns data: %i\n", frame->library_owns_data); // yes
printf("uvc_frame_format (enum) %i\n", frame->frame_format); // 3
// printf("data bytes %i\n",frame->data_bytes); // 15980544 (15 mb)
printf("sequence %u\n",frame->sequence); // frame # (working)
// get one of these working!!
// tim = frame->capture_time;
// printf("time: m%s\m",ctime(tim.tv_sec)); // segfaults
printf("time: %s\n",ctime(&frame->capture_time.tv_sec));
// always reads dates in 1969
printf("time_t: %lu\n",frame->capture_time.tv_sec);
// always reads 0 for time_t
printf("time as int %i\n",(int) frame->capture_time.tv_sec); // also 0
/*
Header shows both timespec and timeval structs, misprint?
struct timeval capture_time; and struct timespec capture_time_finished;
*/
/* We'll convert the image from YUV/JPEG to BGR, so allocate space */
bgr = uvc_allocate_frame(frame->width * frame->height * 3);
printf("Image is %i x %i\n",frame->width, frame->height);
if (!bgr) {
printf("unable to allocate bgr frame!");
return;
}
/* Do the BGR conversion */
/*
uvc_error_t uvc_any2bgr(uvc_frame_t *in, uvc_frame_t *out);
*/
printf("buf %i\n",frame->data_bytes); // uuvc buffer size
fsize = frame->data_bytes;
fmt = (int) frame->frame_format;
printf("Data format %i\n",fmt); // it's 3
bpl = frame->step;
printf("Bytes per line %i\n",bpl);
ret = uvc_any2bgr(frame, bgr);
if (ret) {
uvc_perror(ret, "uvc_any2bgr error");
uvc_free_frame(bgr);
return;
}
writefile(bgr);
uvc_free_frame(bgr);
}
int main(int argc, char **argv) {
uvc_context_t *ctx;
uvc_device_t *dev;
uvc_device_handle_t *devh;
uvc_stream_ctrl_t ctrl;
uvc_error_t res;
int rate = 1;
int valid = 0;
/* Initialize a UVC service context. Libuvc will set up its own libusb
* context. Replace NULL with a libusb_context pointer to run libuvc
* from an existing libusb context. */
res = uvc_init(&ctx, NULL);
if (res < 0) {
uvc_perror(res, "uvc_init error in main()");
return res;
}
puts("UVC initialized");
/* Locates the first attached UVC device, stores in dev */
res = uvc_find_device(
ctx, &dev,
0, 0, NULL); /* filter devices: vendor_id, product_id, "serial_num" */
if (res < 0) {
uvc_perror(res, "uvc_find_device failed"); /* no devices found */
} else {
puts("Device found");
/* Try to open the device: requires exclusive access */
res = uvc_open(dev, &devh);
if (res < 0) {
uvc_perror(res, "uvc_open failed"); /* unable to open device */
} else {
puts("Device opened");
/* Print out a message containing all the information that libuvc
* knows about the device */
uvc_print_diag(devh, stderr);
printf("Starting rate loop\n"); // shows ok
while ((rate < 30) && (valid == 0)) {
res = uvc_get_stream_ctrl_format_size(
devh, &ctrl, /* result stored in ctrl */
UVC_FRAME_FORMAT_YUYV,
hres,vres,rate);
printf("stream format returned %i\n",(int) res);
if (res < 0) {
valid = 0;
rate++ ;
} else {
valid = 1;
printf("solution %i\n",rate); // gives 2
}
}
#if (0)
/* Try to negotiate a 640xx480 30 fps YUYV stream profile
What does this actually do? Getting a 2023 version. Look in stream.c,
grep finds a few of them in there. Looking at one:
"Get a negotiated streaming control block for some common parameters."
Going to notes.txt, this looks long.
*/
res = uvc_get_stream_ctrl_format_size(
devh, &ctrl, /* result stored in ctrl */
UVC_FRAME_FORMAT_YUYV, /* YUV 422, aka YUV 4:2:2. try _COMPRESSED */
// UVC_FRAME_FORMAT_RGB, // get_mode failed: Invalid mode (-51)
// UVC_FRAME_FORMAT_BGR, // get_mode failed: Invalid mode (-51)
640, 480, 30 /* width, height, fps */
);
#endif
/* Print out the result */
printf("Start uvc_get_stream_ctrl_format_size results\n");
uvc_print_stream_ctrl(&ctrl, stderr);
printf("End uvc_get_stream_ctrl_format_size results\n");
if (res < 0) {
uvc_perror(res, "get_mode failed"); /* device doesn't provide a matching stream */
} else {
/* Start the video stream. The library will call user function cb:
* cb(frame, (void*) 12345)
*/
// prototype line, no use for all this yet
res = uvc_start_streaming(devh, &ctrl, cb3, 12345, 0);
// skipping for now
res = 0;
if (res < 0) {
// uvc_perror(res, "start_streaming"); /* unable to start stream */
uvc_perror(res,"can't start streaming (res negative)");
} else {
puts("Streaming...");
uvc_set_ae_mode(devh, 1); /* e.g., turn on auto exposure */
sleep(100); /* stream for 10 seconds */
/* End the stream. Blocks until last callback is serviced */
uvc_stop_streaming(devh);
puts("Done streaming.");
}
}
/* Release our handle on the device */
uvc_close(devh);
puts("Device closed");
}
/* Release the device descriptor */
uvc_unref_device(dev);
}
/* Close the UVC context. This closes and cleans up any existing device handles,
* and it closes the libusb context if one was not provided. */
uvc_exit(ctx);
puts("UVC context exited");
return 0;
}
#if (0)
/* This is a version of uvc_get_stream_ctrl_format_size modified to have the
search processes visible.
*/
/** Get a negotiated streaming control block for some common parameters.
* @ingroup streaming
*
* @param[in] devh Device handle
* @param[in,out] ctrl Control block
* @param[in] format_class Type of streaming format
* @param[in] width Desired frame width
* @param[in] height Desired frame height
* @param[in] fps Frame rate, frames per second
*/
uvc_error_t euvc_get_stream_ctrl_format_size( // note e on function name
uvc_device_handle_t *devh,
uvc_stream_ctrl_t *ctrl,
enum uvc_frame_format cf,
int width, int height,
int fps) {
uvc_streaming_interface_t *stream_if;
/* find a matching frame descriptor and interval */
DL_FOREACH(devh->info->stream_ifs, stream_if) {
uvc_format_desc_t *format;
//printf("%s\n",format->
DL_FOREACH(stream_if->format_descs, format) {
uvc_frame_desc_t *frame;
if (!_uvc_frame_format_matches_guid(cf, format->guidFormat))
continue;
DL_FOREACH(format->frame_descs, frame) {
if (frame->wWidth != width || frame->wHeight != height)
continue;
uint32_t *interval;
ctrl->bInterfaceNumber = stream_if->bInterfaceNumber;
UVC_DEBUG("claiming streaming interface %d", stream_if->bInterfaceNumber );
uvc_claim_if(devh, ctrl->bInterfaceNumber);
/* get the max values */
uvc_query_stream_ctrl( devh, ctrl, 1, UVC_GET_MAX);
if (frame->intervals) {
for (interval = frame->intervals; *interval; ++interval) {
// allow a fps rate of zero to mean "accept first rate available"
if (10000000 / *interval == (unsigned int) fps || fps == 0) {
ctrl->bmHint = (1 << 0); /* don't negotiate interval */
ctrl->bFormatIndex = format->bFormatIndex;
ctrl->bFrameIndex = frame->bFrameIndex;
ctrl->dwFrameInterval = *interval;
goto found;
}
}
} else {
uint32_t interval_100ns = 10000000 / fps;
uint32_t interval_offset = interval_100ns - frame->dwMinFrameInterval;
if (interval_100ns >= frame->dwMinFrameInterval
&& interval_100ns <= frame->dwMaxFrameInterval
&& !(interval_offset
&& (interval_offset % frame->dwFrameIntervalStep))) {
ctrl->bmHint = (1 << 0);
ctrl->bFormatIndex = format->bFormatIndex;
ctrl->bFrameIndex = frame->bFrameIndex;
ctrl->dwFrameInterval = interval_100ns;
goto found;
}
}
}
}
}
return UVC_ERROR_INVALID_MODE;
found:
return uvc_probe_stream_ctrl(devh, ctrl);
}
#endif
-----------------------------------------------------------
I had started with a demo program from somewhere and kept tinkering. But the sizes of my output keep varying like
-rw-r--r-- 1 root root 15980544 Feb 2 17:12 00000.data
-rw-r--r-- 1 root root 15980544 Feb 2 17:12 00001.data
-rw-r--r-- 1 root root 15980544 Feb 2 17:12 00002.data
-rw-r--r-- 1 root root 1605480 Feb 2 17:12 00003.data
These are images but just raw 24bpp like you read into Gimp (no compression). I've found what seem to be a few forks of uvc, this is the one I got when I installed Debian's libuvc-doc and followed links within that. Author's name is ktossell but it leads me to
https://libuvc.github.io. Such a mess, so many broken versions floating around.