diff --git a/src/mynteye/uvc/utlist_osx.h b/src/mynteye/uvc/utlist_osx.h new file mode 100644 index 0000000..34c725b --- /dev/null +++ b/src/mynteye/uvc/utlist_osx.h @@ -0,0 +1,490 @@ +/* +Copyright (c) 2007-2010, Troy D. Hanson http://uthash.sourceforge.net +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 1.9.1 + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ code), this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define LDECLTYPE(x) char* +#endif +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define _NEXT(elt,list) ((char*)((list)->next)) +#define _NEXTASGN(elt,list,to) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +#define _PREV(elt,list) ((char*)((list)->prev)) +#define _PREVASGN(elt,list,to) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define _SV(elt,list) +#define _NEXT(elt,list) ((elt)->next) +#define _NEXTASGN(elt,list,to) ((elt)->next)=(to) +#define _PREV(elt,list) ((elt)->prev) +#define _PREVASGN(elt,list,to) ((elt)->prev)=(to) +#define _RS(list) +#define _CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +#define DL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev, _ls_tail); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +#define CDL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + LDECLTYPE(list) _tmp2; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); \ + if (_NEXT(_ls_q,list) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = _NEXT(_ls_q,list); \ + } \ + _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev,_ls_tail); \ + _CASTASGN(_tmp2,list); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp2); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ +do { \ + (add)->next = head; \ + head = add; \ +} while (0) + +#define LL_APPEND(head,add) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = head; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_DELETE(head,del) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = ((del)->next); \ + } \ + } \ +} while (0) + +/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ +#define LL_APPEND_VS2008(head,add) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#define LL_DELETE_VS2008(head,del) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while (head->next && (head->next != (del))) { \ + head = head->next; \ + } \ + if (head->next) { \ + head->next = ((del)->next); \ + } \ + { \ + char **_head_alias = (char**)&(head); \ + *_head_alias = _tmp; \ + } \ + } \ +} while (0) +#ifdef NO_DECLTYPE +#undef LL_APPEND +#define LL_APPEND LL_APPEND_VS2008 +#undef LL_DELETE +#define LL_DELETE LL_DELETE_VS2008 +#endif +/* end VS2008 replacements */ + +#define LL_FOREACH(head,el) \ + for(el=head;el;el=el->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ +do { \ + LL_FOREACH(head,out) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define LL_SEARCH(head,out,elt,cmp) \ +do { \ + LL_FOREACH(head,out) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ +do { \ + (add)->next = head; \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0); + +#define DL_DELETE(head,del) \ +do { \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0); + + +#define DL_FOREACH(head,el) \ + for(el=head;el;el=el->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_PREPEND(head,add) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ +(head)=(add); \ +} while (0) + +#define CDL_DELETE(head,del) \ +do { \ + if ( ((head)==(del)) && ((head)->next == (head))) { \ + (head) = 0L; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0); + +#define CDL_FOREACH(head,el) \ + for(el=head;el;el=(el->next==head ? 0L : el->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ + (el) && ((tmp2)=(el)->next, 1); \ + ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ +do { \ + CDL_FOREACH(head,out) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define CDL_SEARCH(head,out,elt,cmp) \ +do { \ + CDL_FOREACH(head,out) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +#endif /* UTLIST_H */ + diff --git a/src/mynteye/uvc/uvc-libuvc.cc b/src/mynteye/uvc/uvc-libuvc.cc index e5e157b..e359476 100644 --- a/src/mynteye/uvc/uvc-libuvc.cc +++ b/src/mynteye/uvc/uvc-libuvc.cc @@ -11,11 +11,16 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#include "mynteye/uvc/uvc.h" - -#include +#include +#include +#include +#include #include "mynteye/logger.h" +#include "mynteye/uvc/uvc.h" +#include "libuvc/libuvc.h" + +#include "mynteye/uvc/uvc_osx_internal.h" // #define ENABLE_DEBUG_SPAM @@ -27,6 +32,8 @@ static void check(const char *call, uvc_error_t status) { LOG_IF(FATAL, status < 0) << call << "(...) returned " << uvc_strerror(status); } + +#define CALL_UVC_WITHOUT_CHECK(name, ...) name(__VA_ARGS__) #define CALL_UVC(name, ...) check(#name, name(__VA_ARGS__)) struct context { @@ -44,13 +51,41 @@ struct context { } }; +/** UVC request code (A.8) */ +enum uvc_req_code { + UVC_RC_UNDEFINED = 0x00, + UVC_SET_CUR = 0x01, + UVC_GET_CUR = 0x81, + UVC_GET_MIN = 0x82, + UVC_GET_MAX = 0x83, + UVC_GET_RES = 0x84, + UVC_GET_LEN = 0x85, + UVC_GET_INFO = 0x86, + UVC_GET_DEF = 0x87, + UVC_REQ_TYPE_GET = 0xa1, + UVC_REQ_TYPE_SET = 0x21 +}; + +struct device; + struct device { const std::shared_ptr parent; uvc_device_t *uvcdevice = nullptr; uvc_device_handle_t *handle = nullptr; - + /** Serial number (null if unavailable) */ + std::string serialNumber = ""; + /** Device-reported manufacturer name (or null) */ + std::string manufacturer = ""; + /** Device-reporter product name (or null) */ + std::string product = ""; + uvc_device_info_t info; + int width, height, format, fps; int vid, pid; + video_channel_callback callback = nullptr; + static std::vector s_devices; + + std::mutex _devices_mutex; device(std::shared_ptr parent, uvc_device_t *uvcdevice) : parent(parent), uvcdevice(uvcdevice) { @@ -59,9 +94,16 @@ struct device { uvc_device_descriptor_t *desc; CALL_UVC(uvc_get_device_descriptor, uvcdevice, &desc); + + serialNumber = std::string(desc->serialNumber); + manufacturer = std::string(desc->manufacturer); + product = std::string(desc->product); + vid = desc->idVendor; pid = desc->idProduct; uvc_free_device_descriptor(desc); + std::lock_guard lock(_devices_mutex); + s_devices.push_back(this); } ~device() { @@ -70,14 +112,83 @@ struct device { uvc_close(handle); if (uvcdevice) uvc_unref_device(uvcdevice); + std::lock_guard lock(_devices_mutex); + for(unsigned long i = 0 ; i < s_devices.size() ; i++) { + if(this == s_devices[i]) { + s_devices.erase(s_devices.begin()+i); + } + } } void open() { if (!handle) CALL_UVC(uvc_open, uvcdevice, &handle); } + + void set_format( + int width, int height, int format, int fps, + video_channel_callback callback) { + this->width = width; + this->height = height; + this->format = format; + this->fps = fps; + this->callback = callback; + } + + static void uvc_frame_callback (struct uvc_frame *frame, void *user_ptr) + { + for(unsigned long i = 0 ; i < s_devices.size() ; i++) { + if(user_ptr == (void*)s_devices[i]) { + printf("bingo\n"); + } + } + } + + // int32_t get_data_usb( uvc_req_code action, int control, int unit) const { + // unsigned char buffer[4]; + + // int status = libusb_control_transfer(handle->usb_devh, + // UVC_REQ_TYPE_GET, + // action, + // control << 8, + // unit << 8 | (1),// _interface + // buffer, + // sizeof(int32_t), 0); + // MYNTEYE_UNUSED(status); + // if (status < 0) throw std::runtime_error( + // to_string() << "libusb_control_transfer(...) returned " + // << libusb_error_name(status)); + + // if (status != sizeof(int32_t)) + // throw std::runtime_error("insufficient data read from USB"); + + // return DW_TO_INT(buffer); + // } + + // void set_data_usb( uvc_req_code action, int control, int unit, int value) const { + // unsigned char buffer[4]; + + // INT_TO_DW(value, buffer); + + // int status = libusb_control_transfer(handle->usb_devh, + // UVC_REQ_TYPE_SET, + // action, + // control << 8, + // unit << 8 | (1),// _interface + // buffer, + // sizeof(int32_t), 0); + + // if (status < 0) throw std::runtime_error( + // to_string() << "libusb_control_transfer(...) returned " + // << libusb_error_name(status)); + + // if (status != sizeof(int32_t)) + // throw std::runtime_error("insufficient data writen to USB"); + // } }; +std::vector device::s_devices; + std::shared_ptr create_context() { return std::make_shared(); } @@ -112,17 +223,48 @@ int get_product_id(const device &device) { } std::string get_name(const device &device) { - // TODO(JohnZhao) - MYNTEYE_UNUSED(device) - return ""; + return device.serialNumber + "/" + device.manufacturer + "/" + device.product; } std::string get_video_name(const device &device) { - // TODO(JohnZhao) - MYNTEYE_UNUSED(device) - return ""; + return device.serialNumber + "/" + device.manufacturer + "/" + device.product; } +// class uvc_device +// { +// public: +// virtual void probe_and_commit(stream_profile profile, frame_callback callback, int buffers = DEFAULT_V4L2_FRAME_BUFFERS) = 0; +// virtual void stream_on(std::function error_handler = [](const notification& n){}) = 0; +// virtual void start_callbacks() = 0; +// virtual void stop_callbacks() = 0; +// virtual void close(stream_profile profile) = 0; + +// virtual void set_power_state(power_state state) = 0; +// virtual power_state get_power_state() const = 0; + +// virtual void init_xu(const extension_unit& xu) = 0; +// virtual bool set_xu(const extension_unit& xu, uint8_t ctrl, const uint8_t* data, int len) = 0; +// virtual bool get_xu(const extension_unit& xu, uint8_t ctrl, uint8_t* data, int len) const = 0; +// virtual control_range get_xu_range(const extension_unit& xu, uint8_t ctrl, int len) const = 0; + +// virtual bool get_pu(rs2_option opt, int32_t& value) const = 0; +// virtual bool set_pu(rs2_option opt, int32_t value) = 0; +// virtual control_range get_pu_range(rs2_option opt) const = 0; + +// virtual std::vector get_profiles() const = 0; + +// virtual void lock() const = 0; +// virtual void unlock() const = 0; + +// virtual std::string get_device_location() const = 0; +// virtual usb_spec get_usb_specification() const = 0; + +// virtual ~uvc_device() = default; + +// protected: +// std::function _error_handler; +// }; + bool pu_control_range( const device &device, Option option, int32_t *min, int32_t *max, int32_t *def) { @@ -132,6 +274,7 @@ bool pu_control_range( MYNTEYE_UNUSED(min) MYNTEYE_UNUSED(max) MYNTEYE_UNUSED(def) + // device.uvcdevice -> set_pu(option, *def); return false; } @@ -173,26 +316,112 @@ bool xu_control_query( } void set_device_mode( - device &device, int width, int height, int fourcc, int fps, // NOLINT + device &device, int width, int height, int format, int fps, // NOLINT video_channel_callback callback) { - // TODO(JohnZhao) - MYNTEYE_UNUSED(device) - MYNTEYE_UNUSED(width) - MYNTEYE_UNUSED(height) - MYNTEYE_UNUSED(fourcc) - MYNTEYE_UNUSED(fps) - MYNTEYE_UNUSED(callback) + device.set_format(width, height, format, fps, callback); } void start_streaming(device &device, int num_transfer_bufs) { // NOLINT - // TODO(JohnZhao) - MYNTEYE_UNUSED(device) - MYNTEYE_UNUSED(num_transfer_bufs) + // MYNTEYE_UNUSED(device) + // MYNTEYE_UNUSED(num_transfer_bufs) + +// typedef struct uvc_stream_ctrl { +// uint16_t bmHint; +// uint8_t bFormatIndex; +// uint8_t bFrameIndex; +// uint32_t dwFrameInterval; +// uint16_t wKeyFrameRate; +// uint16_t wPFrameRate; +// uint16_t wCompQuality; +// uint16_t wCompWindowSize; +// uint16_t wDelay; +// uint32_t dwMaxVideoFrameSize; +// uint32_t dwMaxPayloadTransferSize; +// uint32_t dwClockFrequency; +// uint8_t bmFramingInfo; +// uint8_t bPreferredVersion; +// uint8_t bMinVersion; +// uint8_t bMaxVersion; +// uint8_t bInterfaceNumber; +// } uvc_stream_ctrl_t; + + // uvc_error_t uvc_get_stream_ctrl_format_size( + // uvc_device_handle_t *devh, + // uvc_stream_ctrl_t *ctrl, + // enum uvc_frame_format format, + // int width, int height, + // int fps + // ); + + uvc_stream_ctrl_t ctrl_st = {}; + + /** Color coding of stream, transport-independent + * @ingroup streaming + */ +// enum uvc_frame_format { +// UVC_FRAME_FORMAT_UNKNOWN = 0, +// /** Any supported format */ +// UVC_FRAME_FORMAT_ANY = 0, +// UVC_FRAME_FORMAT_UNCOMPRESSED, +// UVC_FRAME_FORMAT_COMPRESSED, +// /** YUYV/YUV2/YUV422: YUV encoding with one luminance value per pixel and +// * one UV (chrominance) pair for every two pixels. +// */ +// UVC_FRAME_FORMAT_YUYV, +// UVC_FRAME_FORMAT_UYVY, +// /** 24-bit RGB */ +// UVC_FRAME_FORMAT_RGB, +// UVC_FRAME_FORMAT_BGR, +// /** Motion-JPEG (or JPEG) encoded images */ +// UVC_FRAME_FORMAT_MJPEG, +// /** Greyscale images */ +// UVC_FRAME_FORMAT_GRAY8, +// UVC_FRAME_FORMAT_GRAY16, +// /* Raw colour mosaic images */ +// UVC_FRAME_FORMAT_BY8, +// UVC_FRAME_FORMAT_BA81, +// UVC_FRAME_FORMAT_SGRBG8, +// UVC_FRAME_FORMAT_SGBRG8, +// UVC_FRAME_FORMAT_SRGGB8, +// UVC_FRAME_FORMAT_SBGGR8, +// /** Number of formats understood */ +// UVC_FRAME_FORMAT_COUNT, +// }; + CALL_UVC(uvc_get_stream_ctrl_format_size, + device.handle, + &ctrl_st, + UVC_FRAME_FORMAT_ANY, // UVC_FRAME_FORMAT_YUYV, //(enum uvc_frame_format)device.format, + device.width, + device.height, + device.fps); + + /** A callback function to handle incoming assembled UVC frames + * @ingroup streaming + */ +// typedef void(uvc_frame_callback_t)(struct uvc_frame *frame, void *user_ptr); + // uvc_frame_callback_t *cb = nullptr; + + CALL_UVC(uvc_start_streaming, + device.handle, + &ctrl_st, + device::uvc_frame_callback, + &device, + num_transfer_bufs); + + printf("begin\n"); + +// uvc_error_t uvc_start_streaming( +// uvc_device_handle_t *devh, +// uvc_stream_ctrl_t *ctrl, +// uvc_frame_callback_t *cb, +// void *user_ptr, +// uint8_t flags); } void stop_streaming(device &device) { // NOLINT - // TODO(JohnZhao) - MYNTEYE_UNUSED(device) + // MYNTEYE_UNUSED(device) + + CALL_UVC_WITHOUT_CHECK(uvc_stop_streaming, device.handle); } } // namespace uvc diff --git a/src/mynteye/uvc/uvc_osx_internal.h b/src/mynteye/uvc/uvc_osx_internal.h new file mode 100644 index 0000000..2f8ccc9 --- /dev/null +++ b/src/mynteye/uvc/uvc_osx_internal.h @@ -0,0 +1,326 @@ +/** @file libuvc_internal.h + * @brief Implementation-specific UVC constants and structures. + * @cond include_hidden + */ +#ifndef UVC_OSX_INTERNAL_H +#define UVC_OSX_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mynteye/uvc/utlist_osx.h" + +#pragma GCC diagnostic ignored "-Wpedantic" +#include + +/** Converts an unaligned four-byte little-endian integer into an int32 */ +#define DW_TO_INT(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24)) +/** Converts an unaligned two-byte little-endian integer into an int16 */ +#define SW_TO_SHORT(p) ((p)[0] | ((p)[1] << 8)) +/** Converts an unaligned eight-byte little-endian integer into an int64 */ +#define QW_TO_QUAD(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24) | \ + ((p)[4] << 32) |((p)[5] << 40) |((p)[6] << 48) |((p)[7] << 56)) + +/** Converts an int16 into an unaligned two-byte little-endian integer */ +#define SHORT_TO_SW(s, p) \ + (p)[0] = (s); \ + (p)[1] = (s) >> 8; +/** Converts an int32 into an unaligned four-byte little-endian integer */ +#define INT_TO_DW(i, p) \ + (p)[0] = (i); \ + (p)[1] = (i) >> 8; \ + (p)[2] = (i) >> 16; \ + (p)[3] = (i) >> 24; + +/** Converts an int64 into an unaligned eight-byte little-endian integer */ +#define QUAD_TO_QW(i, p) \ + (p)[0] = (i); \ + (p)[1] = (i) >> 8; \ + (p)[2] = (i) >> 16; \ + (p)[3] = (i) >> 24; \ + (p)[4] = (i) >> 32; \ + (p)[5] = (i) >> 40; \ + (p)[6] = (i) >> 48; \ + (p)[7] = (i) >> 56; \ + +/** Selects the nth item in a doubly linked list. n=-1 selects the last item. */ +#define DL_NTH(head, out, n) \ + do { \ + int dl_nth_i = 0; \ + LDECLTYPE(head) dl_nth_p = (head); \ + if ((n) < 0) { \ + while (dl_nth_p && dl_nth_i > (n)) { \ + dl_nth_p = dl_nth_p->prev; \ + dl_nth_i--; \ + } \ + } else { \ + while (dl_nth_p && dl_nth_i < (n)) { \ + dl_nth_p = dl_nth_p->next; \ + dl_nth_i++; \ + } \ + } \ + (out) = dl_nth_p; \ + } while (0); + +#ifdef UVC_DEBUGGING +#include +#define UVC_DEBUG(format, ...) fprintf(stderr, "[%s:%d/%s] " format "\n", basename(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__) +#define UVC_ENTER() fprintf(stderr, "[%s:%d] begin %s\n", basename(__FILE__), __LINE__, __FUNCTION__) +#define UVC_EXIT(code) fprintf(stderr, "[%s:%d] end %s (%d)\n", basename(__FILE__), __LINE__, __FUNCTION__, code) +#define UVC_EXIT_VOID() fprintf(stderr, "[%s:%d] end %s\n", basename(__FILE__), __LINE__, __FUNCTION__) +#else +#define UVC_DEBUG(format, ...) +#define UVC_ENTER() +#define UVC_EXIT_VOID() +#define UVC_EXIT(code) +#endif + +/* http://stackoverflow.com/questions/19452971/array-size-macro-that-rejects-pointers */ +#define IS_INDEXABLE(arg) (sizeof(arg[0])) +#define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg))) +#define ARRAYSIZE(arr) (sizeof(arr) / (IS_ARRAY(arr) ? sizeof(arr[0]) : 0)) + +/** Video interface subclass code (A.2) */ +enum uvc_int_subclass_code { + UVC_SC_UNDEFINED = 0x00, + UVC_SC_VIDEOCONTROL = 0x01, + UVC_SC_VIDEOSTREAMING = 0x02, + UVC_SC_VIDEO_INTERFACE_COLLECTION = 0x03 +}; + +/** Video interface protocol code (A.3) */ +enum uvc_int_proto_code { + UVC_PC_PROTOCOL_UNDEFINED = 0x00 +}; + +/** VideoControl interface descriptor subtype (A.5) */ +enum uvc_vc_desc_subtype { + UVC_VC_DESCRIPTOR_UNDEFINED = 0x00, + UVC_VC_HEADER = 0x01, + UVC_VC_INPUT_TERMINAL = 0x02, + UVC_VC_OUTPUT_TERMINAL = 0x03, + UVC_VC_SELECTOR_UNIT = 0x04, + UVC_VC_PROCESSING_UNIT = 0x05, + UVC_VC_EXTENSION_UNIT = 0x06 +}; + +/** UVC endpoint descriptor subtype (A.7) */ +enum uvc_ep_desc_subtype { + UVC_EP_UNDEFINED = 0x00, + UVC_EP_GENERAL = 0x01, + UVC_EP_ENDPOINT = 0x02, + UVC_EP_INTERRUPT = 0x03 +}; + +/** VideoControl interface control selector (A.9.1) */ +enum uvc_vc_ctrl_selector { + UVC_VC_CONTROL_UNDEFINED = 0x00, + UVC_VC_VIDEO_POWER_MODE_CONTROL = 0x01, + UVC_VC_REQUEST_ERROR_CODE_CONTROL = 0x02 +}; + +/** Terminal control selector (A.9.2) */ +enum uvc_term_ctrl_selector { + UVC_TE_CONTROL_UNDEFINED = 0x00 +}; + +/** Selector unit control selector (A.9.3) */ +enum uvc_su_ctrl_selector { + UVC_SU_CONTROL_UNDEFINED = 0x00, + UVC_SU_INPUT_SELECT_CONTROL = 0x01 +}; + +/** Extension unit control selector (A.9.6) */ +enum uvc_xu_ctrl_selector { + UVC_XU_CONTROL_UNDEFINED = 0x00 +}; + +/** VideoStreaming interface control selector (A.9.7) */ +enum uvc_vs_ctrl_selector { + UVC_VS_CONTROL_UNDEFINED = 0x00, + UVC_VS_PROBE_CONTROL = 0x01, + UVC_VS_COMMIT_CONTROL = 0x02, + UVC_VS_STILL_PROBE_CONTROL = 0x03, + UVC_VS_STILL_COMMIT_CONTROL = 0x04, + UVC_VS_STILL_IMAGE_TRIGGER_CONTROL = 0x05, + UVC_VS_STREAM_ERROR_CODE_CONTROL = 0x06, + UVC_VS_GENERATE_KEY_FRAME_CONTROL = 0x07, + UVC_VS_UPDATE_FRAME_SEGMENT_CONTROL = 0x08, + UVC_VS_SYNC_DELAY_CONTROL = 0x09 +}; + +/** Status packet type (2.4.2.2) */ +enum uvc_status_type { + UVC_STATUS_TYPE_CONTROL = 1, + UVC_STATUS_TYPE_STREAMING = 2 +}; + +/** Payload header flags (2.4.3.3) */ +#define UVC_STREAM_EOH (1 << 7) +#define UVC_STREAM_ERR (1 << 6) +#define UVC_STREAM_STI (1 << 5) +#define UVC_STREAM_RES (1 << 4) +#define UVC_STREAM_SCR (1 << 3) +#define UVC_STREAM_PTS (1 << 2) +#define UVC_STREAM_EOF (1 << 1) +#define UVC_STREAM_FID (1 << 0) + +/** Control capabilities (4.1.2) */ +#define UVC_CONTROL_CAP_GET (1 << 0) +#define UVC_CONTROL_CAP_SET (1 << 1) +#define UVC_CONTROL_CAP_DISABLED (1 << 2) +#define UVC_CONTROL_CAP_AUTOUPDATE (1 << 3) +#define UVC_CONTROL_CAP_ASYNCHRONOUS (1 << 4) + +struct uvc_streaming_interface; +struct uvc_device_info; + +/** VideoStream interface */ +typedef struct uvc_streaming_interface { + struct uvc_device_info *parent; + struct uvc_streaming_interface *prev, *next; + /** Interface number */ + uint8_t bInterfaceNumber; + /** Video formats that this interface provides */ + struct uvc_format_desc *format_descs; + /** USB endpoint to use when communicating with this interface */ + uint8_t bEndpointAddress; + uint8_t bTerminalLink; +} uvc_streaming_interface_t; + +/** VideoControl interface */ +typedef struct uvc_control_interface { + struct uvc_device_info *parent; + struct uvc_input_terminal *input_term_descs; + // struct uvc_output_terminal *output_term_descs; + struct uvc_selector_unit *selector_unit_descs; + struct uvc_processing_unit *processing_unit_descs; + struct uvc_extension_unit *extension_unit_descs; + uint16_t bcdUVC; + uint32_t dwClockFrequency; + uint8_t bEndpointAddress; + /** Interface number */ + uint8_t bInterfaceNumber; +} uvc_control_interface_t; + +struct uvc_stream_ctrl; + +struct uvc_device { + struct uvc_context *ctx; + int ref; + libusb_device *usb_dev; + int interface; +}; + +typedef struct uvc_device_info { + /** Configuration descriptor for USB device */ + struct libusb_config_descriptor *config; + /** VideoControl interface provided by device */ + uvc_control_interface_t ctrl_if; + /** VideoStreaming interfaces on the device */ + uvc_streaming_interface_t *stream_ifs; + /** Store the interface for multiple UVCs on a single VID/PID device (Intel RealSense, VF200, e.g) */ + int camera_number; +} uvc_device_info_t; + +/* + set a high number of transfer buffers. This uses a lot of ram, but + avoids problems with scheduling delays on slow boards causing missed + transfers. A better approach may be to make the transfer thread FIFO + scheduled (if we have root). + We could/should change this to allow reduce it to, say, 5 by default + and then allow the user to change the number of buffers as required. + */ +#define LIBUVC_NUM_TRANSFER_BUFS 1 + +#define LIBUVC_XFER_BUF_SIZE ( 16 * 1024 * 1024 ) + +struct uvc_stream_handle { + struct uvc_device_handle *devh; + struct uvc_stream_handle *prev, *next; + struct uvc_streaming_interface *stream_if; + + /** if true, stream is running (streaming video to host) */ + std::atomic running; + /** Current control block */ + struct uvc_stream_ctrl cur_ctrl; + + /* listeners may only access hold*, and only when holding a + * lock on cb_mutex (probably signaled with cb_cond) */ + uint8_t fid; + uint32_t seq, hold_seq; + uint32_t pts, hold_pts; + uint32_t last_scr, hold_last_scr; + uint8_t *metadata_buf; + size_t metadata_bytes,metadata_size; + size_t got_bytes, hold_bytes; + uint8_t *outbuf, *holdbuf; + std::mutex cb_mutex; + std::condition_variable cb_cond; + std::thread cb_thread; + uint32_t last_polled_seq; + uvc_frame_callback_t *user_cb; + void *user_ptr; + struct libusb_transfer *transfers[LIBUVC_NUM_TRANSFER_BUFS]; + uint8_t *transfer_bufs[LIBUVC_NUM_TRANSFER_BUFS]; + std::condition_variable transfer_cancel[LIBUVC_NUM_TRANSFER_BUFS]; + struct uvc_frame frame; + enum uvc_frame_format frame_format; +}; + +/** Handle on an open UVC device + * + * @todo move most of this into a uvc_device struct? + */ +struct uvc_device_handle { + struct uvc_device *dev; + struct uvc_device_handle *prev, *next; + /** Underlying USB device handle */ + libusb_device_handle *usb_devh; + struct uvc_device_info *info; + struct libusb_transfer *status_xfer; + uint8_t status_buf[32]; + /** Function to call when we receive status updates from the camera */ + uvc_status_callback_t *status_cb; + void *status_user_ptr; + /** Function to call when we receive button events from the camera */ + uvc_button_callback_t *button_cb; + void *button_user_ptr; + + uvc_stream_handle_t *streams; + /** Whether the camera is an iSight that sends one header per frame */ + uint8_t is_isight; +}; + +/** Context within which we communicate with devices */ +struct uvc_context { + /** Underlying context for USB communication */ + struct libusb_context *usb_ctx; + /** True iff libuvc initialized the underlying USB context */ + uint8_t own_usb_ctx; + /** List of open devices in this context */ + uvc_device_handle_t *open_devices; + std::thread handler_thread; + int kill_handler_thread; +}; + +uvc_error_t uvc_query_stream_ctrl( + uvc_device_handle_t *devh, + uvc_stream_ctrl_t *ctrl, + uint8_t probe, + enum uvc_req_code req); + +void uvc_start_handler_thread(uvc_context_t *ctx); +uvc_error_t uvc_claim_if(uvc_device_handle_t *devh, int idx); +uvc_error_t uvc_release_if(uvc_device_handle_t *devh, int idx); + +#endif // !def(UVC_OSX_INTERNAL_H) +/** @endcond */ +