由于Leap Motion已弃用了它们的正式Java绑定,因此我试图使用JNA在Leap Motion's C API (LeapC)和Java之间架起桥梁。但是,我不熟悉C,并且以前没有使用过JNA。到目前为止,我已经设法使某些事情起作用(例如,连接到我的Leap Motion控制器并对其进行轮询以获取状态事件),但是在接收实际的跟踪数据时,我陷入了困境。
LeapC的相关结构在LeapC.h中的定义如下:
/** \ingroup Structs * A snapshot, or frame of data, containing the tracking data for a single moment in time. * The LEAP_FRAME struct is the container for all the tracking data. * @since 3.0.0 */ typedef struct _LEAP_TRACKING_EVENT { LEAP_FRAME_HEADER info; int64_t tracking_frame_id; /** The number of hands tracked in this frame, i.e. the number of elements in * the pHands array. */ uint32_t nHands; /** * A pointer to the array of hands tracked in this frame. */ LEAP_HAND* pHands; /** * Current tracking frame rate in hertz. */ float framerate; } LEAP_TRACKING_EVENT;
我在JNA中实现它的尝试仅部分成功:
info
,tracking_frame_id
和nHands
字段被正确读取并包含预期的信息。pHands
拒绝工作。
首先,我尝试将pHands
视为指向LEAP_HAND数组开始的指针:
@FieldOrder({ "info", "tracking_frame_id", "nHands", "pHands", "framerate" }) public static class LEAP_TRACKING_EVENT extends Structure { public LEAP_FRAME_HEADER info; public long tracking_frame_id; public int nHands; public Pointer pHands; public float framerate; }
[尝试从指针指向的地址中读取任何内容,无论是完整的结构(
new LEAP_HAND(pHands)
)还是仅单个字节(pHands.getByte()
),都会从JNA发出无效的内存访问错误。
基于阅读各种其他StackOverflow问题和答案,看来我应该做这样的事情。
第二,我尝试将pHands
当作一个向上的数组:
@FieldOrder({ "info", "tracking_frame_id", "nHands", "pHands", "framerate" }) public static class LEAP_TRACKING_EVENT extends Structure { public LEAP_FRAME_HEADER info; public long tracking_frame_id; public int nHands; public LEAP_HAND[] pHands = new LEAP_HAND[1]; //Size-limited to 1 here while testing. public float framerate; }
给出了恰好有一只手的情况(如上面的JNA代码所定义),该数组由LEAP_HAND的实例填充,但其中包含垃圾数据(大多数为零)。奇怪的是,当我沿x轴移动手时
pHands[0].palm.position.z
的值的确发生了变化,因此此处似乎与正确的内存部分重叠,但未正确对齐。
我在读取此数组的方式上肯定做错了。有人对我想念的东西有想法吗?
他们这样称呼LeapPollConnection
:result = LeapPollConnection(connectionHandle, timeout, &msg);
。如果msg
包含跟踪事件,则他们将调用handleTrackingEvent(msg.tracking_event);
。该函数定义如下:
//File: ExampleConnection.c static void handleTrackingEvent(const LEAP_TRACKING_EVENT *tracking_event){ if(ConnectionCallbacks.on_frame){ ConnectionCallbacks.on_frame(tracking_event); } }
[
ConnectionCallbacks.on_frame
绑定到以下OnFrame
函数:
//File: CallbackSample.c /** Callback for when a frame of tracking data is available. */ static void OnFrame(const LEAP_TRACKING_EVENT *frame){ printf("Frame %lli with %i hands.\n", (long long int)frame->info.frame_id, frame->nHands); for(uint32_t h = 0; h < frame->nHands; h++){ LEAP_HAND* hand = &frame->pHands[h]; printf(" Hand id %i is a %s hand with position (%f, %f, %f).\n", hand->id, (hand->type == eLeapHandType_Left ? "left" : "right"), hand->palm.position.x, hand->palm.position.y, hand->palm.position.z); } }
编辑2:到目前为止的所有(相关)代码:
// Define an object to receive an event message into.
LEAP_CONNECTION_MESSAGE.ByReference messageRef = new LEAP_CONNECTION_MESSAGE.ByReference();
while (true)
{
// Poll LeapC for an event. A status code for success is returned,
// and messageRef is populated with the event message.
LeapC.INSTANCE.LeapPollConnection(leapConnection.getValue(), 500, messageRef);
// If the event is a tracking event, get the event data.
if (messageRef.type == eLeapEventType.Tracking.getShortValue())
{
LEAP_TRACKING_EVENT event = messageRef.union.tracking_event;
}
// Sleep a moment before polling again.
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
}
}
PollLeapConnection
和LEAP_CONNECTION_MESSAGE
的实现。LeapPollConnection(LeapC API ref)
// Original C signature: // LeapPollConnection(LEAP_CONNECTION hConnection, uint32_t timeout, LEAP_CONNECTION_MESSAGE* evt); public eLeapRS LeapPollConnection(Pointer hConnection, int timeout, LEAP_CONNECTION_MESSAGE.ByReference message);
LEAP_CONNECTION_MESSAGE(LeapC API ref)
// Original C signature: // typedef struct _LEAP_CONNECTION_MESSAGE { // /** // * The size of this message struct. @since 3.0.0 // */ // uint32_t size; // // /** // * The message type. @since 3.0.0 // */ // eLeapEventType type; // // /** // * A pointer to the event data for the current type of message. @since 3.0.0 // */ // union { // /** An untyped pointer. @since 3.0.0 */ // const void* pointer; // /** A connection message. @since 3.0.0 */ // const LEAP_CONNECTION_EVENT* connection_event; // /** A connection lost. @since 3.0.0 */ // const LEAP_CONNECTION_LOST_EVENT* connection_lost_event; // /** A device detected message. @since 3.0.0 */ // const LEAP_DEVICE_EVENT* device_event; // /** A device's status has changed. @since 3.1.3 */ // const LEAP_DEVICE_STATUS_CHANGE_EVENT* device_status_change_event; // /** A policy message. @since 3.0.0 */ // const LEAP_POLICY_EVENT* policy_event; // /** A device failure message. @since 3.0.0 */ // const LEAP_DEVICE_FAILURE_EVENT* device_failure_event; // /** A tracking message. @since 3.0.0 */ // const LEAP_TRACKING_EVENT* tracking_event; // /** A log message. @since 3.0.0 */ // const LEAP_LOG_EVENT* log_event; // /** A log messages. @since 4.0.0 */ // const LEAP_LOG_EVENTS* log_events; // /** A get config value message. @since 3.0.0 */ // const LEAP_CONFIG_RESPONSE_EVENT* config_response_event; // /** A set config value message. @since 3.0.0 */ // const LEAP_CONFIG_CHANGE_EVENT* config_change_event; // const LEAP_DROPPED_FRAME_EVENT* dropped_frame_event; // /** A streaming image message. @since 4.0.0 */ // const LEAP_IMAGE_EVENT* image_event; // /** A point mapping message. @since 4.0.0 */ // const LEAP_POINT_MAPPING_CHANGE_EVENT* point_mapping_change_event; // const LEAP_HEAD_POSE_EVENT* head_pose_event; // }; // } LEAP_CONNECTION_MESSAGE; @FieldOrder({ "size", "type", "union" }) public static class LEAP_CONNECTION_MESSAGE extends Structure { public static class EventUnion extends Union { public Pointer pointer; // Pointer is used for all event types I haven't mapped yet. public Pointer connection_event; public Pointer connection_lost_event; public Pointer device_event; public Pointer device_status_change_event; public Pointer policy_event; public Pointer device_failure_event; public LEAP_TRACKING_EVENT.ByReference tracking_event; public Pointer log_event; public Pointer log_events; public Pointer config_response_event; public Pointer config_change_event; public Pointer dropped_frame_event; public Pointer image_event; public Pointer point_mapping_change_event; public Pointer head_pose_event; } public int size; public short type; public EventUnion union; private eLeapEventType typeE; @Override public void read() { super.read(); // Convert the short in type to an enum constant. typeE = eLeapEventType.None.getForValue(type); if (typeE == null) { typeE = eLeapEventType.None; } switch (typeE) { case ConfigChange : union.setType("config_change_event"); break; case ConfigResponse : union.setType("config_response_event"); break; case Connection : union.setType("connection_event"); break; case ConnectionLost : union.setType("connection_lost_event"); break; case Device : union.setType("device_event"); break; case DeviceFailure : union.setType("device_failure_event"); break; case DeviceLost : union.setType("device_event"); break; case DeviceStatusChange : union.setType("device_status_change_event"); break; case DroppedFrame : union.setType("dropped_frame_event"); break; case HeadPose : union.setType("head_pose_event"); break; case Image : union.setType("image_event"); break; case ImageComplete : break; case ImageRequestError : break; case LogEvent : union.setType("log_event"); break; case LogEvents : union.setType("log_events"); break; case None : union.setType("pointer"); break; case PointMappingChange : union.setType("point_mapping_change_event"); break; case Policy : union.setType("policy_event"); break; case Tracking : union.setType("tracking_event"); break; default : System.out.println("Unknown message type: " + typeE); break; } union.read(); } public static class ByReference extends LEAP_CONNECTION_MESSAGE implements Structure.ByReference { } }
最后,是我当前
LEAP_TRACKING_EVENT
的完整代码>
// Original C signature: See the top of this post. @FieldOrder({ "info", "tracking_frame_id", "nHands", "pHands", "framerate" }) public static class LEAP_TRACKING_EVENT extends Structure { public LEAP_FRAME_HEADER info; public long tracking_frame_id; public int nHands; public Pointer pHands; public float framerate; // Field to store all LEAP_HAND objects pointed to by pHands. private LEAP_HAND[] hands; @Override public void read() { super.read(); // Print frame ID and hand count System.out.println("======================"); System.out.println("ID: " + tracking_frame_id); System.out.println("Hands: " + nHands); if (nHands > 0) { // Attempt to read LEAP_HAND data and print info about hand #0. System.out.println("==="); LEAP_HAND hand = new LEAP_HAND(pHands); hands = (LEAP_HAND[]) hand.toArray(nHands); String log = String.format( "Hand 0| id: %d, type: %d, pos: [%.02f, %.02f, %.02f]%n", hands[0].id, hands[0].type, hands[0].palm.position.union.struct.x, hands[0].palm.position.union.struct.y, hands[0].palm.position.union.struct.z); System.out.println(log); } System.out.println("======================"); } public static class ByReference extends LEAP_TRACKING_EVENT implements Structure.ByReference { } }
[
new LEAP_HAND(pHands)
只是将指针传递给超级构造函数,然后对其本身进行调用read()
(当然,此代码仍在下面链接的要点中可用)。[如果需要,这是我对LEAP_HAND的实现(以及这两个人使用的完整LEAP_TRACKING_EVENT代码和其他结构映射)。
我正在尝试使用JNA在Leap Motion的C API(LeapC)和Java之间架起桥梁,因为Leap Motion已弃用了它们的官方Java绑定。但是,我不熟悉C,并且以前没有使用过JNA。...
首先,欢迎使用StackOverflow!其次,感谢您提供其他详细信息。您缺少一些选择的映射结构的实现细节,但是简化实现比添加更多代码要容易。
super(p)
实现的ByReference
的指针构造函数中缺少LEAP_CONNECTION_MESSAGE
,因此它可能无法以这种方式正确读取所有值。但是,没有必要使用ByReference
实现。当在函数参数中使用JNA时,它会自动使用Structure的指针。删除ByReference
,仅用LEAP_CONNECTION_MESSAGE message = new LEAP_CONNECTION_MESSAGE();
实例化。