fxlink: add video capture with frames displayed in an SDL2 window

This commit is contained in:
Lephenixnoir 2021-08-11 01:44:40 +02:00
parent 05d35eb642
commit c81f9cdba4
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
5 changed files with 134 additions and 2 deletions

View file

@ -4,6 +4,7 @@ cmake_minimum_required(VERSION 3.15)
project(fxSDK VERSION 2.5.2 LANGUAGES C)
option(FXLINK_DISABLE_UDISKS2 "Do not build the UDisks2-based features of fxlink")
option(FXLINK_DISABLE_SDL2 "Do not build the SDL2-based features of fxlink")
find_package(PkgConfig REQUIRED)
pkg_check_modules(libpng REQUIRED libpng16 IMPORTED_TARGET)
@ -12,6 +13,9 @@ pkg_check_modules(libusb REQUIRED libusb-1.0 IMPORTED_TARGET)
if(NOT FXLINK_DISABLE_UDISKS2)
pkg_check_modules(udisks2 REQUIRED udisks2 IMPORTED_TARGET)
endif()
if(NOT FXLINK_DISABLE_SDL2)
pkg_check_modules(sdl2 REQUIRED sdl2 IMPORTED_TARGET)
endif()
set(CMAKE_INSTALL_MESSAGE LAZY)
set(SRC "${CMAKE_CURRENT_SOURCE_DIR}")
@ -36,12 +40,15 @@ add_custom_target(fxsdk ALL DEPENDS "${BIN}/fxsdk.sh")
configure_file(fxlink/config.h.in "${BIN}/include/fxlink/config.h")
add_executable(fxlink fxlink/usb.c fxlink/filter.c fxlink/interactive.c
fxlink/main.c fxlink/png.c fxlink/properties.c fxlink/ud2.c fxlink/util.c
fxlink/protocol.c)
fxlink/protocol.c fxlink/sdl2.c)
target_link_libraries(fxlink PkgConfig::libpng PkgConfig::libusb) # PkgConfig::libudev
target_include_directories(fxlink PRIVATE "${BIN}/include/fxlink")
if(NOT FXLINK_DISABLE_UDISKS2)
target_link_libraries(fxlink PkgConfig::udisks2)
endif()
if(NOT FXLINK_DISABLE_SDL2)
target_link_libraries(fxlink PkgConfig::sdl2)
endif()
# Install rules

View file

@ -8,4 +8,7 @@
/* Disables UDisks2 interfaces for systems that don't use it. */
#cmakedefine FXLINK_DISABLE_UDISKS2
/* Disable SDL2 interfaces. */
#cmakedefine FXLINK_DISABLE_SDL2
#endif /* FXLINK_CONFIG_H */

View file

@ -6,6 +6,7 @@
#include "protocol.h"
#include "usb.h"
#include "png.h"
#include "sdl2.h"
#include <libusb.h>
#include <string.h>
@ -88,6 +89,18 @@ static void message_finish(message_t *msg)
printf("------------------\n");
return;
}
if(!strncmp(msg->header.type, "video", 16)) {
usb_fxlink_image_t *img = (void *)msg->output;
uint8_t **row_pointers = fxlink_protocol_decode_image(msg);
#ifndef FXLINK_DISABLE_SDL2
sdl2_stream(row_pointers, img->width, img->height);
#else
warn("SDL2 support disabled, skipping video frame!");
#endif
return;
}
}
/* Default to saving to a blob */
@ -170,9 +183,13 @@ int main_interactive(filter_t *filter, delay_t *delay, libusb_context *context)
while(1)
{
#ifndef FXLINK_DISABLE_SDL2
sdl2_tick();
#endif
int transferred = -1;
rc = libusb_bulk_transfer(dh, 0x81, buffer + buffer_size, 2048,
&transferred, 2000);
&transferred, 500);
if(rc == LIBUSB_ERROR_NO_DEVICE) {
printf("Disconnected, leaving.\n");

82
fxlink/sdl2.c Normal file
View file

@ -0,0 +1,82 @@
#include "config.h"
#ifndef FXLINK_DISABLE_SDL2
#include "sdl2.h"
#include "util.h"
static SDL_Window *window = NULL;
static int init(size_t width, size_t height)
{
if(!SDL_WasInit(SDL_INIT_VIDEO)) {
int rc = SDL_Init(SDL_INIT_VIDEO);
if(rc < 0)
return err("Cannot initialize SDL: %s\n", SDL_GetError());
}
window = SDL_CreateWindow("fxlink", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, width, height, 0);
return 0;
}
__attribute__((destructor))
static void quit(void)
{
if(!window)
return;
SDL_DestroyWindow(window);
window = NULL;
}
/* Generate an RGB888 surface from image data. */
static SDL_Surface *surface_for_image(uint8_t **RGB888_rows, int width,
int height)
{
/* Little endian setup for RGB */
SDL_Surface *s = SDL_CreateRGBSurface(0, width, height, 24,
0x000000ff, 0x0000ff00, 0x0000ff00, 0);
if(!s) {
err("Cannot create surface for image");
return NULL;
}
for(int i = 0; i < height; i++)
memcpy(s->pixels + i * s->pitch, RGB888_rows[i], width * 3);
return s;
}
void sdl2_stream(uint8_t **RGB888_rows, int width, int height)
{
if(!window && init(width, height))
return;
int current_w, current_h;
SDL_GetWindowSize(window, &current_w, &current_h);
if(current_w != width || current_h != height)
SDL_SetWindowSize(window, width, height);
SDL_Surface *src = surface_for_image(RGB888_rows, width, height);
if(!src)
return;
SDL_Surface *dst = SDL_GetWindowSurface(window);
SDL_BlitSurface(src, NULL, dst, NULL);
SDL_FreeSurface(src);
SDL_UpdateWindowSurface(window);
}
void sdl2_tick(void)
{
if(!window)
return;
SDL_Event e;
while(SDL_PollEvent(&e)) {
if(e.type == SDL_QUIT)
quit();
}
}
#endif /* FXLINK_DISABLE_SDL2 */

23
fxlink/sdl2.h Normal file
View file

@ -0,0 +1,23 @@
//---
// fxlink:sdl2 - SDL2 functions
//---
#ifndef FXLINK_SDL2_H
#define FXLINK_SDL2_H
#ifndef FXLINK_DISABLE_SDL2
#include <SDL2/SDL.h>
/* sdl2_stream(): Display a streaming image on the window
This function opens or reuses an SDL2 window to display an image. */
void sdl2_stream(uint8_t **RGB888_rows, int width, int height);
/* sdl2_tick(): Handle SDL events
This just needs to be called regularly from the main thread, to respond to
events on the window. */
void sdl2_tick(void);
#endif /* FXLINK_DISABLE_SDL2 */
#endif /* FXLINK_SDL2_H */