GLXextFuncPtr glinject_my_glXGetProcAddressARB(const GLubyte *proc_name) { for(unsigned int i = 0; i < sizeof(hook_table) / sizeof(Hook); ++i) { if(strcmp(hook_table[i].name, (const char*) proc_name) == 0) { std::lock_guard<std::mutex> lock(g_glinject_mutex); GLINJECT_PRINT("Hooked: glXGetProcAddressARB(" << proc_name << ")."); return (GLXextFuncPtr) hook_table[i].address; } } return g_glinject_real_glXGetProcAddressARB(proc_name); }
extern "C" void* dlvsym(void* handle, const char* symbol, const char* version) { InitGLInject(); for(unsigned int i = 0; i < sizeof(hook_table) / sizeof(Hook); ++i) { if(strcmp(hook_table[i].name, symbol) == 0) { std::lock_guard<std::mutex> lock(g_glinject_mutex); GLINJECT_PRINT("Hooked: dlvsym(" << symbol << "," << version << ")."); return hook_table[i].address; } } return g_glinject_real_dlvsym(handle, symbol, version); }
void SSRVideoStreamWriter::UpdateSize(unsigned int width, unsigned int height, int stride) { if(m_width != width || m_height != height) { GLINJECT_PRINT("[" << m_filename_main << "] frame size = " << width << "x" << height << "."); m_width = width; m_height = height; GLInjectHeader *header = GetGLInjectHeader(); header->current_width = m_width; header->current_height = m_height; std::atomic_thread_fence(std::memory_order_release); } m_stride = stride; }
void glinject_my_glXSwapBuffers(Display* dpy, GLXDrawable drawable) { { std::lock_guard<std::mutex> lock(g_glinject_mutex); GLXFrameGrabber *fg = g_glinject->FindGLXFrameGrabber(dpy, drawable); if(fg == NULL) { GLINJECT_PRINT("Warning: glXSwapBuffers called without existing frame grabber, creating one assuming window == drawable."); fg = g_glinject->NewGLXFrameGrabber(dpy, drawable, drawable); } fg->GrabFrame(); } g_glinject_real_glXSwapBuffers(dpy, drawable); }
void SSRVideoStreamWriter::Free() { for(unsigned int i = 0; i < GLINJECT_RING_BUFFER_SIZE; ++i) { FrameData &fd = m_frame_data[i]; // unmap frame file if(fd.m_mmap_ptr_frame != MAP_FAILED) { munmap(fd.m_mmap_ptr_frame, fd.m_mmap_size_frame); fd.m_mmap_ptr_frame = MAP_FAILED; } // close and unlink frame file if(fd.m_fd_frame != -1) { close(fd.m_fd_frame); fd.m_fd_frame = -1; unlink(fd.m_filename_frame.c_str()); } } // unmap main file if(m_mmap_ptr_main != MAP_FAILED) { munmap(m_mmap_ptr_main, m_mmap_size_main); m_mmap_ptr_main = MAP_FAILED; } // close and unlink main file if(m_fd_main != -1) { close(m_fd_main); m_fd_main = -1; unlink(m_filename_main.c_str()); } GLINJECT_PRINT("[" << m_filename_main << "] Destroyed video stream."); }
void SSRVideoStreamWriter::Init() { GLINJECT_PRINT("[" << m_filename_main << "] Created video stream."); bool relax_permissions = false; { char *ssr_stream_relax_permissions = getenv("SSR_STREAM_RELAX_PERMISSIONS"); if(ssr_stream_relax_permissions != NULL && atoi(ssr_stream_relax_permissions) > 0) { GLINJECT_PRINT("Warning: Using relaxed file permissions, any user on this machine will be able to read or manipulate the stream!"); relax_permissions = true; } } // create channel directory (permissions may be wrong because of umask, fix this later) if(mkdir(m_channel_directory.c_str(), (relax_permissions)? 0777 : 0700) == -1) { if(errno != EEXIST) { // does the directory already exist? GLINJECT_PRINT("Error: Can't create channel directory!"); throw SSRStreamException(); } } // check ownership and permissions struct stat statinfo; if(lstat(m_channel_directory.c_str(), &statinfo) == -1) { GLINJECT_PRINT("Error: Can't stat channel directory!"); throw SSRStreamException(); } if(!S_ISDIR(statinfo.st_mode) || S_ISLNK(statinfo.st_mode)) { GLINJECT_PRINT("Error: Channel directory is not a regular directory!"); throw SSRStreamException(); } if(statinfo.st_uid == geteuid()) { if(chmod(m_channel_directory.c_str(), (relax_permissions)? 0777 : 0700) == -1) { GLINJECT_PRINT("Error: Can't set channel directory mode!"); throw SSRStreamException(); } } else { if(!relax_permissions) { GLINJECT_PRINT("Error: Channel directory is owned by a different user! " "Choose a different channel name, or enable relaxed file permissions to use it anyway."); throw SSRStreamException(); } } // open frame files for(unsigned int i = 0; i < GLINJECT_RING_BUFFER_SIZE; ++i) { FrameData &fd = m_frame_data[i]; fd.m_fd_frame = open(fd.m_filename_frame.c_str(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, (relax_permissions)? 0666 : 0600); if(fd.m_fd_frame == -1) { GLINJECT_PRINT("Error: Can't open video frame file!"); throw SSRStreamException(); } if(fchmod(fd.m_fd_frame, (relax_permissions)? 0666 : 0600) == -1) { GLINJECT_PRINT("Error: Can't set video frame file mode!"); throw SSRStreamException(); } } // open main file m_fd_main = open(m_filename_main.c_str(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, (relax_permissions)? 0666 : 0600); if(m_fd_main == -1) { GLINJECT_PRINT("Error: Can't open video stream file!"); throw SSRStreamException(); } if(fchmod(m_fd_main, (relax_permissions)? 0666 : 0600) == -1) { GLINJECT_PRINT("Error: Can't set video stream file mode!"); throw SSRStreamException(); } // resize main file m_mmap_size_main = (sizeof(GLInjectHeader) + GLINJECT_RING_BUFFER_SIZE * sizeof(GLInjectFrameInfo) + m_page_size - 1) / m_page_size * m_page_size; if(ftruncate(m_fd_main, m_mmap_size_main) == -1) { GLINJECT_PRINT("Error: Can't resize video stream file!"); throw SSRStreamException(); } // map main file m_mmap_ptr_main = mmap(NULL, m_mmap_size_main, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd_main, 0); if(m_mmap_ptr_main == MAP_FAILED) { GLINJECT_PRINT("Error: Can't memory-map video stream file!"); throw SSRStreamException(); } // initialize header GLInjectHeader *header = GetGLInjectHeader(); header->identifier = 0; // will be set later header->ring_buffer_read_pos = 0; header->ring_buffer_write_pos = 0; header->current_width = m_width; header->current_height = m_height; header->frame_counter = 0; // initialize frame info for(unsigned int i = 0; i < GLINJECT_RING_BUFFER_SIZE; ++i) { GLInjectFrameInfo *frameinfo = GetGLInjectFrameInfo(i); frameinfo->timestamp = 0; frameinfo->width = 0; frameinfo->height = 0; frameinfo->stride = 0; } // set the identifier to indicate that initialization is complete std::atomic_thread_fence(std::memory_order_release); header->identifier = GLINJECT_IDENTIFIER; std::atomic_thread_fence(std::memory_order_release); }
void* SSRVideoStreamWriter::NewFrame(unsigned int* flags) { // increment the frame counter GLInjectHeader *header = GetGLInjectHeader(); ++header->frame_counter; std::atomic_thread_fence(std::memory_order_release); // get capture parameters std::atomic_thread_fence(std::memory_order_acquire); *flags = header->capture_flags; if(!(*flags & GLINJECT_FLAG_CAPTURE_ENABLED)) return NULL; // check the timestamp and maybe limit the fps unsigned int target_fps = header->capture_target_fps; int64_t timestamp = hrt_time_micro(); if(target_fps > 0) { int64_t interval = 1000000 / target_fps; if(*flags & GLINJECT_FLAG_LIMIT_FPS) { if(timestamp < m_next_frame_time) { usleep(m_next_frame_time - timestamp); timestamp = hrt_time_micro(); } } else { if(timestamp < m_next_frame_time - interval) return NULL; } m_next_frame_time = std::max(m_next_frame_time + interval, timestamp); } // make sure that at least one frame is available unsigned int read_pos = header->ring_buffer_read_pos; unsigned int write_pos = header->ring_buffer_write_pos; unsigned int frames_used = positive_mod((int) write_pos - (int) read_pos, GLINJECT_RING_BUFFER_SIZE * 2); if(frames_used >= GLINJECT_RING_BUFFER_SIZE) return NULL; // write frame info GLInjectFrameInfo *frameinfo = GetGLInjectFrameInfo(write_pos % GLINJECT_RING_BUFFER_SIZE); frameinfo->timestamp = timestamp; frameinfo->width = m_width; frameinfo->height = m_height; frameinfo->stride = m_stride; // prepare the frame file FrameData &fd = m_frame_data[write_pos % GLINJECT_RING_BUFFER_SIZE]; size_t required_size = (size_t) abs(m_stride) * (size_t) m_height; if(required_size > fd.m_mmap_size_frame) { // calculate new size required_size = (required_size + required_size / 4 + m_page_size - 1) / m_page_size * m_page_size; // unmap frame file if(fd.m_mmap_ptr_frame != MAP_FAILED) { munmap(fd.m_mmap_ptr_frame, fd.m_mmap_size_frame); fd.m_mmap_ptr_frame = MAP_FAILED; fd.m_mmap_size_frame = 0; } // resize frame file if(ftruncate(fd.m_fd_frame, required_size) == -1) { GLINJECT_PRINT("Error: Can't resize video frame file!"); throw SSRStreamException(); } // map frame file fd.m_mmap_ptr_frame = mmap(NULL, required_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.m_fd_frame, 0); if(fd.m_mmap_ptr_frame == MAP_FAILED) { GLINJECT_PRINT("Error: Can't memory-map video frame file!"); throw SSRStreamException(); } fd.m_mmap_size_frame = required_size; } return fd.m_mmap_ptr_frame; }
void InitGLInject() { std::lock_guard<std::mutex> lock(g_glinject_mutex); if(g_glinject != NULL) return; // part 1: get dlsym and dlvsym eh_obj_t libdl; if(eh_find_obj(&libdl, "*/libdl.so*")) { GLINJECT_PRINT("Error: Can't open libdl.so!"); exit(1); } if(eh_find_sym(&libdl, "dlsym", (void**) &g_glinject_real_dlsym)) { GLINJECT_PRINT("Error: Can't get dlsym address!"); eh_destroy_obj(&libdl); exit(1); } if(eh_find_sym(&libdl, "dlvsym", (void**) &g_glinject_real_dlvsym)) { GLINJECT_PRINT("Error: Can't get dlvsym address!"); eh_destroy_obj(&libdl); exit(1); } eh_destroy_obj(&libdl); // part 2: get everything else g_glinject_real_glXCreateWindow = (GLXWindow (*)(Display*, GLXFBConfig, Window, const int*)) g_glinject_real_dlsym(RTLD_NEXT, "glXCreateWindow"); if(g_glinject_real_glXCreateWindow == NULL) { GLINJECT_PRINT("Error: Can't get glXCreateWindow address!"); exit(1); } g_glinject_real_glXDestroyWindow = (void (*)(Display*, GLXWindow)) g_glinject_real_dlsym(RTLD_NEXT, "glXDestroyWindow"); if(g_glinject_real_glXDestroyWindow == NULL) { GLINJECT_PRINT("Error: Can't get glXDestroyWindow address!"); exit(1); } g_glinject_real_XDestroyWindow = (int (*)(Display*, Window)) g_glinject_real_dlsym(RTLD_NEXT, "XDestroyWindow"); if(g_glinject_real_XDestroyWindow == NULL) { GLINJECT_PRINT("Error: Can't get XDestroyWindow address!"); exit(1); } g_glinject_real_glXSwapBuffers = (void (*)(Display*, GLXDrawable)) g_glinject_real_dlsym(RTLD_NEXT, "glXSwapBuffers"); if(g_glinject_real_glXSwapBuffers == NULL) { GLINJECT_PRINT("Error: Can't get glXSwapBuffers address!"); exit(1); } g_glinject_real_glXGetProcAddressARB = (GLXextFuncPtr (*)(const GLubyte*)) g_glinject_real_dlsym(RTLD_NEXT, "glXGetProcAddressARB"); if(g_glinject_real_glXGetProcAddressARB == NULL) { GLINJECT_PRINT("Error: Can't get glXGetProcAddressARB address!"); exit(1); } g_glinject_real_XNextEvent = (int (*)(Display*, XEvent*)) g_glinject_real_dlsym(RTLD_NEXT, "XNextEvent"); if(g_glinject_real_XNextEvent == NULL) { GLINJECT_PRINT("Error: Can't get XNextEvent address!"); exit(1); } g_glinject = new GLInject(); atexit(FreeGLInject); }