2023-11-09 03:11:07 -07:00
|
|
|
/*
|
2024-01-01 14:15:26 -07:00
|
|
|
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
2023-11-09 03:11:07 -07:00
|
|
|
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
|
|
warranty. In no event will the authors be held liable for any damages
|
|
|
|
arising from the use of this software.
|
|
|
|
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
|
|
including commercial applications, and to alter it and redistribute it
|
|
|
|
freely.
|
|
|
|
*/
|
|
|
|
#include "SDL3/SDL_main.h"
|
|
|
|
#include "SDL3/SDL.h"
|
|
|
|
#include "SDL3/SDL_test.h"
|
|
|
|
#include "SDL3/SDL_video_capture.h"
|
|
|
|
|
2024-01-23 18:40:51 -07:00
|
|
|
#ifdef SDL_PLATFORM_EMSCRIPTEN
|
2023-11-09 03:11:07 -07:00
|
|
|
#include <emscripten/emscripten.h>
|
|
|
|
#endif
|
|
|
|
|
2024-01-23 18:40:51 -07:00
|
|
|
#include <stdio.h>
|
|
|
|
|
2023-11-09 03:11:07 -07:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
SDL_Window *window = NULL;
|
|
|
|
SDL_Renderer *renderer = NULL;
|
|
|
|
SDL_Event evt;
|
|
|
|
int quit = 0;
|
|
|
|
SDLTest_CommonState *state = NULL;
|
|
|
|
|
|
|
|
SDL_VideoCaptureDevice *device = NULL;
|
|
|
|
SDL_VideoCaptureSpec obtained;
|
|
|
|
|
|
|
|
SDL_VideoCaptureFrame frame_current;
|
|
|
|
SDL_Texture *texture = NULL;
|
|
|
|
int texture_updated = 0;
|
|
|
|
|
|
|
|
SDL_zero(evt);
|
|
|
|
SDL_zero(obtained);
|
|
|
|
SDL_zero(frame_current);
|
|
|
|
|
|
|
|
/* Set 0 to disable TouchEvent to be duplicated as MouseEvent with SDL_TOUCH_MOUSEID */
|
|
|
|
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
|
|
|
|
/* Set 0 to disable MouseEvent to be duplicated as TouchEvent with SDL_MOUSE_TOUCHID */
|
|
|
|
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
|
|
|
|
|
|
|
|
/* Initialize test framework */
|
|
|
|
state = SDLTest_CommonCreateState(argv, 0);
|
|
|
|
if (state == NULL) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable standard application logging */
|
|
|
|
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
|
|
|
|
|
|
|
|
/* Load the SDL library */
|
2023-11-27 11:35:45 -07:00
|
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { /* FIXME: SDL_INIT_JOYSTICK needed for add/removing devices at runtime */
|
2023-11-09 03:11:07 -07:00
|
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
window = SDL_CreateWindow("Local Video", 1000, 800, 0);
|
|
|
|
if (window == NULL) {
|
|
|
|
SDL_Log("Couldn't create window: %s", SDL_GetError());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE);
|
|
|
|
|
|
|
|
renderer = SDL_CreateRenderer(window, NULL, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
|
|
|
if (renderer == NULL) {
|
|
|
|
/* SDL_Log("Couldn't create renderer: %s", SDL_GetError()); */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
device = SDL_OpenVideoCaptureWithSpec(0, NULL, &obtained, SDL_VIDEO_CAPTURE_ALLOW_ANY_CHANGE);
|
|
|
|
if (!device) {
|
|
|
|
SDL_Log("No video capture? %s", SDL_GetError());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SDL_StartVideoCapture(device) < 0) {
|
|
|
|
SDL_Log("error SDL_StartVideoCapture(): %s", SDL_GetError());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create texture with appropriate format */
|
|
|
|
if (texture == NULL) {
|
|
|
|
texture = SDL_CreateTexture(renderer, obtained.format, SDL_TEXTUREACCESS_STATIC, obtained.width, obtained.height);
|
|
|
|
if (texture == NULL) {
|
|
|
|
SDL_Log("Couldn't create texture: %s", SDL_GetError());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!quit) {
|
|
|
|
while (SDL_PollEvent(&evt)) {
|
|
|
|
int sym = 0;
|
|
|
|
switch (evt.type)
|
|
|
|
{
|
|
|
|
case SDL_EVENT_KEY_DOWN:
|
|
|
|
{
|
|
|
|
sym = evt.key.keysym.sym;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SDL_EVENT_QUIT:
|
|
|
|
{
|
|
|
|
quit = 1;
|
|
|
|
SDL_Log("Ctlr+C : Quit!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sym == SDLK_ESCAPE || sym == SDLK_AC_BACK) {
|
|
|
|
quit = 1;
|
|
|
|
SDL_Log("Key : Escape!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
SDL_VideoCaptureFrame frame_next;
|
|
|
|
SDL_zero(frame_next);
|
|
|
|
|
|
|
|
if (SDL_AcquireVideoCaptureFrame(device, &frame_next) < 0) {
|
|
|
|
SDL_Log("err SDL_AcquireVideoCaptureFrame: %s", SDL_GetError());
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
if (frame_next.num_planes) {
|
|
|
|
SDL_Log("frame: %p at %" SDL_PRIu64, (void*)frame_next.data[0], frame_next.timestampNS);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (frame_next.num_planes) {
|
|
|
|
if (frame_current.num_planes) {
|
|
|
|
if (SDL_ReleaseVideoCaptureFrame(device, &frame_current) < 0) {
|
|
|
|
SDL_Log("err SDL_ReleaseVideoCaptureFrame: %s", SDL_GetError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* It's not needed to keep the frame once updated the texture is updated.
|
|
|
|
* But in case of 0-copy, it's needed to have the frame while using the texture.
|
|
|
|
*/
|
|
|
|
frame_current = frame_next;
|
|
|
|
texture_updated = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update SDL_Texture with last video frame (only once per new frame) */
|
|
|
|
if (frame_current.num_planes && texture_updated == 0) {
|
|
|
|
/* Use software data */
|
|
|
|
if (frame_current.num_planes == 1) {
|
|
|
|
SDL_UpdateTexture(texture, NULL,
|
|
|
|
frame_current.data[0], frame_current.pitch[0]);
|
|
|
|
} else if (frame_current.num_planes == 2) {
|
|
|
|
SDL_UpdateNVTexture(texture, NULL,
|
|
|
|
frame_current.data[0], frame_current.pitch[0],
|
|
|
|
frame_current.data[1], frame_current.pitch[1]);
|
|
|
|
} else if (frame_current.num_planes == 3) {
|
|
|
|
SDL_UpdateYUVTexture(texture, NULL, frame_current.data[0], frame_current.pitch[0],
|
|
|
|
frame_current.data[1], frame_current.pitch[1],
|
|
|
|
frame_current.data[2], frame_current.pitch[2]);
|
|
|
|
}
|
|
|
|
texture_updated = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255);
|
|
|
|
SDL_RenderClear(renderer);
|
|
|
|
{
|
|
|
|
int win_w, win_h, tw, th, w;
|
|
|
|
SDL_FRect d;
|
|
|
|
SDL_QueryTexture(texture, NULL, NULL, &tw, &th);
|
|
|
|
SDL_GetRenderOutputSize(renderer, &win_w, &win_h);
|
|
|
|
w = win_w;
|
|
|
|
if (tw > w - 20) {
|
|
|
|
float scale = (float) (w - 20) / (float) tw;
|
|
|
|
tw = w - 20;
|
|
|
|
th = (int)((float) th * scale);
|
|
|
|
}
|
|
|
|
d.x = (float)(10 );
|
|
|
|
d.y = (float)(win_h - th);
|
|
|
|
d.w = (float)tw;
|
|
|
|
d.h = (float)(th - 10);
|
|
|
|
SDL_RenderTexture(renderer, texture, NULL, &d);
|
|
|
|
}
|
|
|
|
SDL_Delay(10);
|
|
|
|
SDL_RenderPresent(renderer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SDL_StopVideoCapture(device) < 0) {
|
|
|
|
SDL_Log("error SDL_StopVideoCapture(): %s", SDL_GetError());
|
|
|
|
}
|
|
|
|
if (frame_current.num_planes) {
|
|
|
|
SDL_ReleaseVideoCaptureFrame(device, &frame_current);
|
|
|
|
}
|
|
|
|
SDL_CloseVideoCapture(device);
|
|
|
|
|
|
|
|
if (texture) {
|
|
|
|
SDL_DestroyTexture(texture);
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_DestroyRenderer(renderer);
|
|
|
|
SDL_DestroyWindow(window);
|
|
|
|
SDL_Quit();
|
|
|
|
SDLTest_CommonDestroyState(state);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|