Compare commits

...

9 Commits

Author SHA1 Message Date
mid
b1a1b9a1eb Architectural updates
1. Introduce image UUIDs, so nodes may check for source updates.
2. Flip over design. Now, CHiPubNodes contain private data, instead of
CHiPubNodes being contained within private data. Each node must cache
its source data if it wants to conditionally run.
2025-10-15 09:45:38 +03:00
mid
cc70d9138a Don't fuckin call SetImage from the not UI thread wtf 2025-10-15 09:42:06 +03:00
mid
28e7fa3365 Fix vpx encoding 2025-10-12 16:57:58 +03:00
mid
8b78f0cdf7 fix preview resizing 2025-10-12 12:19:46 +03:00
mid
542b271c6d fix window_x11 2025-10-12 12:10:28 +03:00
mid
9ad84c447b do unaligned load 2025-10-12 11:50:58 +03:00
mid
3993163d6d Start Windows compatibility
screen_capture_lite turned out to be pretty broken and so I brought back
my old X11 implementation for the Window node, for Unices only.
Hopefully SCL actually works on Windows because lemme tell you, I do not
want to go knee-deep in that.

Additionally, SAIL was replaced with stb_image because I couldn't get
SAIL to build under MinGW.
2025-10-12 11:23:49 +03:00
mid
d77ae15b46 MKV output bugfixes 2025-10-12 11:19:17 +03:00
mid
fb6b0dd492 prepare for trouble 2025-10-12 11:09:06 +03:00
23 changed files with 759 additions and 278 deletions

3
.gitignore vendored
View File

@@ -3,3 +3,6 @@ tests
*.webm *.webm
/cuticle /cuticle
*.so *.so
*.dll
*.exe

View File

@@ -1,6 +1,26 @@
CXXFLAGS := -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -Wno-narrowing -march=native -flto -Wall -fvisibility=hidden -fPIC -msse4 -I./ -I/usr/local/include/sail -L/usr/local/lib `pkg-config --cflags pango opus libv4l2` '-Wl,-rpath,$$ORIGIN' -Wno-multichar CXXFLAGS := -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -Wno-narrowing -march=native -flto -Wall -fvisibility=hidden -fPIC -msse4 -I./ '-Wl,-rpath,$$ORIGIN' -Wno-multichar
LDFLAGS := -lwebm -lpng -lvpx -lsail -lsail-manip `pkg-config --libs pango opus libv4l2` -lportaudio -lXtst -lrtmp -lfdk-aac -leebie -lscreen_capture_lite_shared LDFLAGS := -lwebm -lpng -lvpx -lportaudio -lrtmp -lfdk-aac -leebie -lscreen_capture_lite_shared
ifneq ($(MINGW),0)
CXXFLAGS := `pkg-config --cflags pango opus libv4l2` -I/usr/local/bin -I/usr/local/include/sail $(CXXFLAGS)
LDFLAGS := $(LDFLAGS) -lXtst `pkg-config --libs pango opus libv4l2` -lstdc++
WXCFLAGS := `wx-config --cflags base,adv,core,aui`
WXLDFLAGS := `wx-config --libs base,adv,core,aui`
RELAY_C := relay_x11.c
WINDOW_C := window_x11.cpp
LIBCUTIHI := libcutihi.so
else
CXXFLAGS := `x86_64-w64-mingw32-pkg-config --cflags pangoft2 freetype2 opus` $(CXXFLAGS)
LDFLAGS := $(LDFLAGS) -pthread -lwinmm -lwebm -lole32 -lsetupapi -lws2_32 -lopus -lssp `x86_64-w64-mingw32-pkg-config --libs pangoft2` -lstdc++
WXCFLAGS := `wx-config --host=x86_64-w64-mingw32 --prefix=/usr/x86_64-w64-mingw32/ --cflags base,adv,core,aui`
WXLDFLAGS := `wx-config --host=x86_64-w64-mingw32 --prefix=/usr/x86_64-w64-mingw32/ --libs base,adv,core,aui`
RELAY_C := relay_win.c
WINDOW_C := window_scl.cpp
CC = x86_64-w64-mingw32-gcc
CXX = x86_64-w64-mingw32-g++
LIBCUTIHI := libcutihi.dll
endif
ifneq ($(RELEASE),0) ifneq ($(RELEASE),0)
CXXFLAGS := $(CXXFLAGS) -O0 -gdwarf-2 -DMTR_ENABLED CXXFLAGS := $(CXXFLAGS) -O0 -gdwarf-2 -DMTR_ENABLED
@@ -8,24 +28,29 @@ else
CXXFLAGS := $(CXXFLAGS) -O3 -fopenmp -DMTR_ENABLED CXXFLAGS := $(CXXFLAGS) -O3 -fopenmp -DMTR_ENABLED
endif endif
ifneq ($(SANITIZE),0)
else
CXXFLAGS := -fsanitize=address $(CXXFLAGS)
endif
all: all:
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o node.o hi/node.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o node.o hi/node.c $(LDFLAGS)
$(CXX) $(CXXFLAGS) -std=c++17 -shared -c -o window.o hi/window.cpp $(LDFLAGS) $(CXX) $(CXXFLAGS) -std=c++17 -shared -c -o window.o hi/$(WINDOW_C) $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o microphone.o hi/microphone.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o microphone.o hi/microphone.c $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o mode.o hi/mode.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o mode.o hi/mode.c $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o img.o hi/img.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o img.o hi/img.c $(LDFLAGS)
$(CXX) $(CXXFLAGS) -std=c++17 -shared -c -o webmdec.o hi/webmdec.cpp $(LDFLAGS) $(CXX) $(CXXFLAGS) -std=c++17 -shared -c -o webmdec.o hi/webmdec.cpp $(LDFLAGS)
$(CXX) $(CXXFLAGS) -std=c++17 -shared -c -o webmenc.o hi/webmenc.cpp $(LDFLAGS) # $(CXX) $(CXXFLAGS) -std=c++17 -shared -c -o webmenc.o hi/webmenc.cpp $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o vpxenc.o hi/vpxenc.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o vpxenc.o hi/vpxenc.c $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o opus.o hi/opus.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o opus.o hi/opus.c $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o webcam.o hi/webcam.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o webcam.o hi/webcam.c $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o scale.o hi/relay.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o scale.o hi/$(RELAY_C) $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o minitrace.o hi/minitrace.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o minitrace.o hi/minitrace.c $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o h264enc.o hi/h264enc.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o h264enc.o hi/h264enc.c $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o rtmp.o hi/rtmp.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o rtmp.o hi/rtmp.c $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o aaclc.o hi/aaclc.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o aaclc.o hi/aaclc.c $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o mkv.o hi/mkv.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o mkv.o hi/mkv.c $(LDFLAGS)
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o serialize.o hi/serialize.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o serialize.o hi/serialize.c $(LDFLAGS)
$(CC) $(CXXFLAGS) -shared -o libcutihi.so -shared node.o webmdec.o webmenc.o window.o microphone.o mode.o img.o opus.o webcam.o scale.o minitrace.o h264enc.o rtmp.o aaclc.o vpxenc.o mkv.o serialize.o $(LDFLAGS) $(CC) $(CXXFLAGS) -shared -o $(LIBCUTIHI) node.o webmdec.o window.o microphone.o mode.o img.o opus.o webcam.o scale.o minitrace.o h264enc.o rtmp.o aaclc.o vpxenc.o mkv.o serialize.o $(LDFLAGS)
$(CXX) $(CXXFLAGS) -std=c++11 `wx-config --cflags base,adv,core,aui` -o cuticle ui/main.cpp ui/frame.cpp ui/textctrl.cpp ui/timeline.cpp -L./ -lcutihi $(LDFLAGS) `wx-config --libs base,adv,core,aui` $(CXX) $(CXXFLAGS) $(WXCFLAGS) -std=c++11 -o cuticle ui/main.cpp ui/frame.cpp ui/textctrl.cpp ui/timeline.cpp -L./ -lcutihi $(LDFLAGS) $(WXLDFLAGS)

View File

@@ -5,16 +5,18 @@
#include<assert.h> #include<assert.h>
CUTIVIS CHiImage* CHi_Image_New(uint8_t bpc, uint8_t channels, uint16_t stride, uint16_t width, uint16_t height, void *data) { CUTIVIS CHiImage* CHi_Image_New(uint8_t bpc, uint8_t channels, uint16_t stride, uint16_t width, uint16_t height, void *data) {
CHiImage *img = malloc(sizeof(*img)); CHiImage *img = calloc(1, sizeof(*img));
img->bpc = bpc; img->bpc = bpc;
img->channels = channels; img->channels = channels;
img->stride = stride; img->stride = stride;
img->width = width; img->width = width;
img->height = height; img->height = height;
if(data) img->data16 = data; if(data) img->data16 = data;
else img->data16 = _mm_malloc(bpc * stride * height, 16); else img->data16 = _mm_malloc(bpc * stride * height + 16, 16);
img->owned = !data; img->owned = !data;
img->uuid = CHi_NextUUID();
assert(stride % 16 == 0); assert(stride % 16 == 0);
return img; return img;
@@ -40,3 +42,8 @@ CUTIVIS void CHi_Restride(const void *oldbuf_, void *newbuf_, uint16_t oldStride
memmove(&newbuf[newStride * row], &oldbuf[oldStride * row], oldStride); memmove(&newbuf[newStride * row], &oldbuf[oldStride * row], oldStride);
} }
} }
CUTIVIS size_t CHi_NextUUID() {
static size_t i = 0;
return i++;
}

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include<stddef.h>
#include<stdint.h> #include<stdint.h>
#include"defs.h" #include"defs.h"
@@ -18,6 +19,7 @@ typedef struct CHiImage {
uint8_t *data8; uint8_t *data8;
}; };
uint8_t owned; uint8_t owned;
size_t uuid;
} CHiImage; } CHiImage;
CUTIVIS CHiImage* CHi_Image_New(uint8_t bpc, uint8_t channels, uint16_t stride, uint16_t width, uint16_t height, void *data); CUTIVIS CHiImage* CHi_Image_New(uint8_t bpc, uint8_t channels, uint16_t stride, uint16_t width, uint16_t height, void *data);
@@ -25,6 +27,8 @@ CUTIVIS void CHi_Image_Free(CHiImage *img);
CUTIVIS void CHi_Restride(const void *oldbuf, void *newbuf, uint16_t oldStride, uint16_t newStride, uint16_t rows); CUTIVIS void CHi_Restride(const void *oldbuf, void *newbuf, uint16_t oldStride, uint16_t newStride, uint16_t rows);
CUTIVIS size_t CHi_NextUUID();
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -219,7 +219,7 @@ _PS_CONST(cephes_log_q2, 0.693359375);
*/ */
inline __m128 my_movehl_ps(__m128 a, const __m128 b) { inline __m128 my_movehl_ps(__m128 a, const __m128 b) {
asm ( __asm__ (
"movhlps %2,%0\n\t" "movhlps %2,%0\n\t"
: "=x" (a) : "=x" (a)
: "0" (a), "x"(b) : "0" (a), "x"(b)
@@ -229,7 +229,7 @@ inline __m128 my_movehl_ps(__m128 a, const __m128 b) {
#define _mm_movehl_ps my_movehl_ps #define _mm_movehl_ps my_movehl_ps
inline __m128 my_cmplt_ps(__m128 a, const __m128 b) { inline __m128 my_cmplt_ps(__m128 a, const __m128 b) {
asm ( __asm__ (
"cmpltps %2,%0\n\t" "cmpltps %2,%0\n\t"
: "=x" (a) : "=x" (a)
: "0" (a), "x"(b) : "0" (a), "x"(b)
@@ -237,7 +237,7 @@ inline __m128 my_cmplt_ps(__m128 a, const __m128 b) {
return a; return a;
} }
inline __m128 my_cmpgt_ps(__m128 a, const __m128 b) { inline __m128 my_cmpgt_ps(__m128 a, const __m128 b) {
asm ( __asm__ (
"cmpnleps %2,%0\n\t" "cmpnleps %2,%0\n\t"
: "=x" (a) : "=x" (a)
: "0" (a), "x"(b) : "0" (a), "x"(b)
@@ -245,7 +245,7 @@ inline __m128 my_cmpgt_ps(__m128 a, const __m128 b) {
return a; return a;
} }
inline __m128 my_cmpeq_ps(__m128 a, const __m128 b) { inline __m128 my_cmpeq_ps(__m128 a, const __m128 b) {
asm ( __asm__ (
"cmpeqps %2,%0\n\t" "cmpeqps %2,%0\n\t"
: "=x" (a) : "=x" (a)
: "0" (a), "x"(b) : "0" (a), "x"(b)

View File

@@ -24,8 +24,15 @@
#include<stdio.h> #include<stdio.h>
#include<arpa/inet.h> #ifdef _WIN32
#include<windows.h>
#include<ntsecapi.h>
static int getrandom(void *buf, size_t buflen, unsigned int flags) {
return RtlGenRandom(buf, buflen) ? buflen : -1;
}
#else
#include<sys/random.h> #include<sys/random.h>
#endif
#define NALLENSZ 4 #define NALLENSZ 4
@@ -272,7 +279,7 @@ struct Internal {
size_t videoTrack, audioTrack; size_t videoTrack, audioTrack;
size_t currentClusterTimecode; size_t currentClusterTimecode;
bool beforeFirstCluster;
bool codecPrivatesFound; bool codecPrivatesFound;
CHiBSFrames *audioBacklog; CHiBSFrames *audioBacklog;
@@ -315,12 +322,13 @@ static int muxmkv_perform(CHiPubNode *pubn) {
size_t nextTimestamp = next_timestamp(this); size_t nextTimestamp = next_timestamp(this);
bool shouldUpdateCluster = (this->videoBacklog && this->videoBacklog->count && (this->videoBacklog->data[0].flags & CUTIHI_BS_FLAG_KEY)) || (nextTimestamp - this->currentClusterTimecode > 15000); bool shouldUpdateCluster = this->beforeFirstCluster || (this->videoBacklog && this->videoBacklog->count && (this->videoBacklog->data[0].flags & CUTIHI_BS_FLAG_KEY)) || (nextTimestamp - this->currentClusterTimecode > 15000);
if(shouldUpdateCluster) { if(shouldUpdateCluster) {
if(this->currentClusterTimecode != 0) { if(!this->beforeFirstCluster) {
ebml_writer_pop(&this->wr); ebml_writer_pop(&this->wr);
} }
this->beforeFirstCluster = false;
// Cluster // Cluster
ebml_writer_push(&this->wr, 0x1F43B675); ebml_writer_push(&this->wr, 0x1F43B675);
@@ -336,8 +344,16 @@ static int muxmkv_perform(CHiPubNode *pubn) {
assert((frame->flags & CUTIHI_BS_SETUP_PACKET) == 0); assert((frame->flags & CUTIHI_BS_SETUP_PACKET) == 0);
bool freeAvcc = false;
size_t avccSz = 0; size_t avccSz = 0;
uint8_t *avcc = annexb_to_avcc(frame->ptr, frame->sz, &avccSz); uint8_t *avcc = NULL;
if(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_H264BS) {
avcc = annexb_to_avcc(frame->ptr, frame->sz, &avccSz);
freeAvcc = true;
} else {
avcc = frame->ptr;
avccSz = frame->sz;
}
size_t simpleBlockSize = 4 + avccSz; size_t simpleBlockSize = 4 + avccSz;
@@ -352,7 +368,10 @@ static int muxmkv_perform(CHiPubNode *pubn) {
ebml_writer_put(&this->wr, 0xA3, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = simpleBlockSize, .ptr = simpleBlock}}); ebml_writer_put(&this->wr, 0xA3, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = simpleBlockSize, .ptr = simpleBlock}});
free(simpleBlock); free(simpleBlock);
free(avcc);
if(freeAvcc) {
free(avcc);
}
CHi_BS_Pop(this->videoBacklog, 1); CHi_BS_Pop(this->videoBacklog, 1);
} }
@@ -493,14 +512,14 @@ static int muxmkv_perform(CHiPubNode *pubn) {
assert(this->audioBacklog->data[0].flags & CUTIHI_BS_SETUP_PACKET); assert(this->audioBacklog->data[0].flags & CUTIHI_BS_SETUP_PACKET);
if(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_OPUSBS) { if(CHi_Crawl(&pubn->sinks[1])->type == CUTIHI_VAL_OPUSBS) {
ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "A_OPUS"}); ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "A_OPUS"});
CHiBSFrame *opusHead = &this->audioBacklog->data[0]; CHiBSFrame *opusHead = &this->audioBacklog->data[0];
// CodecPrivate // CodecPrivate
ebml_writer_put(&this->wr, 0x63A2, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = opusHead->sz, .ptr = opusHead->ptr}}); ebml_writer_put(&this->wr, 0x63A2, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = opusHead->sz, .ptr = opusHead->ptr}});
} else if(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_AACBS) { } else if(CHi_Crawl(&pubn->sinks[1])->type == CUTIHI_VAL_AACBS) {
ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "A_AAC/MPEG2/LC"}); ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "A_AAC/MPEG2/LC"});
} }
@@ -547,6 +566,7 @@ static int muxmkv_start(CHiPubNode *pubn) {
this->currentClusterTimecode = 0; this->currentClusterTimecode = 0;
this->codecPrivatesFound = false; this->codecPrivatesFound = false;
this->beforeFirstCluster = true;
int trackNum = 1; int trackNum = 1;

150
hi/node.c
View File

@@ -2,8 +2,6 @@
#include<stdlib.h> #include<stdlib.h>
#include"img.h" #include"img.h"
#include<sail/sail.h>
#include<sail-manip/sail-manip.h>
#include<assert.h> #include<assert.h>
#include<string.h> #include<string.h>
#include<tmmintrin.h> #include<tmmintrin.h>
@@ -21,6 +19,9 @@
#include"node_internal.h" #include"node_internal.h"
#define STB_IMAGE_IMPLEMENTATION
#include<stb_image.h>
CUTIVIS CHiNodeGraph *CHi_NewNodeGraph() { CUTIVIS CHiNodeGraph *CHi_NewNodeGraph() {
static int inited = 0; static int inited = 0;
if(!inited) { if(!inited) {
@@ -521,14 +522,14 @@ typedef struct {
CHiPubNode pubn; CHiPubNode pubn;
char *cachePath; char *cachePath;
CHiImage *cacheImg; CHiImage *cacheImg;
} ImageNode; } ImageImpl;
static int image_perform(CHiPubNode *node) { static int image_perform(CHiPubNode *node) {
ImageNode *internal = (ImageNode*) node; ImageImpl *impl = node->impl;
node->sources->type = CUTIHI_VAL_SAMPLE; node->sources->type = CUTIHI_VAL_SAMPLE;
const char *fn = node->sinks[CUTIHI_IMAGE_IN_FILE].data.text; const char *fn = node->sinks[CUTIHI_IMAGE_IN_FILE].data.text;
if(fn && (!internal->cachePath || strcmp(internal->cachePath, fn))) { if(fn && (!impl->cachePath || strcmp(impl->cachePath, fn))) {
if(node->sinks[CUTIHI_IMAGE_IN_FILE].type == CUTIHI_VAL_NONE) { if(node->sinks[CUTIHI_IMAGE_IN_FILE].type == CUTIHI_VAL_NONE) {
return 1; return 1;
} }
@@ -538,45 +539,39 @@ static int image_perform(CHiPubNode *node) {
return 1; return 1;
} }
if(internal->cacheImg) { if(impl->cacheImg) {
CHi_Image_Free(internal->cacheImg); CHi_Image_Free(impl->cacheImg);
internal->cacheImg = NULL; impl->cacheImg = NULL;
} }
} }
if(!internal->cacheImg) { if(!impl->cacheImg) {
struct sail_image *simg; size_t w = 0, h = 0, n = 4;
if(sail_load_from_file(fn, &simg) != SAIL_OK) { float *data = stbi_loadf(fn, &w, &h, &n, 4);
if(!data) {
CHi_AddError(node, "invalid file", 0); CHi_AddError(node, "invalid file", 0);
return 1; return 1;
} }
struct sail_image *cimg; CHiImage *img = CHi_Image_New(2, 4, (w * 8 + 15) & ~15, w, h, NULL);
sail_convert_image(simg, SAIL_PIXEL_FORMAT_BPP64_BGRA, &cimg); impl->cacheImg = img;
sail_destroy_image(simg);
simg = NULL;
CHiImage *img = CHi_Image_New(2, 4, (cimg->bytes_per_line + 15) & ~15, cimg->width, cimg->height, NULL);
CHi_Restride(cimg->pixels, img->data16, cimg->bytes_per_line, img->stride, img->height);
internal->cacheImg = img;
for(size_t y = 0; y < img->height; y++) { for(size_t y = 0; y < img->height; y++) {
for(size_t x = 0; x < img->stride; x += 16) { for(size_t x = 0; x < img->width; x++) {
__m128i pixels = _mm_load_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x)); __m128 pixels = _mm_loadu_ps((__m128*) &data[w * y + x]);
pixels = apply_gamma_epi16(pixels, _mm_set_ps(1.0f, 2.2f, 2.2f, 2.2f)); pixels = apply_gamma_ps(pixels, _mm_set_ps(1.0f, 2.2f, 2.2f, 2.2f));
_mm_stream_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x), pixels); __m128i pixelsi = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(pixels, _mm_set1_ps(0.0f)), _mm_set1_ps(1.0f)), _mm_set1_ps(65535.0f)));
pixelsi = _mm_shuffle_epi8(pixelsi, _mm_set_epi8(0, 1, 4, 5, 8, 9, 12, 13, -128, -128, -128, -128, -128, -128, -128, -128));
_mm_stream_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x), pixelsi);
} }
} }
sail_destroy_image(cimg); free(impl->cachePath);
impl->cachePath = strdup(fn);
free(internal->cachePath);
internal->cachePath = strdup(fn);
} }
if(CHi_Node_Active(node)) { if(CHi_Node_Active(node)) {
node->sources->data.sample = internal->cacheImg; node->sources->data.sample = impl->cacheImg;
} else { } else {
node->sources->data.sample = NULL; node->sources->data.sample = NULL;
} }
@@ -584,15 +579,10 @@ static int image_perform(CHiPubNode *node) {
return 1; return 1;
} }
CUTIVIS CHiPubNode *CHi_Image() { CUTIVIS CHiPubNode *CHi_Image() {
CHiPubNode *n = calloc(1, sizeof(ImageNode)); CHiPubNode *n = CHi_AllocNode(CUTIHI_T('CIma','ge '), 1, 1, sizeof(ImageImpl));
n->type = CUTIHI_T('CIma','ge ');
n->Start = n->Stop = NULL; n->Start = n->Stop = NULL;
n->Perform = image_perform; n->Perform = image_perform;
n->sinkCount = 1; ((ImageImpl*) n->impl)->cachePath = strdup("");
n->sinks = calloc(sizeof(*n->sinks), 1);
n->sourceCount = 1;
n->sources = calloc(sizeof(*n->sources), 1);
((ImageNode*) n)->cachePath = strdup("");
return n; return n;
} }
@@ -643,32 +633,40 @@ static int embed_perform(CHiPubNode *node) {
return 1; return 1;
} }
CUTIVIS CHiPubNode *CHi_Embed() { CUTIVIS CHiPubNode *CHi_Embed() {
CHiPubNode *n = calloc(1, sizeof(*n)); CHiPubNode *n = CHi_AllocNode(CUTIHI_T('CEmb','ed '), 1 + 3 * CUTIHI_EMBED_MAX_SMALLS, 1, 0);
n->type = CUTIHI_T('CEmb','ed ');
n->Start = n->Stop = NULL; n->Start = n->Stop = NULL;
n->Perform = embed_perform; n->Perform = embed_perform;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount = 1 + 3 * CUTIHI_EMBED_MAX_SMALLS);
n->sources = calloc(sizeof(*n->sources), n->sourceCount = 1);
return n; return n;
} }
struct ConstantSampleImpl {
size_t cacheW;
size_t cacheH;
float cacheCol[4];
};
static int constantsample_perform(CHiPubNode *node) { static int constantsample_perform(CHiPubNode *node) {
struct ConstantSampleImpl *impl = node->impl;
node->sources[0].type = CUTIHI_VAL_SAMPLE; node->sources[0].type = CUTIHI_VAL_SAMPLE;
if(node->sources->data.sample) CHi_Image_Free(node->sources->data.sample); if(node->sources->data.sample) CHi_Image_Free(node->sources->data.sample);
CHiValue *sink = CHi_Crawl(&node->sinks[0]); CHiValue *color = CHi_Crawl(&node->sinks[0]);
CHiValue *sz = CHi_Crawl(&node->sinks[1]); CHiValue *sz = CHi_Crawl(&node->sinks[1]);
size_t w = sz->data.vec4[0] < 1 ? 1 : sz->data.vec4[0]; size_t w = sz->data.vec4[0] < 1 ? 1 : sz->data.vec4[0];
size_t h = sz->data.vec4[1] < 1 ? 1 : sz->data.vec4[1]; size_t h = sz->data.vec4[1] < 1 ? 1 : sz->data.vec4[1];
impl->cacheW = w;
impl->cacheH = h;
memcpy(impl->cacheCol, color->data.vec4, sizeof(impl->cacheCol));
CHiImage *img = CHi_Image_New(2, 4, 8 * ((w + 1) & ~1), w, h, NULL); CHiImage *img = CHi_Image_New(2, 4, 8 * ((w + 1) & ~1), w, h, NULL);
if(CHi_Node_Active(node)) { if(CHi_Node_Active(node)) {
for(size_t y = 0; y < h; y++) { for(size_t y = 0; y < h; y++) {
for(size_t x = 0; x < w; x++) { for(size_t x = 0; x < w; x++) {
img->data16[y * img->stride / 2 + x * 4 + 0] = sink->data.vec4[2] * 65535; img->data16[y * img->stride / 2 + x * 4 + 0] = color->data.vec4[2] * 65535;
img->data16[y * img->stride / 2 + x * 4 + 1] = sink->data.vec4[1] * 65535; img->data16[y * img->stride / 2 + x * 4 + 1] = color->data.vec4[1] * 65535;
img->data16[y * img->stride / 2 + x * 4 + 2] = sink->data.vec4[0] * 65535; img->data16[y * img->stride / 2 + x * 4 + 2] = color->data.vec4[0] * 65535;
img->data16[y * img->stride / 2 + x * 4 + 3] = 65535; img->data16[y * img->stride / 2 + x * 4 + 3] = 65535;
} }
} }
@@ -678,18 +676,13 @@ static int constantsample_perform(CHiPubNode *node) {
return 1; return 1;
} }
CUTIVIS CHiPubNode *CHi_ConstantSample() { CUTIVIS CHiPubNode *CHi_ConstantSample() {
CHiPubNode *n = calloc(1, sizeof(*n)); CHiPubNode *n = CHi_AllocNode(CUTIHI_T('CCns','tCol'), 2, 1, sizeof(struct ConstantSampleImpl));
n->type = CUTIHI_T('CCns','tCol');
n->Start = n->Stop = NULL; n->Start = n->Stop = NULL;
n->Perform = constantsample_perform; n->Perform = constantsample_perform;
n->sinkCount = 2;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount);
n->sourceCount = 1;
n->sources = calloc(sizeof(*n->sources), n->sourceCount);
n->sinks[0].type = CUTIHI_VAL_VEC4; n->sinks[1].type = CUTIHI_VAL_VEC4;
n->sinks[0].data.vec4[0] = 1280; n->sinks[1].data.vec4[0] = 1280;
n->sinks[0].data.vec4[1] = 720; n->sinks[1].data.vec4[1] = 720;
return n; return n;
} }
@@ -853,14 +846,9 @@ static int modulate_perform(CHiPubNode *node) {
return 1; return 1;
} }
CUTIVIS CHiPubNode *CHi_Modulate() { CUTIVIS CHiPubNode *CHi_Modulate() {
CHiPubNode *n = calloc(1, sizeof(*n)); CHiPubNode *n = CHi_AllocNode(CUTIHI_T('CMod','ulat'), 4, 1, 0);
n->type = CUTIHI_T('CMod','ulat');
n->Start = n->Stop = NULL; n->Start = n->Stop = NULL;
n->Perform = modulate_perform; n->Perform = modulate_perform;
n->sinkCount = 4;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount);
n->sourceCount = 1;
n->sources = calloc(sizeof(*n->sources), n->sourceCount);
n->sinks[0].type = CUTIHI_VAL_VEC4; n->sinks[0].type = CUTIHI_VAL_VEC4;
n->sinks[0].data.vec4[0] = 1; n->sinks[0].data.vec4[0] = 1;
@@ -890,14 +878,9 @@ CUTIVIS float CHi_Time_GetDelta(CHiNodeGraph *ng) {
return ng->timedelta; return ng->timedelta;
} }
CUTIVIS CHiPubNode *CHi_Time() { CUTIVIS CHiPubNode *CHi_Time() {
CHiPubNode *n = calloc(1, sizeof(*n)); CHiPubNode *n = CHi_AllocNode(CUTIHI_T('CTim','e '), 0, 1, 0);
n->type = CUTIHI_T('CTim','e ');
n->Start = n->Stop = NULL; n->Start = n->Stop = NULL;
n->Perform = time_perform; n->Perform = time_perform;
n->sinkCount = 0;
n->sinks = NULL;
n->sourceCount = 1;
n->sources = calloc(sizeof(*n->sources), 1);
return n; return n;
} }
@@ -1090,7 +1073,13 @@ CUTIVIS CHiPubNode *CHi_Preview() {
return n; return n;
} }
struct ChromaKeyImpl {
uint64_t cacheImg;
float cacheCol[4];
};
static int chromakey_perform(CHiPubNode *n) { static int chromakey_perform(CHiPubNode *n) {
struct ChromaKeyImpl *impl = n->impl;
CHiValue *sampleV = CHi_Crawl(&n->sinks[0]); CHiValue *sampleV = CHi_Crawl(&n->sinks[0]);
CHiValue *colorV = CHi_Crawl(&n->sinks[1]); CHiValue *colorV = CHi_Crawl(&n->sinks[1]);
@@ -1104,6 +1093,9 @@ static int chromakey_perform(CHiPubNode *n) {
CHi_Image_Free(n->sources[0].data.sample); CHi_Image_Free(n->sources[0].data.sample);
} }
impl->cacheImg = src->uuid;
memcpy(impl->cacheCol, colorV->data.vec4, sizeof(impl->cacheCol));
n->sources[0].type = CUTIHI_VAL_SAMPLE; n->sources[0].type = CUTIHI_VAL_SAMPLE;
CHiImage *dst = n->sources[0].data.sample = CHi_Image_New(2, 4, (src->width * src->bpc * src->channels + 15) & ~15, src->width, src->height, NULL); CHiImage *dst = n->sources[0].data.sample = CHi_Image_New(2, 4, (src->width * src->bpc * src->channels + 15) & ~15, src->width, src->height, NULL);
@@ -1195,14 +1187,12 @@ static int chromakey_perform(CHiPubNode *n) {
return 1; return 1;
} }
CUTIVIS CHiPubNode *CHi_ChromaKey() { CUTIVIS CHiPubNode *CHi_ChromaKey() {
CHiPubNode *n = calloc(1, sizeof(*n)); CHiPubNode *n = CHi_AllocNode(CUTIHI_T('CChr','omaK'), 2, 1, sizeof(struct ChromaKeyImpl));
n->type = CUTIHI_T('CChr','omaK');
n->Start = n->Stop = NULL; n->Start = n->Stop = NULL;
n->Perform = chromakey_perform; n->Perform = chromakey_perform;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount = 2);
n->sources = calloc(sizeof(*n->sources), n->sourceCount = 1);
n->sinks[1].type = CUTIHI_VAL_VEC4; // Default green // Default green
n->sinks[1].type = CUTIHI_VAL_VEC4;
n->sinks[1].data.vec4[0] = 0; n->sinks[1].data.vec4[0] = 0;
n->sinks[1].data.vec4[1] = 1; n->sinks[1].data.vec4[1] = 1;
n->sinks[1].data.vec4[2] = 0; n->sinks[1].data.vec4[2] = 0;
@@ -1263,3 +1253,23 @@ CUTIVIS void CHi_AddError(CHiPubNode *node, const char *err, size_t sink) {
} }
} }
} }
CUTIVIS CHiPubNode *CHi_AllocNode(uint64_t type, size_t sinkCount, size_t sourceCount, size_t implSize) {
CHiPubNode *n = calloc(1, sizeof(*n) + implSize);
if(!n) {
return NULL;
}
n->type = type;
n->sinks = calloc(n->sinkCount = sinkCount, sizeof(*n->sinks));
if(!n->sinks) {
free(n);
return NULL;
}
n->sources = calloc(n->sourceCount = sourceCount, sizeof(*n->sources));
if(!n->sources) {
free(n->sinks);
free(n);
return NULL;
}
return n;
}

View File

@@ -84,8 +84,6 @@ typedef struct CHiPubNode {
uint32_t flags; uint32_t flags;
#define CUTIHI_PERFORM_UPDATED 1
#define CUTIHI_PERFORM_NOTUPDATED 0
int (*Perform)(struct CHiPubNode *node); int (*Perform)(struct CHiPubNode *node);
int (*Start)(struct CHiPubNode *node); int (*Start)(struct CHiPubNode *node);
int (*Stop)(struct CHiPubNode *node); int (*Stop)(struct CHiPubNode *node);
@@ -110,6 +108,8 @@ typedef struct CHiPubNode {
} lifespan; } lifespan;
char _dfsmark; char _dfsmark;
uint8_t impl[];
} CHiPubNode; } CHiPubNode;
typedef enum { typedef enum {
@@ -293,6 +293,8 @@ CUTIVIS void CHi_Node_Destroy(CHiPubNode*);
CUTIVIS void CHi_AddError(CHiPubNode*, const char *err, size_t sink); CUTIVIS void CHi_AddError(CHiPubNode*, const char *err, size_t sink);
CUTIVIS CHiPubNode *CHi_AllocNode(uint64_t type, size_t sinkCount, size_t sourceCount, size_t implSize);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -33,7 +33,7 @@ static int encodeopus_perform(CHiPubNode *pubn) {
n->pcmSamples += newpcm->width; n->pcmSamples += newpcm->width;
} }
CHiBSFrames *frames = malloc(sizeof(*frames)); CHiBSFrames *frames = calloc(1, sizeof(*frames));
frames->count = 0; frames->count = 0;
if(!n->firstFrame) { if(!n->firstFrame) {

149
hi/relay_win.c Normal file
View File

@@ -0,0 +1,149 @@
#include"relay.h"
#include"img.h"
#include<tmmintrin.h>
#include<stdio.h>
#include<pthread.h>
#include<stdatomic.h>
#include<math.h>
#include<windows.h>
#include<winuser.h>
#include<pthread.h>
static int scale_perform(CHiPubNode *n) {
float *scales = CHi_Crawl(&n->sinks[0])->data.vec4;
CHiImage *img = CHi_Crawl(&n->sinks[1])->data.sample;
if(n->sources[0].data.sample) {
CHi_Image_Free(n->sources[0].data.sample);
}
CHiImage *ret = n->sources[0].data.sample = CHi_Image_New(img->bpc, img->channels, img->stride, img->width, img->height, NULL);
__m128i iscales = _mm_set_epi16(
scales[3] * 65535, scales[0] * 65535, scales[1] * 65535, scales[2] * 65535,
scales[3] * 65535, scales[0] * 65535, scales[1] * 65535, scales[2] * 65535
);
for(size_t y = 0; y < img->height; y++) {
for(size_t x = 0; x < img->width; x += 16) {
__m128i pixels8 = _mm_loadu_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x));
__m128i mulled = _mm_mulhi_epu16(pixels8, iscales);
_mm_storeu_si128((__m128i*) ((uintptr_t) ret->data16 + y * img->stride + x), mulled);
}
}
return 1;
}
CUTIVIS CHiPubNode *CHi_ComponentScale() {
CHiPubNode *n = calloc(1, sizeof(*n));
n->type = CUTIHI_T('CCmp','nScl');
n->Start = n->Stop = NULL;
n->Perform = scale_perform;
n->sinkCount = 2;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount);
n->sourceCount = 1;
n->sources = calloc(sizeof(*n->sources), n->sourceCount);
return n;
}
typedef struct {
CHiPubNode pub;
pthread_t thrd;
char key[64];
atomic_bool on;
atomic_bool active;
} CHiKeyhookNode;
static _Thread_local CHiKeyhookNode *lookwhatyoumademedo;
static LRESULT CALLBACK keyhook_handler(int nCode, WPARAM wParam, LPARAM lParam) {
bool eatKeystroke = false;
CHiKeyhookNode *n = lookwhatyoumademedo;
if(nCode == HC_ACTION) {
switch(wParam) {
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP: {
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam;
bool press = wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN;
char keyname[64];
GetKeyNameTextA(p->vkCode, keyname, sizeof(keyname));
if(!strcmp(keyname, n->key)) {
n->on = press;
}
break;
}
}
}
return eatKeystroke ? true : CallNextHookEx(NULL, nCode, wParam, lParam);
}
static void *keyhook_thread(void *ud) {
CHiKeyhookNode *n = ud;
lookwhatyoumademedo = n;
HHOOK hhkLowLevelKybd = SetWindowsHookEx(WH_KEYBOARD_LL, keyhook_handler, 0, 0);
MSG msg;
while(n->active && !GetMessage(&msg, NULL, NULL, NULL)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hhkLowLevelKybd);
return NULL;
}
static int keyhook_perform(CHiPubNode *n) {
CHiKeyhookNode *me = (CHiKeyhookNode*) n;
strncpy(me->key, CHi_Crawl(&n->sinks[0])->data.text, 63);
me->key[63] = '\0';
n->sources[0].type = CUTIHI_VAL_VEC4;
if(n->ng->compilationStatus == CUTIHI_COMP_READY || n->sinks[1].data.vec4[0] == 0) {
n->sources[0].data.vec4[0] = ((CHiKeyhookNode*) n)->on;
} else if(((CHiKeyhookNode*) n)->on) {
n->sources[0].data.vec4[0] = fminf(1, n->sources[0].data.vec4[0] + CHi_Time_GetDelta(n->ng) * n->sinks[1].data.vec4[0]);
} else {
n->sources[0].data.vec4[0] = fmaxf(0, n->sources[0].data.vec4[0] - CHi_Time_GetDelta(n->ng) * n->sinks[1].data.vec4[0]);
}
return 1;
}
static void keyhook_destroy(CHiPubNode *pubn) {
CHiKeyhookNode *n = (void*) pubn;
n->active = false;
pthread_join(n->thrd, NULL);
free(n);
}
CUTIVIS CHiPubNode *CHi_Keyhook() {
CHiKeyhookNode *n = calloc(1, sizeof(*n));
n->pub.type = CUTIHI_T('CKey','hook');
n->pub.Start = n->pub.Stop = NULL;
n->pub.Perform = keyhook_perform;
n->pub.Destroy = keyhook_destroy;
n->pub.sinkCount = 2;
n->pub.sinks = calloc(sizeof(*n->pub.sinks), n->pub.sinkCount);
n->pub.sourceCount = 1;
n->pub.sources = calloc(sizeof(*n->pub.sources), n->pub.sourceCount);
n->on = 0;
n->key[0] = '\n';
n->active = true;
pthread_create(&n->thrd, NULL, keyhook_thread, n);
return &n->pub;
}

View File

@@ -9,8 +9,12 @@
#include<librtmp/rtmp.h> #include<librtmp/rtmp.h>
#include<librtmp/log.h> #include<librtmp/log.h>
#ifdef _WIN32
#include<winsock2.h>
#else
#include<arpa/inet.h> #include<arpa/inet.h>
#include<sys/select.h> #include<sys/select.h>
#endif
#include"img.h" #include"img.h"

View File

@@ -218,8 +218,8 @@ static void ebml_exit_callback(EBMLReader *ebml) {
n = CHi_EncodeVP8(); n = CHi_EncodeVP8();
} else if(type == CUTIHI_T('CEnc','GVP9')) { } else if(type == CUTIHI_T('CEnc','GVP9')) {
n = CHi_EncodeVP9(); n = CHi_EncodeVP9();
} else if(type == CUTIHI_T('CExp','Webm')) { // } else if(type == CUTIHI_T('CExp','Webm')) {
n = CHi_MuxWebm(); // n = CHi_MuxWebm();
} else if(type == CUTIHI_T('CKey','hook')) { } else if(type == CUTIHI_T('CKey','hook')) {
n = CHi_Keyhook(); n = CHi_Keyhook();
} else if(type == CUTIHI_T('CKey','hook')) { } else if(type == CUTIHI_T('CKey','hook')) {

View File

@@ -42,7 +42,7 @@ static int encodevpx_perform(CHiPubNode *pub) {
MTR_BEGIN("CHi", "encodevp9_perform"); MTR_BEGIN("CHi", "encodevp9_perform");
pub->sources[0].type = CUTIHI_VAL_VP9BS; pub->sources[0].type = pub->type == CUTIHI_T('CEnc','GVP8') ? CUTIHI_VAL_VP8BS : CUTIHI_VAL_VP9BS;
pub->sources[0].data.bitstream = NULL; pub->sources[0].data.bitstream = NULL;
if(node->state == WAITING) return 1; if(node->state == WAITING) return 1;

View File

@@ -1,130 +1,20 @@
#include"node.h" #include"node.h"
#include<sys/ioctl.h>
#include<sys/time.h>
#include<sys/mman.h>
#include<stdlib.h> #include<stdlib.h>
#include<stdio.h> #include<stdio.h>
#include<fcntl.h>
#include<string.h> #include<string.h>
#include<time.h> #include<time.h>
#include<linux/videodev2.h>
#include<libv4l2.h>
#include<errno.h> #include<errno.h>
#include"img.h" #include"img.h"
#include<assert.h> #include<assert.h>
#include<smmintrin.h> #include<smmintrin.h>
static int camId = -1;
static struct Buf {
size_t length;
uint8_t *ptr;
} bufs[2];
struct v4l2_format fmt;
static void xioctl(int fh, int request, void *arg) {
int r;
do {
r = v4l2_ioctl(fh, request, arg);
} while(r == -1 && ((errno == EINTR) || (errno == EAGAIN)));
if(r == -1) {
fprintf(stderr, "error %d, %s\\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
static int camera_perform(CHiPubNode *pubn) { static int camera_perform(CHiPubNode *pubn) {
pubn->sources[0].type = CUTIHI_VAL_SAMPLE; pubn->sources[0].type = CUTIHI_VAL_NONE;
fd_set fds;
FD_ZERO(&fds);
FD_SET(camId, &fds);
int r = select(camId + 1, &fds, NULL, NULL, &(struct timeval) {.tv_sec = 0, .tv_usec = 100});
if(r == -1) {
return 1;
}
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
xioctl(camId, VIDIOC_DQBUF, &buf);
CHiImage *ret;
if(pubn->sources[0].data.sample) {
ret = pubn->sources[0].data.sample;
} else {
ret = CHi_Image_New(2, 4, 8 * ((fmt.fmt.pix.width + 15) & ~15), fmt.fmt.pix.width, fmt.fmt.pix.height, NULL);
pubn->sources[0].data.sample = ret;
}
/* 24-to-32 TODO: make faster by processing 48-to-64 bytes in one iteration (like bgra32torgb24 does) */
for(size_t y = 0; y < ret->height; y++) {
for(size_t x = 0; x < ret->width; x += 2) {
__m128i asdf = _mm_loadu_si128((__m128i*) &bufs[buf.index].ptr[(y * ret->width + x) * 3]);
asdf = _mm_shuffle_epi8(asdf, _mm_set_epi8(-128, -128, 5, -128, 4, -128, 3, -128, -128, -128, 2, -128, 1, -128, 0, -128));
asdf = _mm_or_si128(asdf, _mm_set_epi32(0xFFFF0000, 0, 0xFFFF0000, 0));
_mm_stream_si128((__m128i*) ((uintptr_t) ret->data16 + y * ret->stride + x * 8), asdf);
}
}
xioctl(camId, VIDIOC_QBUF, &buf);
return 1; return 1;
} }
CUTIVIS CHiPubNode *CHi_Camera() { CUTIVIS CHiPubNode *CHi_Camera() {
if(camId == -1) {
camId = v4l2_open("/dev/video0", O_RDWR | O_NONBLOCK, 0);
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
xioctl(camId, VIDIOC_S_FMT, &fmt);
assert(fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24);
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof(req));
req.count = 2;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
xioctl(camId, VIDIOC_REQBUFS, &req);
for(int i = 0; i < 2; i++) {
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
xioctl(camId, VIDIOC_QUERYBUF, &buf);
bufs[i].length = buf.length;
bufs[i].ptr = v4l2_mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camId, buf.m.offset);
assert(MAP_FAILED != bufs[i].ptr);
}
for(int i = 0; i < 2; i++) {
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
xioctl(camId, VIDIOC_QBUF, &buf);
}
xioctl(camId, VIDIOC_STREAMON, &(enum v4l2_buf_type) {V4L2_BUF_TYPE_VIDEO_CAPTURE});
}
CHiPubNode *pubn = calloc(1, sizeof(*pubn)); CHiPubNode *pubn = calloc(1, sizeof(*pubn));
pubn->type = CUTIHI_T('CWeb','Cam '); pubn->type = CUTIHI_T('CWeb','Cam ');
pubn->Start = pubn->Stop = NULL; pubn->Start = pubn->Stop = NULL;

136
hi/webcam_v4l2.c Normal file
View File

@@ -0,0 +1,136 @@
#include"node.h"
#include<sys/ioctl.h>
#include<sys/time.h>
#include<sys/mman.h>
#include<stdlib.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<time.h>
#include<linux/videodev2.h>
#include<libv4l2.h>
#include<errno.h>
#include"img.h"
#include<assert.h>
#include<smmintrin.h>
static int camId = -1;
static struct Buf {
size_t length;
uint8_t *ptr;
} bufs[2];
struct v4l2_format fmt;
static void xioctl(int fh, int request, void *arg) {
int r;
do {
r = v4l2_ioctl(fh, request, arg);
} while(r == -1 && ((errno == EINTR) || (errno == EAGAIN)));
if(r == -1) {
fprintf(stderr, "error %d, %s\\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
static int camera_perform(CHiPubNode *pubn) {
pubn->sources[0].type = CUTIHI_VAL_SAMPLE;
fd_set fds;
FD_ZERO(&fds);
FD_SET(camId, &fds);
int r = select(camId + 1, &fds, NULL, NULL, &(struct timeval) {.tv_sec = 0, .tv_usec = 100});
if(r == -1) {
return 1;
}
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
xioctl(camId, VIDIOC_DQBUF, &buf);
CHiImage *ret;
if(pubn->sources[0].data.sample) {
ret = pubn->sources[0].data.sample;
} else {
ret = CHi_Image_New(2, 4, 8 * ((fmt.fmt.pix.width + 15) & ~15), fmt.fmt.pix.width, fmt.fmt.pix.height, NULL);
pubn->sources[0].data.sample = ret;
}
/* 24-to-32 TODO: make faster by processing 48-to-64 bytes in one iteration (like bgra32torgb24 does) */
for(size_t y = 0; y < ret->height; y++) {
for(size_t x = 0; x < ret->width; x += 2) {
__m128i asdf = _mm_loadu_si128((__m128i*) &bufs[buf.index].ptr[(y * ret->width + x) * 3]);
asdf = _mm_shuffle_epi8(asdf, _mm_set_epi8(-128, -128, 5, -128, 4, -128, 3, -128, -128, -128, 2, -128, 1, -128, 0, -128));
asdf = _mm_or_si128(asdf, _mm_set_epi32(0xFFFF0000, 0, 0xFFFF0000, 0));
_mm_stream_si128((__m128i*) ((uintptr_t) ret->data16 + y * ret->stride + x * 8), asdf);
}
}
xioctl(camId, VIDIOC_QBUF, &buf);
return 1;
}
CUTIVIS CHiPubNode *CHi_Camera() {
if(camId == -1) {
camId = v4l2_open("/dev/video0", O_RDWR | O_NONBLOCK, 0);
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
xioctl(camId, VIDIOC_S_FMT, &fmt);
assert(fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24);
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof(req));
req.count = 2;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
xioctl(camId, VIDIOC_REQBUFS, &req);
for(int i = 0; i < 2; i++) {
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
xioctl(camId, VIDIOC_QUERYBUF, &buf);
bufs[i].length = buf.length;
bufs[i].ptr = v4l2_mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camId, buf.m.offset);
assert(MAP_FAILED != bufs[i].ptr);
}
for(int i = 0; i < 2; i++) {
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
xioctl(camId, VIDIOC_QBUF, &buf);
}
xioctl(camId, VIDIOC_STREAMON, &(enum v4l2_buf_type) {V4L2_BUF_TYPE_VIDEO_CAPTURE});
}
CHiPubNode *pubn = calloc(1, sizeof(*pubn));
pubn->type = CUTIHI_T('CWeb','Cam ');
pubn->Start = pubn->Stop = NULL;
pubn->Perform = camera_perform;
pubn->sinks = calloc(sizeof(*pubn->sinks), pubn->sinkCount = 0);
pubn->sources = calloc(sizeof(*pubn->sources), pubn->sourceCount = 1);
pubn->ng = NULL;
return pubn;
}

View File

View File

@@ -4,6 +4,7 @@
#include<ScreenCapture.h> #include<ScreenCapture.h>
#include<mutex> #include<mutex>
#include<atomic>
#include<tmmintrin.h> #include<tmmintrin.h>
#include<smmintrin.h> #include<smmintrin.h>
@@ -26,6 +27,7 @@ typedef struct {
std::mutex mut; std::mutex mut;
std::vector<CHiImage*> images; std::vector<CHiImage*> images;
std::atomic<bool> ignoreAlpha;
} CHiWindowNode; } CHiWindowNode;
static int window_perform(CHiPubNode *n) { static int window_perform(CHiPubNode *n) {
@@ -33,6 +35,9 @@ static int window_perform(CHiPubNode *n) {
MTR_BEGIN("CHi", "window_perform"); MTR_BEGIN("CHi", "window_perform");
CHiValue *ignoreAlphaVal = CHi_Crawl(&w->pub.sinks[1]);
w->ignoreAlpha.store(ignoreAlphaVal ? (bool) ignoreAlphaVal->data.vec4[0] : true);
const char *expectedTitle = CHi_Crawl(&w->pub.sinks[0])->data.text; const char *expectedTitle = CHi_Crawl(&w->pub.sinks[0])->data.text;
if(w->lastWindowString == nullptr || strcmp(w->lastWindowString, expectedTitle)) { if(w->lastWindowString == nullptr || strcmp(w->lastWindowString, expectedTitle)) {
@@ -52,6 +57,8 @@ static int window_perform(CHiPubNode *n) {
CHiImage *new_image = CHi_Image_New(2, 4, (window.Size.x * 8 + 15) & ~15, window.Size.x, window.Size.y, nullptr); CHiImage *new_image = CHi_Image_New(2, 4, (window.Size.x * 8 + 15) & ~15, window.Size.x, window.Size.y, nullptr);
memset(new_image->data8, 0, new_image->stride * new_image->height); memset(new_image->data8, 0, new_image->stride * new_image->height);
bool ignoreAlpha = w->ignoreAlpha.load();
#pragma omp parallel for #pragma omp parallel for
for(size_t y = 0; y < new_image->height; y++) { for(size_t y = 0; y < new_image->height; y++) {
uint8_t buf[16] = {}; uint8_t buf[16] = {};
@@ -60,6 +67,9 @@ static int window_perform(CHiPubNode *n) {
__m128i c = _mm_loadu_si128((__m128i*) buf); __m128i c = _mm_loadu_si128((__m128i*) buf);
c = _mm_shuffle_epi8(c, _mm_set_epi8(7, -128, 6, -128, 5, -128, 4, -128, 3, -128, 2, -128, 1, -128, 0, -128)); c = _mm_shuffle_epi8(c, _mm_set_epi8(7, -128, 6, -128, 5, -128, 4, -128, 3, -128, 2, -128, 1, -128, 0, -128));
if(ignoreAlpha) {
c = _mm_or_si128(c, _mm_set_epi16(0xFFFF, 0, 0, 0, 0xFFFF, 0, 0, 0));
}
c = apply_gamma_epi16(c, _mm_set_ps(1, 2.2f, 2.2f, 2.2f)); c = apply_gamma_epi16(c, _mm_set_ps(1, 2.2f, 2.2f, 2.2f));
_mm_store_si128((__m128i*) ((uintptr_t) new_image->data8 + y * new_image->stride + x * 8), c); _mm_store_si128((__m128i*) ((uintptr_t) new_image->data8 + y * new_image->stride + x * 8), c);
} }
@@ -103,7 +113,7 @@ CUTIVIS CHiPubNode *CHi_Window() {
n->pub.Perform = window_perform; n->pub.Perform = window_perform;
n->pub.Destroy = window_destroy; n->pub.Destroy = window_destroy;
n->pub.sinkCount = 1; n->pub.sinkCount = 1;
n->pub.sinks = (CHiValue*) calloc(sizeof(*n->pub.sinks), 1); n->pub.sinks = (CHiValue*) calloc(sizeof(*n->pub.sinks), 2);
n->pub.sourceCount = 1; n->pub.sourceCount = 1;
n->pub.sources = (CHiValue*) calloc(sizeof(*n->pub.sources), 1); n->pub.sources = (CHiValue*) calloc(sizeof(*n->pub.sources), 1);

205
hi/window_x11.cpp Normal file
View File

@@ -0,0 +1,205 @@
#include"node.h"
#include<stdlib.h>
#include<X11/Xlib.h>
#include<X11/Xutil.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<X11/extensions/XShm.h>
#include<tmmintrin.h>
#include<smmintrin.h>
#include<time.h>
#include<string.h>
#include"img.h"
#include"linearity.h"
#include"minitrace.h"
static thread_local Display *d;
static thread_local Window root;
static int find_window(Display *d, Window *w, const char *contains) {
if(contains) {
int found = 0;
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
Atom actualType;
int format;
unsigned long numItems, bytesAfter;
Window *list;
XTextProperty windowName;
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
if(status >= Success) {
for(int i = 0; i < numItems; i++) {
status = XGetWMName(d, list[i], &windowName);
if(status >= Success) {
if(windowName.value && strstr((const char*) windowName.value, contains)) {
*w = list[i];
found = 1;
break;
}
}
}
}
XFree(list);
return found;
} else {
*w = root;
return 1;
}
}
typedef struct {
Window xcache;
XImage *ximg;
XShmSegmentInfo shminfo;
CHiImage *vcache;
} WindowImpl;
static int window_perform(CHiPubNode *n) {
auto *w = (WindowImpl*) n->impl;
if(!d) {
d = XOpenDisplay(NULL);
root = RootWindow(d, DefaultScreen(d));
}
MTR_BEGIN("CHi", "window_perform");
Window toshoot;
if(!find_window(d, &toshoot, CHi_Crawl(&n->sinks[0])->data.text)) return 0;
size_t stride;
if(!w->xcache || w->xcache != toshoot) {
w->xcache = toshoot;
XWindowAttributes attrs;
XGetWindowAttributes(d, toshoot, &attrs);
w->ximg = XShmCreateImage(d, attrs.visual, 32, ZPixmap, NULL, &w->shminfo, attrs.width, attrs.height);
stride = ((w->ximg->bytes_per_line + 15) & ~15);
w->shminfo.shmid = shmget(IPC_PRIVATE, stride * w->ximg->height, IPC_CREAT | 0777);
w->shminfo.shmaddr = w->ximg->data = (char*) shmat(w->shminfo.shmid, 0, 0);
w->shminfo.readOnly = False;
XShmAttach(d, &w->shminfo);
w->vcache = CHi_Image_New(2, 4, (8 * attrs.width + 15) & ~15, attrs.width, attrs.height, NULL);
} else {
stride = ((w->ximg->bytes_per_line + 15) & ~15);
}
XWindowAttributes toshootattrs;
XGetWindowAttributes(d, w->xcache, &toshootattrs);
XShmGetImage(d, w->xcache, w->ximg, 0, 0, AllPlanes);
bool ignoreAlpha = CHi_Crawl(&n->sinks[1])->data.vec4[0] != 0;
// Turn u8 image to u16
#pragma omp parallel for
for(size_t y = 0; y < w->vcache->height; y++) {
for(size_t x = 0; x < w->vcache->width; x += 2) {
__m128i c = _mm_loadu_si128((__m128i*) ((uintptr_t) w->ximg->data + y * w->ximg->bytes_per_line + x * 4));
c = _mm_shuffle_epi8(c, _mm_set_epi8(7, -128, 6, -128, 5, -128, 4, -128, 3, -128, 2, -128, 1, -128, 0, -128));
if(ignoreAlpha) {
c = _mm_or_si128(c, _mm_set_epi16(0xFFFF, 0, 0, 0, 0xFFFF, 0, 0, 0));
}
c = apply_gamma_epi16(c, _mm_set_ps(1, 2.2f, 2.2f, 2.2f));
_mm_storeu_si128((__m128i*) ((uintptr_t) w->vcache->data16 + y * w->vcache->stride + x * 8), c);
}
}
n->sources[0].type = CUTIHI_VAL_SAMPLE;
n->sources[0].data.sample = w->vcache;
MTR_END("CHi", "window_perform");
return 1;
}
static void window_destroy(CHiPubNode *pubn) {
auto *n = (WindowImpl*) pubn->impl;
if(n->vcache) {
XShmDetach(d, &n->shminfo);
shmdt(n->shminfo.shmaddr);
XDestroyImage(n->ximg);
}
free(pubn);
}
CUTIVIS CHiPubNode *CHi_Window() {
CHiPubNode *n = CHi_AllocNode(CUTIHI_T('CWin','dow '), 2, 1, sizeof(WindowImpl));
n->Start = n->Stop = NULL;
n->Perform = window_perform;
n->Destroy = window_destroy;
return n;
}
// All of the following are ews
struct WindowListDatum {
Window handle;
char name[128];
};
CUTIVIS size_t CHi_Window_GetList(void **buf) {
if(!d) {
d = XOpenDisplay(NULL);
root = RootWindow(d, DefaultScreen(d));
}
int found = 0;
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
Atom actualType;
int format;
unsigned long numItems, bytesAfter;
Window *list;
XTextProperty windowName;
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
if(status >= Success) {
WindowListDatum *data = (WindowListDatum*) calloc(numItems, sizeof(*data));
size_t successfulWindows = 0;
for(size_t i = 0; i < numItems; i++) {
status = XGetWMName(d, list[i], &windowName);
if(status >= Success) {
data[successfulWindows].handle = list[i];
strncpy(data[successfulWindows].name, (char*) windowName.value, sizeof(data[successfulWindows].name));
successfulWindows++;
}
}
XFree(list);
*buf = data;
return successfulWindows;
}
*buf = nullptr;
return 0;
}
CUTIVIS const char *CHi_Window_GetName(void *buf, size_t i) {
return ((WindowListDatum*) buf)[i].name;
}
CUTIVIS size_t CHi_Window_GetHandle(void *buf, size_t i) {
return ((WindowListDatum*) buf)[i].handle;
}
CUTIVIS void CHi_Window_FreeList(void *buf) {
free(buf);
}

View File

@@ -37,7 +37,13 @@ static int INJECTED_PREVIEW_FUNC(CHiPubNode *preview) {
CHiValue *val = CHi_Crawl(&preview->sinks[0]); CHiValue *val = CHi_Crawl(&preview->sinks[0]);
if(val->type == CUTIHI_VAL_SAMPLE && val->data.sample) { if(val->type == CUTIHI_VAL_SAMPLE && val->data.sample) {
globaldis->viewer->SetImage(val->data.sample); float t = CHi_Time_Get(preview->ng);
globaldis->CallAfter([=](){
globaldis->viewer->SetImage(val->data.sample);
globaldis->timeline->Refresh();
globaldis->stba->SetStatusText(wxString::Format("%02i:%02i:%06.03fs", (int) (t / 3600), (int) (t / 60), fmodf(t, 60)));
});
} }
return 1; return 1;
@@ -135,19 +141,19 @@ static void ShapeGrNode(GrNode *gn) {
gn->sinks.push_back({" Size", GrNode::Port::Type::VEC1, true}); gn->sinks.push_back({" Size", GrNode::Port::Type::VEC1, true});
} }
} else if(gn->logical->type == CUTIHI_T('CIma','ge ')) { } else if(gn->logical->type == CUTIHI_T('CIma','ge ')) {
gn->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPEN}}; gn->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPENING}};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}}; gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CWin','dow ')) { } else if(gn->logical->type == CUTIHI_T('CWin','dow ')) {
gn->sinks = {{"Name", GrNode::Port::Type::WINDOW_SOURCE}}; gn->sinks = {{"Name", GrNode::Port::Type::WINDOW_SOURCE}, {"Ignore Alpha", GrNode::Port::Type::CHECKBOX}};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}}; gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CInA','udio')) { } else if(gn->logical->type == CUTIHI_T('CInA','udio')) {
gn->sinks = {{"Source", GrNode::Port::Type::MIC_SOURCE}}; gn->sinks = {{"Source", GrNode::Port::Type::MIC_SOURCE}};
gn->sources = {{"Audio", GrNode::Port::Type::SAMPLE}}; gn->sources = {{"Audio", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CExp','Wave')) { } else if(gn->logical->type == CUTIHI_T('CExp','Wave')) {
gn->sinks = {{"Filename", GrNode::Port::Type::FILE_SAVE}, {"Audio", GrNode::Port::Type::SAMPLE}}; gn->sinks = {{"Filename", GrNode::Port::Type::FILE_SAVING}, {"Audio", GrNode::Port::Type::SAMPLE}};
gn->sources = {}; gn->sources = {};
} else if(gn->logical->type == CUTIHI_T('CMov','ie ')) { } else if(gn->logical->type == CUTIHI_T('CMov','ie ')) {
gn->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPEN}, {"Time", GrNode::Port::Type::VEC1}}; gn->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPENING}, {"Time", GrNode::Port::Type::VEC1}};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}, {"Audio", GrNode::Port::Type::SAMPLE}}; gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}, {"Audio", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CEnc','GVP8')) { } else if(gn->logical->type == CUTIHI_T('CEnc','GVP8')) {
gn->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}}; gn->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}};
@@ -159,7 +165,7 @@ static void ShapeGrNode(GrNode *gn) {
gn->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}}; gn->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}};
gn->sources = {{"Bitstream"}}; gn->sources = {{"Bitstream"}};
} else if(gn->logical->type == CUTIHI_T('CExp','Webm')) { } else if(gn->logical->type == CUTIHI_T('CExp','Webm')) {
gn->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVE}}; gn->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVING}};
gn->sources = {}; gn->sources = {};
} else if(gn->logical->type == CUTIHI_T('CKey','hook')) { } else if(gn->logical->type == CUTIHI_T('CKey','hook')) {
gn->sinks = {{"Key", GrNode::Port::Type::TEXT}, {"Smooth Time", GrNode::Port::Type::VEC1}}; gn->sinks = {{"Key", GrNode::Port::Type::TEXT}, {"Smooth Time", GrNode::Port::Type::VEC1}};
@@ -183,7 +189,7 @@ static void ShapeGrNode(GrNode *gn) {
gn->sinks = {{"Audio", GrNode::Port::Type::SAMPLE}}; gn->sinks = {{"Audio", GrNode::Port::Type::SAMPLE}};
gn->sources = {{"Bitstream"}}; gn->sources = {{"Bitstream"}};
} else if(gn->logical->type == CUTIHI_T('CExp','Mkv ')) { } else if(gn->logical->type == CUTIHI_T('CExp','Mkv ')) {
gn->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVE}}; gn->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVING}};
gn->sources = {}; gn->sources = {};
} }
@@ -301,21 +307,6 @@ Frame::Frame() : wxFrame(NULL, wxID_ANY, "Cuticle", wxDefaultPosition, {wxSystem
} else { } else {
CHi_BeginCompilation(graph->backendNG); CHi_BeginCompilation(graph->backendNG);
toolbar.btnPerform->SetLabel("Kill"); toolbar.btnPerform->SetLabel("Kill");
std::thread{[=](){
while(graph->backendNG->compilationStatus == CUTIHI_COMP_RUNNING) {
CallAfter([=](){
float t = CHi_Time_Get(graph->backendNG);
stba->SetStatusText(wxString::Format("%02i:%02i:%06.03fs", (int) (t / 3600), (int) (t / 60), fmodf(t, 60)));
});
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
CallAfter([=](){
stba->SetStatusText("Compilation has ended.");
});
}}.detach();
} }
}); });
@@ -412,6 +403,9 @@ void Frame::LoadProject(std::string filename) {
auto tlba = ((Frame*) ng->ud)->tlba; auto tlba = ((Frame*) ng->ud)->tlba;
tlba->EnableTool(wxID_SAVE, true); tlba->EnableTool(wxID_SAVE, true);
tlba->EnableTool(wxID_OPEN, true); tlba->EnableTool(wxID_OPEN, true);
auto stba = ((Frame*) ng->ud)->stba;
stba->SetStatusText("Compilation has ended.");
}); });
}; };
@@ -484,8 +478,8 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
int y = 13; int y = 13;
int i = 0; int i = 0;
for(Port &p : sinks) { for(Port &p : sinks) {
wxColour col = p.type == GrNode::Port::Type::FILE_OPEN ? wxColour{255, 0, 0} wxColour col = p.type == GrNode::Port::Type::FILE_OPENING ? wxColour{255, 0, 0}
: p.type == GrNode::Port::Type::FILE_SAVE ? wxColour{255, 0, 0} : p.type == GrNode::Port::Type::FILE_SAVING ? wxColour{255, 0, 0}
: p.type == GrNode::Port::Type::COLOR ? wxColour{0, 0, 255} : p.type == GrNode::Port::Type::COLOR ? wxColour{0, 0, 255}
: p.type == GrNode::Port::Type::VEC2 ? wxColour{0, 255, 0} : p.type == GrNode::Port::Type::VEC2 ? wxColour{0, 255, 0}
: p.type == GrNode::Port::Type::TEXT ? wxColour{255, 255, 0} : p.type == GrNode::Port::Type::TEXT ? wxColour{255, 255, 0}
@@ -512,8 +506,8 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
y = 13; y = 13;
i = 0; i = 0;
for(Port &p : sources) { for(Port &p : sources) {
wxColour col = p.type == GrNode::Port::Type::FILE_OPEN ? wxColour{255, 0, 0} wxColour col = p.type == GrNode::Port::Type::FILE_OPENING ? wxColour{255, 0, 0}
: p.type == GrNode::Port::Type::FILE_SAVE ? wxColour{255, 0, 0} : p.type == GrNode::Port::Type::FILE_SAVING ? wxColour{255, 0, 0}
: p.type == GrNode::Port::Type::COLOR ? wxColour{0, 0, 255} : p.type == GrNode::Port::Type::COLOR ? wxColour{0, 0, 255}
: p.type == GrNode::Port::Type::VEC2 ? wxColour{0, 255, 0} : p.type == GrNode::Port::Type::VEC2 ? wxColour{0, 255, 0}
: p.type == GrNode::Port::Type::TEXT ? wxColour{255, 255, 0} : p.type == GrNode::Port::Type::TEXT ? wxColour{255, 255, 0}
@@ -650,7 +644,7 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
pthread_mutex_unlock(&this->logical->ng->mut); pthread_mutex_unlock(&this->logical->ng->mut);
} }
} else if(sinks[p].type == Port::Type::FILE_OPEN) { } else if(sinks[p].type == Port::Type::FILE_OPENING) {
wxFileDialog dlg(this, wxFileSelectorPromptStr, wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_OPEN | wxFD_PREVIEW); wxFileDialog dlg(this, wxFileSelectorPromptStr, wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_OPEN | wxFD_PREVIEW);
if(dlg.ShowModal() == wxID_OK) { if(dlg.ShowModal() == wxID_OK) {
pthread_mutex_lock(&this->logical->ng->mut); pthread_mutex_lock(&this->logical->ng->mut);
@@ -663,7 +657,7 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
pthread_mutex_unlock(&this->logical->ng->mut); pthread_mutex_unlock(&this->logical->ng->mut);
} }
} else if(sinks[p].type == Port::Type::FILE_SAVE) { } else if(sinks[p].type == Port::Type::FILE_SAVING) {
wxFileDialog dlg(this, wxFileSelectorPromptStr, wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_SAVE | wxFD_PREVIEW | wxFD_OVERWRITE_PROMPT); wxFileDialog dlg(this, wxFileSelectorPromptStr, wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_SAVE | wxFD_PREVIEW | wxFD_OVERWRITE_PROMPT);
if(dlg.ShowModal() == wxID_OK) { if(dlg.ShowModal() == wxID_OK) {
pthread_mutex_lock(&this->logical->ng->mut); pthread_mutex_lock(&this->logical->ng->mut);
@@ -679,28 +673,29 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
} else if(sinks[p].type >= Port::Type::VEC1 && sinks[p].type <= Port::Type::VEC4) { } else if(sinks[p].type >= Port::Type::VEC1 && sinks[p].type <= Port::Type::VEC4) {
auto ctrls = std::make_shared<std::vector<wxTextCtrl*>>(); auto ctrls = std::make_shared<std::vector<wxTextCtrl*>>();
for(int i = 0; i <= (int) sinks[p].type - (int) Port::Type::VEC1; i++) { for(int i = 0; i <= (int) sinks[p].type - (int) Port::Type::VEC1; i++) {
wxTextCtrl *tc = new wxTextCtrl(GetParent(), wxID_ANY, wxString::Format("%f", this->logical->sinks[p].data.vec4[i]), GetParent()->ScreenToClient(ClientToScreen({5 + 60 * i, (p + 1) * 20}))); wxTextCtrl *tc = new wxTextCtrl(GetParent(), wxID_ANY, wxString::Format("%f", this->logical->sinks[p].data.vec4[i]), GetParent()->ScreenToClient(ClientToScreen({5 + 60 * i, (p + 1) * 20})), wxDefaultSize, wxTE_PROCESS_ENTER);
tc->Bind(wxEVT_TEXT_ENTER, [=](wxCommandEvent &ev){
double d;
if(tc->GetValue().ToDouble(&d)) {
pthread_mutex_lock(&this->logical->ng->mut);
CHiValue newv = *CHi_Crawl(&this->logical->sinks[p]);
newv.type = CUTIHI_VAL_VEC4;
newv.data.vec4[i] = d;
CHi_ConfigureSink(this->logical, p, newv);
auto it = std::find(ctrls->begin(), ctrls->end(), tc);
ctrls->operator[]((it - ctrls->begin() + 1) % ctrls->size())->SetFocus();
ctrls->erase(it);
CallAfter([tc](){tc->Destroy();});
parent->Dirtify(this);
pthread_mutex_unlock(&this->logical->ng->mut);
}
});
tc->Bind(wxEVT_KEY_DOWN, [=](wxKeyEvent &ev){ tc->Bind(wxEVT_KEY_DOWN, [=](wxKeyEvent &ev){
if(ev.GetKeyCode() == WXK_RETURN) { if(ev.GetKeyCode() == WXK_TAB) {
double d;
if(tc->GetValue().ToDouble(&d)) {
pthread_mutex_lock(&this->logical->ng->mut);
CHiValue newv = *CHi_Crawl(&this->logical->sinks[p]);
newv.type = CUTIHI_VAL_VEC4;
newv.data.vec4[i] = d;
CHi_ConfigureSink(this->logical, p, newv);
auto it = std::find(ctrls->begin(), ctrls->end(), tc);
ctrls->operator[]((it - ctrls->begin() + 1) % ctrls->size())->SetFocus();
ctrls->erase(it);
CallAfter([tc](){tc->Destroy();});
parent->Dirtify(this);
pthread_mutex_unlock(&this->logical->ng->mut);
}
} else if(ev.GetKeyCode() == WXK_TAB) {
ctrls->operator[]((i + ctrls->size() + (wxGetKeyState(WXK_SHIFT) ? -1 : 1)) % ctrls->size())->SetFocus(); ctrls->operator[]((i + ctrls->size() + (wxGetKeyState(WXK_SHIFT) ? -1 : 1)) % ctrls->size())->SetFocus();
parent->Dirtify(this); parent->Dirtify(this);
@@ -710,7 +705,7 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
} }
ctrls->operator[](0)->SetFocus(); ctrls->operator[](0)->SetFocus();
} else if(sinks[p].type == Port::Type::TEXT) { } else if(sinks[p].type == Port::Type::TEXT) {
wxTextCtrl *ctrl = new wxTextCtrl(GetParent(), wxID_ANY, this->logical->sinks[p].data.text, GetParent()->ScreenToClient(ClientToScreen({5, (p + 1) * 26}))); wxTextCtrl *ctrl = new wxTextCtrl(GetParent(), wxID_ANY, this->logical->sinks[p].data.text, GetParent()->ScreenToClient(ClientToScreen({5, (p + 1) * 26})), wxDefaultSize, wxTE_PROCESS_ENTER);
ctrl->SetValue(wxString{CHi_Crawl(&this->logical->sinks[p])->data.text}); ctrl->SetValue(wxString{CHi_Crawl(&this->logical->sinks[p])->data.text});
ctrl->SetFocus(); ctrl->SetFocus();
ctrl->Bind(wxEVT_KILL_FOCUS, [=](wxFocusEvent &ev){ ctrl->Bind(wxEVT_KILL_FOCUS, [=](wxFocusEvent &ev){
@@ -727,21 +722,19 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
pthread_mutex_unlock(&this->logical->ng->mut); pthread_mutex_unlock(&this->logical->ng->mut);
}); });
ctrl->Bind(wxEVT_KEY_DOWN, [=](wxKeyEvent &ev){ ctrl->Bind(wxEVT_TEXT_ENTER, [=](wxCommandEvent &ev){
if(ev.GetKeyCode() == WXK_RETURN) { pthread_mutex_lock(&this->logical->ng->mut);
pthread_mutex_lock(&this->logical->ng->mut);
CHiValue newv = {}; CHiValue newv = {};
newv.type = CUTIHI_VAL_TEXT; newv.type = CUTIHI_VAL_TEXT;
char *c = (char*) malloc(ctrl->GetValue().Len() + 1); char *c = (char*) malloc(ctrl->GetValue().Len() + 1);
memcpy(c, ctrl->GetValue().c_str(), ctrl->GetValue().Len() + 1); memcpy(c, ctrl->GetValue().c_str(), ctrl->GetValue().Len() + 1);
newv.data.text = c; newv.data.text = c;
CHi_ConfigureSink(this->logical, p, newv); CHi_ConfigureSink(this->logical, p, newv);
CallAfter([ctrl](){ctrl->Destroy();}); CallAfter([ctrl](){ctrl->Destroy();});
parent->Dirtify(this); parent->Dirtify(this);
pthread_mutex_unlock(&this->logical->ng->mut); pthread_mutex_unlock(&this->logical->ng->mut);
} else ev.Skip();
}); });
} else if(sinks[p].type == Port::Type::MIC_SOURCE) { } else if(sinks[p].type == Port::Type::MIC_SOURCE) {
std::vector<wxString> choices; std::vector<wxString> choices;
@@ -790,6 +783,26 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
} }
CHi_Window_FreeList(wbuf); CHi_Window_FreeList(wbuf);
} else if(sinks[p].type == Port::Type::CHECKBOX) {
wxCheckBox *cb = new wxCheckBox(this, wxID_ANY, sinks[p].name);
cb->SetValue((bool) this->logical->sinks[p].data.vec4[0]);
cb->SetFocus();
cb->Bind(wxEVT_KILL_FOCUS, [=](wxFocusEvent &ev){
pthread_mutex_lock(&this->logical->ng->mut);
CHiValue newv = {};
newv.type = CUTIHI_VAL_VEC4;
newv.data.vec4[0] = cb->IsChecked();
newv.data.vec4[1] = cb->IsChecked();
newv.data.vec4[2] = cb->IsChecked();
newv.data.vec4[3] = cb->IsChecked();
CHi_ConfigureSink(this->logical, p, newv);
parent->Dirtify(this);
CallAfter([cb](){cb->Destroy();});
pthread_mutex_unlock(&this->logical->ng->mut);
});
} }
} }
}); });
@@ -973,7 +986,7 @@ void ImageViewer::SetImage(CHiImage *chim) {
ResizeImage(siez); ResizeImage(siez);
} }
void ImageViewer::ResizeImage(float size) { void ImageViewer::ResizeImage(float size) {
float w = size, h = (float) bufH / bufW * siez; float w = size, h = (float) bufH / bufW * size;
if(w <= 1 || h <= 1) { if(w <= 1 || h <= 1) {
return; return;

View File

@@ -51,7 +51,7 @@ struct GrNode : wxPanel {
struct Port { struct Port {
wxString name; wxString name;
enum class Type { enum class Type {
NONE, FILE_OPEN, COLOR, VEC1, VEC2, VEC3, VEC4, TEXT, SAMPLE, FILE_SAVE, MIC_SOURCE, WINDOW_SOURCE NONE, FILE_OPENING, COLOR, VEC1, VEC2, VEC3, VEC4, TEXT, SAMPLE, FILE_SAVING, MIC_SOURCE, WINDOW_SOURCE, CHECKBOX
} type; } type;
bool separator; bool separator;

View File

@@ -6,6 +6,9 @@ Frame *globaldis;
struct App : wxApp { struct App : wxApp {
virtual bool OnInit() { virtual bool OnInit() {
#if wxCHECK_VERSION(3, 3, 0)
SetAppearance(Appearance::System);
#endif
(new Frame())->Show(true); (new Frame())->Show(true);
return true; return true;
} }

View File

@@ -259,8 +259,8 @@ Timeline::Timeline(struct Frame *parent) : wxPanel(parent, wxID_ANY) {
switch(sinks[i].type) { switch(sinks[i].type) {
case GrNode::Port::Type::TEXT: case GrNode::Port::Type::TEXT:
case GrNode::Port::Type::FILE_OPEN: case GrNode::Port::Type::FILE_OPENING:
case GrNode::Port::Type::FILE_SAVE: case GrNode::Port::Type::FILE_SAVING:
SetToolTip(wxString{val->text}); SetToolTip(wxString{val->text});
break; break;
case GrNode::Port::Type::VEC1: case GrNode::Port::Type::VEC1: