diff --git a/inc/osvr/Common/ImagingComponent.h b/inc/osvr/Common/ImagingComponent.h index caa2cb2ac..393a5600c 100644 --- a/inc/osvr/Common/ImagingComponent.h +++ b/inc/osvr/Common/ImagingComponent.h @@ -32,6 +32,7 @@ #include #include #include +#include // Library/third-party includes #include @@ -54,6 +55,14 @@ namespace common { static const char *identifier(); }; +#ifdef OSVR_COMMON_IN_PROCESS_IMAGING + class ImagePlacedInProcessMemory + : public MessageRegistration { + public: + class MessageSerialization; + static const char *identifier(); + }; +#endif class ImagePlacedInSharedMemory : public MessageRegistration { public: @@ -79,6 +88,12 @@ namespace common { /// shared memory ring buffer. messages::ImagePlacedInSharedMemory imagePlacedInSharedMemory; +#ifdef OSVR_COMMON_IN_PROCESS_IMAGING + /// @brief Message from server to client, notifying of image data in process + /// memory (assumes joint client kit) + messages::ImagePlacedInProcessMemory imagePlacedInProcessMemory; +#endif + OSVR_COMMON_EXPORT void sendImageData( OSVR_ImagingMetadata metadata, OSVR_ImageBufferElement *imageData, OSVR_ChannelCount sensor, OSVR_TimeValue const ×tamp); @@ -103,12 +118,25 @@ namespace common { OSVR_ChannelCount sensor, OSVR_TimeValue const ×tamp); +#ifdef OSVR_COMMON_IN_PROCESS_IMAGING + /// @return true if we could send it. + bool m_sendImageDataViaInProcessMemory(OSVR_ImagingMetadata metadata, + OSVR_ImageBufferElement *imageData, + OSVR_ChannelCount sensor, + OSVR_TimeValue const ×tamp); +#endif + static int VRPN_CALLBACK m_handleImageRegion(void *userdata, vrpn_HANDLERPARAM p); static int VRPN_CALLBACK m_handleImagePlacedInSharedMemory(void *userdata, vrpn_HANDLERPARAM p); +#ifdef OSVR_COMMON_IN_PROCESS_IMAGING + static int VRPN_CALLBACK + m_handleImagePlacedInProcessMemory(void *userdata, vrpn_HANDLERPARAM p); +#endif + void m_checkFirst(OSVR_ImagingMetadata const &metadata); void m_growShmVecIfRequired(OSVR_ChannelCount sensor); diff --git a/src/osvr/Common/CMakeLists.txt b/src/osvr/Common/CMakeLists.txt index a003d26de..eec94bc24 100644 --- a/src/osvr/Common/CMakeLists.txt +++ b/src/osvr/Common/CMakeLists.txt @@ -23,8 +23,13 @@ if(BUILD_WITH_TRACING) endif() endif() +option(OSVR_COMMON_IN_PROCESS_IMAGING "Option to switch from shared-memory imaging messages to use only in-process memory messages. Requires single-process client/server." OFF) + +mark_as_advanced(OSVR_COMMON_IN_PROCESS_IMAGING) + configure_file(TracingConfig.h.cmake_in "${CMAKE_CURRENT_BINARY_DIR}/TracingConfig.h") +configure_file(ImagingComponentConfig.h.cmake_in "${CMAKE_CURRENT_BINARY_DIR}/ImagingComponentConfig.h") set(API "${HEADER_LOCATION}/AddDevice.h" @@ -58,6 +63,7 @@ set(API "${HEADER_LOCATION}/GeneralizedTransform.h" "${HEADER_LOCATION}/GetEnvironmentVariable.h" "${HEADER_LOCATION}/ImagingComponent.h" + "${CMAKE_CURRENT_BINARY_DIR}/ImagingComponentConfig.h" "${HEADER_LOCATION}/IntegerByteSwap.h" "${HEADER_LOCATION}/InterfaceCallbacks.h" "${HEADER_LOCATION}/InterfaceList.h" diff --git a/src/osvr/Common/ImagingComponent.cpp b/src/osvr/Common/ImagingComponent.cpp index 36e3cf069..0aeaee9a1 100644 --- a/src/osvr/Common/ImagingComponent.cpp +++ b/src/osvr/Common/ImagingComponent.cpp @@ -108,6 +108,47 @@ namespace common { return "com.osvr.imaging.imageregion"; } +#ifdef OSVR_COMMON_IN_PROCESS_IMAGING + namespace { + struct InProcessMemoryMessage { + OSVR_ImagingMetadata metadata; + OSVR_ChannelCount sensor; + int buffer; + }; + template + void process(InProcessMemoryMessage &ipmmMsg, T &p) { + process(ipmmMsg.metadata, p); + p(ipmmMsg.sensor); + p(ipmmMsg.buffer); + } + } // namespace + + const char *ImagePlacedInProcessMemory::identifier() { + return "com.osvr.imaging.imageplacedinprocessmemory"; + } + + class ImagePlacedInProcessMemory::MessageSerialization { + public: + MessageSerialization() {} + explicit MessageSerialization(InProcessMemoryMessage &&msg) + : m_msgData(std::move(msg)) {} + +#if defined(_MSC_VER) && defined(_PREFAST_) + // @todo workaround for apparent bug in VS2013 /analyze + explicit MessageSerialization(InProcessMemoryMessage const &msg) + : m_msgData(msg) { } +#endif + template void processMessage(T &p) { + process(m_msgData, p); + } + + InProcessMemoryMessage const &getMessage() { return m_msgData; } + + private: + InProcessMemoryMessage m_msgData; + }; +#endif + namespace { struct SharedMemoryMessage { OSVR_ImagingMetadata metadata; @@ -169,8 +210,14 @@ namespace common { OSVR_TimeValue const ×tamp) { util::Flag dataSent; + +#ifdef OSVR_COMMON_IN_PROCESS_IMAGING + dataSent += + m_sendImageDataViaInProcessMemory(metadata, imageData, sensor, timestamp); +#else dataSent += m_sendImageDataViaSharedMemory(metadata, imageData, sensor, timestamp); +#endif dataSent += m_sendImageDataOnTheWire(metadata, imageData, sensor, timestamp); if (dataSent) { @@ -178,6 +225,27 @@ namespace common { } } +#ifdef OSVR_COMMON_IN_PROCESS_IMAGING + bool ImagingComponent::m_sendImageDataViaInProcessMemory( + OSVR_ImagingMetadata metadata, OSVR_ImageBufferElement *imageData, + OSVR_ChannelCount sensor, OSVR_TimeValue const ×tamp) { + + auto imageBufferSize = getBufferSize(metadata); + auto imageBufferCopy = reinterpret_cast(cv::fastMalloc(imageBufferSize)); + memcpy(imageBufferCopy, imageData, imageBufferSize); + + Buffer<> buf; + messages::ImagePlacedInProcessMemory::MessageSerialization serialization( + messages::InProcessMemoryMessage{ metadata, sensor, reinterpret_cast(imageBufferCopy) }); + + serialize(buf, serialization); + m_getParent().packMessage( + buf, imagePlacedInProcessMemory.getMessageType(), timestamp); + + return true; + } +#endif + bool ImagingComponent::m_sendImageDataViaSharedMemory( OSVR_ImagingMetadata metadata, OSVR_ImageBufferElement *imageData, OSVR_ChannelCount sensor, OSVR_TimeValue const ×tamp) { @@ -259,6 +327,30 @@ namespace common { return 0; } +#ifdef OSVR_COMMON_IN_PROCESS_IMAGING + int VRPN_CALLBACK + ImagingComponent::m_handleImagePlacedInProcessMemory(void *userdata, + vrpn_HANDLERPARAM p) { + auto self = static_cast(userdata); + auto bufReader = readExternalBuffer(p.buffer, p.payload_len); + + messages::ImagePlacedInProcessMemory::MessageSerialization msgSerialize; + deserialize(bufReader, msgSerialize); + auto msg = msgSerialize.getMessage(); + ImageData data; + data.sensor = msg.sensor; + data.metadata = msg.metadata; + data.buffer.reset(reinterpret_cast(msg.buffer), &cv::fastFree); + auto timestamp = util::time::fromStructTimeval(p.msg_time); + + self->m_checkFirst(msg.metadata); + for (auto const &cb : self->m_cb) { + cb(data, timestamp); + } + return 0; + } +#endif + int VRPN_CALLBACK ImagingComponent::m_handleImagePlacedInSharedMemory(void *userdata, vrpn_HANDLERPARAM p) { @@ -317,12 +409,21 @@ namespace common { m_registerHandler( &ImagingComponent::m_handleImagePlacedInSharedMemory, this, imagePlacedInSharedMemory.getMessageType()); + +#ifdef OSVR_COMMON_IN_PROCESS_IMAGING + m_registerHandler( + &ImagingComponent::m_handleImagePlacedInProcessMemory, this, + imagePlacedInProcessMemory.getMessageType()); +#endif } m_cb.push_back(handler); } void ImagingComponent::m_parentSet() { m_getParent().registerMessageType(imageRegion); m_getParent().registerMessageType(imagePlacedInSharedMemory); +#ifdef OSVR_COMMON_IN_PROCESS_IMAGING + m_getParent().registerMessageType(imagePlacedInProcessMemory); +#endif } void ImagingComponent::m_checkFirst(OSVR_ImagingMetadata const &metadata) { diff --git a/src/osvr/Common/ImagingComponentConfig.h.cmake_in b/src/osvr/Common/ImagingComponentConfig.h.cmake_in new file mode 100644 index 000000000..185a957bd --- /dev/null +++ b/src/osvr/Common/ImagingComponentConfig.h.cmake_in @@ -0,0 +1,30 @@ +/** @file + @brief Generated header describing imaging component config + + @date 2015 + + @author + Sensics, Inc. + +*/ + +// Copyright 2015 Sensics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef INCLUDED_ImagingComponentConfig_h_GUID_093B7AF1_DCAB_4307_ACBB_F9DA4282E3BB +#define INCLUDED_ImagingComponentConfig_h_GUID_093B7AF1_DCAB_4307_ACBB_F9DA4282E3BB + +#cmakedefine OSVR_COMMON_IN_PROCESS_IMAGING 1 + +#endif // INCLUDED_ImagingComponentConfig_h_GUID_093B7AF1_DCAB_4307_ACBB_F9DA4282E3BB diff --git a/src/osvr/PluginKit/CMakeLists.txt b/src/osvr/PluginKit/CMakeLists.txt index a9d8dac88..5f5aa8d9a 100644 --- a/src/osvr/PluginKit/CMakeLists.txt +++ b/src/osvr/PluginKit/CMakeLists.txt @@ -85,7 +85,6 @@ add_library(osvr::${LIBNAME_INTERFACE} ALIAS ${LIBNAME_INTERFACE}) ### osvr_add_interface_library(Imaging) - # In an installed version, we search and add the dependency in the # osvrConfigInstalledOpenCV.cmake script since we don't need the same version in # the same place. Thus, here we only add it to the interface if using a build tree. diff --git a/src/osvr/PluginKit/ImagingInterfaceC.cpp b/src/osvr/PluginKit/ImagingInterfaceC.cpp index eca03ad43..8cfac9615 100644 --- a/src/osvr/PluginKit/ImagingInterfaceC.cpp +++ b/src/osvr/PluginKit/ImagingInterfaceC.cpp @@ -39,6 +39,42 @@ // Standard includes // - none +// @todo This is a hack. expect this to be moved to a separate osvrJniBridge +// library and encapsulated behind a proper API. + +#if defined(__ANDROID__) +#include +OSVR_ImageBufferElement *gLastFrame = NULL; +OSVR_ImageBufferElement *gLastFrameBuffer = NULL; +OSVR_ImagingMetadata gLastFrameMetadata; + +extern "C" { + JNIEXPORT void JNICALL Java_com_osvr_android_jni_JNIBridge_reportFrame(JNIEnv * env, jclass clazz, + jbyteArray data, jlong width, jlong height); +} + +JNIEXPORT void JNICALL Java_com_osvr_android_jni_JNIBridge_reportFrame(JNIEnv * env, jclass clazz, + jbyteArray data, jlong width, jlong height) { + + gLastFrameMetadata.height = (OSVR_ImageDimension)height; + gLastFrameMetadata.width = (OSVR_ImageDimension)width; + gLastFrameMetadata.channels = (OSVR_ImageChannels)4; + gLastFrameMetadata.depth = (OSVR_ImageDepth)1; + + // @todo determine whether the current metadata matches the last metadata, + // and if so, reuse the last frame buffer instead of deleting and recreating. + // better yet, use a ring buffer so that image reports aren't lost if update + // isn't called frequently enough. + int size = env->GetArrayLength(data); + + if(gLastFrameBuffer == NULL) { + gLastFrameBuffer = new OSVR_ImageBufferElement[size]; + } + gLastFrame = gLastFrameBuffer; + env->GetByteArrayRegion(data, 0, size, reinterpret_cast(gLastFrameBuffer)); +} +#endif + struct OSVR_ImagingDeviceInterfaceObject : public osvr::connection::DeviceInterfaceBase { osvr::common::ImagingComponent *imaging;