Compare commits
20 Commits
fb3c9d892d
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1a1b9a1eb | ||
|
|
cc70d9138a | ||
|
|
28e7fa3365 | ||
|
|
8b78f0cdf7 | ||
|
|
542b271c6d | ||
|
|
9ad84c447b | ||
|
|
3993163d6d | ||
|
|
d77ae15b46 | ||
|
|
fb6b0dd492 | ||
|
|
aff60df9da | ||
|
|
c7468d0944 | ||
|
|
1651beddf4 | ||
|
|
0384176de6 | ||
|
|
e510d92b17 | ||
|
|
c91115dad4 | ||
|
|
b6e6713860 | ||
|
|
33c9207947 | ||
|
|
50c9a7a6ff | ||
|
|
cc88f1f0dd | ||
|
|
86fe6995ad |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,3 +3,6 @@ tests
|
|||||||
*.webm
|
*.webm
|
||||||
/cuticle
|
/cuticle
|
||||||
*.so
|
*.so
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
|||||||
45
Makefile
45
Makefile
@@ -1,6 +1,26 @@
|
|||||||
|
|
||||||
CXXFLAGS := -D_POSIX_C_SOURCE=200809L -Wno-narrowing -march=native -flto -Wall -fvisibility=hidden -fPIC -msse4 -I./ -I/usr/local/include/sail `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
|
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,22 +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)
|
||||||
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o window.o hi/window.c $(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++11 -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++11 -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=c++11 -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) -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 $(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) -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)
|
||||||
|
|||||||
75
hi/bs.h
75
hi/bs.h
@@ -5,6 +5,9 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<string.h>
|
||||||
|
|
||||||
#define CUTIHI_BS_FLAG_KEY 1
|
#define CUTIHI_BS_FLAG_KEY 1
|
||||||
#define CUTIHI_BS_SETUP_PACKET 2
|
#define CUTIHI_BS_SETUP_PACKET 2
|
||||||
|
|
||||||
@@ -20,6 +23,78 @@ typedef struct {
|
|||||||
CHiBSFrame data[];
|
CHiBSFrame data[];
|
||||||
} CHiBSFrames;
|
} CHiBSFrames;
|
||||||
|
|
||||||
|
static inline CHiBSFrames *CHi_BS_Combine(CHiBSFrames *A, const CHiBSFrames *B) {
|
||||||
|
CHiBSFrames *ret = NULL;
|
||||||
|
|
||||||
|
size_t aoffset = 0;
|
||||||
|
|
||||||
|
if(A) {
|
||||||
|
aoffset = A->count;
|
||||||
|
|
||||||
|
ret = (CHiBSFrames*) calloc(1, sizeof(*ret) + sizeof(CHiBSFrame) * (A->count + B->count));
|
||||||
|
ret->count = A->count + B->count;
|
||||||
|
memcpy(ret->data, A->data, sizeof(*A->data) * A->count);
|
||||||
|
|
||||||
|
free(A);
|
||||||
|
} else {
|
||||||
|
ret = (CHiBSFrames*) calloc(1, sizeof(*ret) + sizeof(CHiBSFrame) * B->count);
|
||||||
|
ret->count = B->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < B->count; i++) {
|
||||||
|
void *copy = malloc(B->data[i].sz);
|
||||||
|
memcpy(copy, B->data[i].ptr, B->data[i].sz);
|
||||||
|
|
||||||
|
ret->data[aoffset + i] = (CHiBSFrame) {
|
||||||
|
.timestamp = B->data[i].timestamp,
|
||||||
|
.sz = B->data[i].sz,
|
||||||
|
.flags = B->data[i].flags,
|
||||||
|
.ptr = copy
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline CHiBSFrames *CHi_BS_Empty() {
|
||||||
|
return (CHiBSFrames*) calloc(1, sizeof(CHiBSFrames));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CHi_BS_Pop(CHiBSFrames *bsfs, size_t num) {
|
||||||
|
for(size_t i = 0; i < num; i++) {
|
||||||
|
free(bsfs->data[i].ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
memmove(&bsfs->data[0], &bsfs->data[num], sizeof(bsfs->data[0]) * (bsfs->count - num));
|
||||||
|
|
||||||
|
bsfs->count -= num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CHi_BS_Clear(CHiBSFrames *bsfs) {
|
||||||
|
CHi_BS_Pop(bsfs, bsfs->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CHi_BS_Free(CHiBSFrames *bsfs) {
|
||||||
|
if(bsfs) {
|
||||||
|
CHi_BS_Clear(bsfs);
|
||||||
|
free(bsfs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline CHiBSFrames *CHi_BS_Grow(CHiBSFrames *bsfs, size_t num) {
|
||||||
|
size_t oldsz = sizeof(*bsfs) + sizeof(CHiBSFrame) * bsfs->count;
|
||||||
|
size_t newsz = sizeof(*bsfs) + sizeof(CHiBSFrame) * (bsfs->count + num);
|
||||||
|
CHiBSFrames *ret = (CHiBSFrames*) realloc(bsfs, newsz);
|
||||||
|
ret->count += num;
|
||||||
|
memset((uint8_t*) ret + oldsz, 0, newsz - oldsz);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
} CHiVPxBSSettings;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
14
hi/img.c
14
hi/img.c
@@ -2,18 +2,23 @@
|
|||||||
|
|
||||||
#include<mm_malloc.h>
|
#include<mm_malloc.h>
|
||||||
#include<string.h>
|
#include<string.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);
|
||||||
|
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,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++;
|
||||||
|
}
|
||||||
|
|||||||
4
hi/img.h
4
hi/img.h
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ static int microphone_perform(CHiPubNode *pubn) {
|
|||||||
size_t width = roundf(CHi_Time_GetDelta(pubn->ng) * 48000.f);
|
size_t width = roundf(CHi_Time_GetDelta(pubn->ng) * 48000.f);
|
||||||
do {
|
do {
|
||||||
}while((node->paBufferWriteIdx - node->paBufferReadIdx + pabufsize) % pabufsize < width); // Wait until available
|
}while((node->paBufferWriteIdx - node->paBufferReadIdx + pabufsize) % pabufsize < width); // Wait until available
|
||||||
CHiImage *ret = CHi_Image_New(2, 1, 2 * width, width, 1, NULL);
|
CHiImage *ret = CHi_Image_New(2, 1, (2 * width + 15) & ~15, width, 1, NULL);
|
||||||
if(node->paBufferReadIdx + width >= pabufsize) {
|
if(node->paBufferReadIdx + width >= pabufsize) {
|
||||||
memcpy(ret->data16, node->paBuffer + node->paBufferReadIdx, sizeof(*node->paBuffer) * (pabufsize - node->paBufferReadIdx));
|
memcpy(ret->data16, node->paBuffer + node->paBufferReadIdx, sizeof(*node->paBuffer) * (pabufsize - node->paBufferReadIdx));
|
||||||
memcpy(ret->data16 + pabufsize - node->paBufferReadIdx, node->paBuffer, sizeof(*node->paBuffer) * (width - pabufsize + node->paBufferReadIdx));
|
memcpy(ret->data16 + pabufsize - node->paBufferReadIdx, node->paBuffer, sizeof(*node->paBuffer) * (width - pabufsize + node->paBufferReadIdx));
|
||||||
|
|||||||
632
hi/mkv.c
Normal file
632
hi/mkv.c
Normal file
@@ -0,0 +1,632 @@
|
|||||||
|
#include"node.h"
|
||||||
|
|
||||||
|
#include<vpx/vpx_encoder.h>
|
||||||
|
#include<vpx/vp8cx.h>
|
||||||
|
|
||||||
|
#include<eebie/writer.h>
|
||||||
|
|
||||||
|
#include<assert.h>
|
||||||
|
|
||||||
|
#include"mode.h"
|
||||||
|
|
||||||
|
#include"img.h"
|
||||||
|
#include<math.h>
|
||||||
|
|
||||||
|
#include<smmintrin.h>
|
||||||
|
|
||||||
|
#include<string.h>
|
||||||
|
|
||||||
|
#include"minitrace.h"
|
||||||
|
|
||||||
|
#include"linearity.h"
|
||||||
|
|
||||||
|
#include"yuv.h"
|
||||||
|
|
||||||
|
#include<stdio.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>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NALLENSZ 4
|
||||||
|
|
||||||
|
static size_t annexb_parse(const uint8_t *src, const uint8_t *srcEnd) {
|
||||||
|
int zeros = 0;
|
||||||
|
const uint8_t *src2;
|
||||||
|
for(src2 = src; src2 != srcEnd; src2++) {
|
||||||
|
if(*src2 == 0) {
|
||||||
|
zeros++;
|
||||||
|
} else if((zeros == 2 || zeros == 3) && *src2 == 1) {
|
||||||
|
src2 -= zeros;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
zeros = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return src2 - src;
|
||||||
|
}
|
||||||
|
|
||||||
|
// I want to die.
|
||||||
|
static void get_dimensions_from_extradata(const uint8_t *src, uint16_t *foundWidth, uint16_t *foundHeight) {
|
||||||
|
src += 8;
|
||||||
|
|
||||||
|
int nal_unit = *(src++);
|
||||||
|
int profile_idc = *(src++);
|
||||||
|
int constraints = *(src++);
|
||||||
|
int level_idc = *(src++);
|
||||||
|
|
||||||
|
int constraint_set0_flag = (constraints >> 7) & 1;
|
||||||
|
int constraint_set1_flag = (constraints >> 6) & 1;
|
||||||
|
int constraint_set2_flag = (constraints >> 5) & 1;
|
||||||
|
int constraint_set3_flag = (constraints >> 4) & 1;
|
||||||
|
int constraint_set4_flag = (constraints >> 3) & 1;
|
||||||
|
int constraint_set5_flag = (constraints >> 2) & 1;
|
||||||
|
|
||||||
|
size_t bitoffset = 0;
|
||||||
|
|
||||||
|
#define ZOOOZOOO_BIT(b) do { b = ((src[0] >> (bitoffset = (bitoffset + 7) % 8)) & 1); if(bitoffset == 0) src++; } while(0);
|
||||||
|
#define ZOOOZOOO_READ(out, n) do { out = 0; int b; for(int i_i = 0; i_i < n; i_i++) { ZOOOZOOO_BIT(b); out |= (b << (n - i_i - 1)); } } while(0);
|
||||||
|
#define ZOOOZOOO_UE(out) do { int i = 0; int b = 0; while(1) { ZOOOZOOO_BIT(b); if(b == 1 || i >= 32) break; i++; } ZOOOZOOO_READ(out, i); out += (1 << i) - 1; } while(0);
|
||||||
|
#define ZOOOZOOO_SE(out) do { ZOOOZOOO_UE(out); if(out & 1) { out = (out + 1) / 2; } else { out = -(out / 2); } } while(0);
|
||||||
|
|
||||||
|
int seq_parameter_set_id;
|
||||||
|
ZOOOZOOO_UE(seq_parameter_set_id);
|
||||||
|
|
||||||
|
if(profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || profile_idc == 86 || profile_idc == 118) {
|
||||||
|
int chroma_format_idc;
|
||||||
|
ZOOOZOOO_UE(chroma_format_idc);
|
||||||
|
|
||||||
|
if(chroma_format_idc == 3) {
|
||||||
|
int residual_color_transform_flag;
|
||||||
|
ZOOOZOOO_BIT(residual_color_transform_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bit_depth_luma_minus8;
|
||||||
|
ZOOOZOOO_UE(bit_depth_luma_minus8);
|
||||||
|
|
||||||
|
int bit_depth_chroma_minus8;
|
||||||
|
ZOOOZOOO_UE(bit_depth_chroma_minus8);
|
||||||
|
|
||||||
|
int qpprime_y_zero_transform_bypass_flag;
|
||||||
|
ZOOOZOOO_BIT(qpprime_y_zero_transform_bypass_flag);
|
||||||
|
|
||||||
|
int seq_scaling_matrix_present_flag;
|
||||||
|
ZOOOZOOO_BIT(seq_scaling_matrix_present_flag);
|
||||||
|
|
||||||
|
if(seq_scaling_matrix_present_flag) {
|
||||||
|
int i = 0;
|
||||||
|
int lim = chroma_format_idc != 3 ? 8 : 12;
|
||||||
|
for(i = 0; i < lim; i++) {
|
||||||
|
int seq_scaling_list_present_flag;
|
||||||
|
ZOOOZOOO_BIT(seq_scaling_list_present_flag);
|
||||||
|
|
||||||
|
if(seq_scaling_list_present_flag) {
|
||||||
|
int sizeOfScalingList = (i < 6) ? 16 : 64;
|
||||||
|
int lastScale = 8;
|
||||||
|
int nextScale = 8;
|
||||||
|
int j = 0;
|
||||||
|
for(j = 0; j < sizeOfScalingList; j++) {
|
||||||
|
if(nextScale != 0) {
|
||||||
|
int delta_scale;
|
||||||
|
ZOOOZOOO_SE(delta_scale);
|
||||||
|
|
||||||
|
nextScale = (lastScale + delta_scale + 256) % 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastScale = (nextScale == 0) ? lastScale : nextScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int log2_max_frame_num_minus4;
|
||||||
|
ZOOOZOOO_UE(log2_max_frame_num_minus4);
|
||||||
|
|
||||||
|
int pic_order_cnt_type;
|
||||||
|
ZOOOZOOO_UE(pic_order_cnt_type);
|
||||||
|
|
||||||
|
if(pic_order_cnt_type == 0) {
|
||||||
|
int log2_max_pic_order_cnt_lsb_minus4;
|
||||||
|
ZOOOZOOO_UE(log2_max_pic_order_cnt_lsb_minus4);
|
||||||
|
} else if(pic_order_cnt_type == 1) {
|
||||||
|
int delta_pic_order_always_zero_flag;
|
||||||
|
ZOOOZOOO_BIT(delta_pic_order_always_zero_flag);
|
||||||
|
|
||||||
|
int offset_for_non_ref_pic;
|
||||||
|
ZOOOZOOO_SE(offset_for_non_ref_pic);
|
||||||
|
|
||||||
|
int offset_for_top_to_bottom_field;
|
||||||
|
ZOOOZOOO_SE(offset_for_top_to_bottom_field);
|
||||||
|
|
||||||
|
int num_ref_frames_in_pic_order_cnt_cycle;
|
||||||
|
ZOOOZOOO_UE(num_ref_frames_in_pic_order_cnt_cycle);
|
||||||
|
|
||||||
|
for(int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
|
||||||
|
int offset_for_ref_frame_i;
|
||||||
|
ZOOOZOOO_SE(offset_for_ref_frame_i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int max_num_ref_frames;
|
||||||
|
ZOOOZOOO_UE(max_num_ref_frames);
|
||||||
|
|
||||||
|
int gaps_in_frame_num_value_allowed_flag;
|
||||||
|
ZOOOZOOO_BIT(gaps_in_frame_num_value_allowed_flag);
|
||||||
|
|
||||||
|
int pic_width_in_mbs_minus1;
|
||||||
|
ZOOOZOOO_UE(pic_width_in_mbs_minus1);
|
||||||
|
|
||||||
|
int pic_height_in_map_units_minus1;
|
||||||
|
ZOOOZOOO_UE(pic_height_in_map_units_minus1);
|
||||||
|
|
||||||
|
int frame_mbs_only_flag;
|
||||||
|
ZOOOZOOO_BIT(frame_mbs_only_flag);
|
||||||
|
|
||||||
|
if(!frame_mbs_only_flag) {
|
||||||
|
int mb_adaptive_frame_field_flag;
|
||||||
|
ZOOOZOOO_BIT(mb_adaptive_frame_field_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
int direct_8x8_inference_flag;
|
||||||
|
ZOOOZOOO_BIT(direct_8x8_inference_flag);
|
||||||
|
|
||||||
|
int frame_cropping_flag;
|
||||||
|
ZOOOZOOO_BIT(frame_cropping_flag);
|
||||||
|
|
||||||
|
int frame_crop_left_offset = 0;
|
||||||
|
int frame_crop_right_offset = 0;
|
||||||
|
int frame_crop_top_offset = 0;
|
||||||
|
int frame_crop_bottom_offset = 0;
|
||||||
|
if(frame_cropping_flag) {
|
||||||
|
ZOOOZOOO_UE(frame_crop_left_offset);
|
||||||
|
ZOOOZOOO_UE(frame_crop_right_offset);
|
||||||
|
ZOOOZOOO_UE(frame_crop_top_offset);
|
||||||
|
ZOOOZOOO_UE(frame_crop_bottom_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int vui_parameters_present_flag;
|
||||||
|
ZOOOZOOO_BIT(vui_parameters_present_flag);
|
||||||
|
|
||||||
|
*foundWidth = ((pic_width_in_mbs_minus1 + 1) * 16) - frame_crop_bottom_offset * 2 - frame_crop_top_offset * 2;
|
||||||
|
*foundHeight = (2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16 - frame_crop_right_offset * 2 - frame_crop_left_offset * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *annexb_to_extradata(const uint8_t *src, const uint8_t *srcEnd, size_t *szRet, size_t *srcSzEnd) {
|
||||||
|
const uint8_t *sps = src;
|
||||||
|
while(*sps == 0) sps++;
|
||||||
|
assert(sps[0] == 1);
|
||||||
|
sps++;
|
||||||
|
|
||||||
|
size_t szSps = annexb_parse(sps, srcEnd);
|
||||||
|
|
||||||
|
const uint8_t *pps = sps + szSps;
|
||||||
|
while(*pps == 0) pps++;
|
||||||
|
assert(pps[0] == 1);
|
||||||
|
pps++;
|
||||||
|
|
||||||
|
size_t szPps = annexb_parse(pps, srcEnd);
|
||||||
|
|
||||||
|
uint8_t *ret = malloc(*szRet = (6 + 2 + szSps + 1 + 2 + szPps));
|
||||||
|
ret[0] = 1;
|
||||||
|
ret[1] = sps[1];
|
||||||
|
ret[2] = sps[2];
|
||||||
|
ret[3] = sps[3];
|
||||||
|
ret[4] = 0xFC | (NALLENSZ - 1);
|
||||||
|
|
||||||
|
ret[5] = 0xE0 | 1;
|
||||||
|
ret[6] = szSps >> 8;
|
||||||
|
ret[7] = szSps & 0xFF;
|
||||||
|
|
||||||
|
memcpy(&ret[8], sps, szSps);
|
||||||
|
|
||||||
|
ret[8 + szSps + 0] = 1;
|
||||||
|
ret[8 + szSps + 1] = szPps >> 8;
|
||||||
|
ret[8 + szSps + 2] = szPps & 0xFF;
|
||||||
|
|
||||||
|
memcpy(&ret[8 + szSps + 3], pps, szPps);
|
||||||
|
|
||||||
|
*srcSzEnd = pps + szPps - src;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *annexb_to_avcc(const uint8_t *src, size_t szSrc, size_t *szRet) {
|
||||||
|
size_t cap = 4096, sz = 0;
|
||||||
|
uint8_t *ret = malloc(cap);
|
||||||
|
|
||||||
|
const uint8_t *srcEnd = src + szSrc;
|
||||||
|
while(src != srcEnd) {
|
||||||
|
assert(*src == 0);
|
||||||
|
while(*src == 0) {
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
assert(*src == 1);
|
||||||
|
src++;
|
||||||
|
|
||||||
|
size_t nalSize = annexb_parse(src, srcEnd);
|
||||||
|
|
||||||
|
size_t additionSz = NALLENSZ + nalSize;
|
||||||
|
|
||||||
|
if(sz + additionSz > cap) {
|
||||||
|
ret = realloc(ret, cap = (sz + additionSz));
|
||||||
|
}
|
||||||
|
|
||||||
|
*(uint32_t*) &ret[sz] = htonl(nalSize);
|
||||||
|
memcpy(&ret[sz + NALLENSZ], src, nalSize);
|
||||||
|
|
||||||
|
sz += additionSz;
|
||||||
|
src += nalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
*szRet = sz;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Internal {
|
||||||
|
CHiPubNode pub;
|
||||||
|
|
||||||
|
FILE *fd;
|
||||||
|
EBMLWriter wr;
|
||||||
|
|
||||||
|
size_t videoTrack, audioTrack;
|
||||||
|
|
||||||
|
size_t currentClusterTimecode;
|
||||||
|
bool beforeFirstCluster;
|
||||||
|
bool codecPrivatesFound;
|
||||||
|
|
||||||
|
CHiBSFrames *audioBacklog;
|
||||||
|
CHiBSFrames *videoBacklog;
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t next_timestamp(struct Internal *this) {
|
||||||
|
if(this->videoBacklog && this->videoBacklog->count > 0 && (!this->audioBacklog || (this->audioBacklog->count > 0 && this->videoBacklog->data[0].timestamp <= this->audioBacklog->data[0].timestamp))) {
|
||||||
|
return this->videoBacklog->data[0].timestamp;
|
||||||
|
} else if(this->audioBacklog && this->audioBacklog->count > 0 && (!this->videoBacklog || (this->videoBacklog->count > 0 && this->audioBacklog->data[0].timestamp <= this->videoBacklog->data[0].timestamp))) {
|
||||||
|
return this->audioBacklog->data[0].timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int muxmkv_perform(CHiPubNode *pubn) {
|
||||||
|
MTR_BEGIN("CHi", "muxmkv_perform");
|
||||||
|
|
||||||
|
struct Internal *this = (void*) pubn;
|
||||||
|
|
||||||
|
if(pubn->sinks[0].linked.to) {
|
||||||
|
CHiBSFrames *vp9bs = CHi_Crawl(&pubn->sinks[0])->data.bitstream;
|
||||||
|
if(vp9bs) {
|
||||||
|
this->videoBacklog = CHi_BS_Combine(this->videoBacklog, vp9bs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pubn->sinks[1].linked.to) {
|
||||||
|
CHiBSFrames *opus = CHi_Crawl(&pubn->sinks[1])->data.bitstream;
|
||||||
|
if(opus) {
|
||||||
|
this->audioBacklog = CHi_BS_Combine(this->audioBacklog, opus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->codecPrivatesFound) {
|
||||||
|
while(
|
||||||
|
(!this->audioBacklog || this->audioBacklog->count > 0) &&
|
||||||
|
(!this->videoBacklog || this->videoBacklog->count > 0)) {
|
||||||
|
|
||||||
|
size_t nextTimestamp = next_timestamp(this);
|
||||||
|
|
||||||
|
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(!this->beforeFirstCluster) {
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
}
|
||||||
|
this->beforeFirstCluster = false;
|
||||||
|
|
||||||
|
// Cluster
|
||||||
|
ebml_writer_push(&this->wr, 0x1F43B675);
|
||||||
|
|
||||||
|
this->currentClusterTimecode = nextTimestamp;
|
||||||
|
|
||||||
|
// Timecode
|
||||||
|
ebml_writer_put(&this->wr, 0xE7, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = this->currentClusterTimecode});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->videoBacklog && this->videoBacklog->count && (!this->audioBacklog || (this->audioBacklog->count > 0 && this->videoBacklog->data[0].timestamp <= this->audioBacklog->data[0].timestamp))) {
|
||||||
|
CHiBSFrame *frame = &this->videoBacklog->data[0];
|
||||||
|
|
||||||
|
assert((frame->flags & CUTIHI_BS_SETUP_PACKET) == 0);
|
||||||
|
|
||||||
|
bool freeAvcc = false;
|
||||||
|
size_t avccSz = 0;
|
||||||
|
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;
|
||||||
|
|
||||||
|
uint8_t *simpleBlock = malloc(simpleBlockSize);
|
||||||
|
simpleBlock[0] = 0x80 | this->videoTrack;
|
||||||
|
*(uint16_t*) &simpleBlock[1] = __builtin_bswap16(frame->timestamp - this->currentClusterTimecode);
|
||||||
|
simpleBlock[3] = (frame->flags & CUTIHI_BS_FLAG_KEY) ? 128 : 0;
|
||||||
|
|
||||||
|
memcpy(simpleBlock + 4, avcc, avccSz);
|
||||||
|
|
||||||
|
// SimpleBlock
|
||||||
|
ebml_writer_put(&this->wr, 0xA3, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = simpleBlockSize, .ptr = simpleBlock}});
|
||||||
|
|
||||||
|
free(simpleBlock);
|
||||||
|
|
||||||
|
if(freeAvcc) {
|
||||||
|
free(avcc);
|
||||||
|
}
|
||||||
|
|
||||||
|
CHi_BS_Pop(this->videoBacklog, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->audioBacklog && this->audioBacklog->count && (!this->videoBacklog || (this->videoBacklog->count > 0 && this->audioBacklog->data[0].timestamp <= this->videoBacklog->data[0].timestamp))) {
|
||||||
|
CHiBSFrame *frame = &this->audioBacklog->data[0];
|
||||||
|
|
||||||
|
assert((frame->flags & CUTIHI_BS_SETUP_PACKET) == 0);
|
||||||
|
|
||||||
|
size_t simpleBlockSize = 4 + frame->sz;
|
||||||
|
|
||||||
|
uint8_t *simpleBlock = malloc(simpleBlockSize);
|
||||||
|
simpleBlock[0] = 0x80 | this->audioTrack;
|
||||||
|
*(uint16_t*) &simpleBlock[1] = __builtin_bswap16(frame->timestamp - this->currentClusterTimecode);
|
||||||
|
simpleBlock[3] = (frame->flags & CUTIHI_BS_FLAG_KEY) ? 128 : 0;
|
||||||
|
|
||||||
|
memcpy(simpleBlock + 4, frame->ptr, frame->sz);
|
||||||
|
|
||||||
|
// SimpleBlock
|
||||||
|
ebml_writer_put(&this->wr, 0xA3, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = simpleBlockSize, .ptr = simpleBlock}});
|
||||||
|
|
||||||
|
free(simpleBlock);
|
||||||
|
|
||||||
|
CHi_BS_Pop(this->audioBacklog, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if((!this->audioBacklog || this->audioBacklog->count) && (!this->videoBacklog || this->videoBacklog->count)) {
|
||||||
|
uint8_t randbuf[16];
|
||||||
|
|
||||||
|
// Segment
|
||||||
|
ebml_writer_push_ind(&this->wr, 0x18538067);
|
||||||
|
|
||||||
|
// Info
|
||||||
|
ebml_writer_push(&this->wr, 0x1549A966);
|
||||||
|
// SegmentUUID
|
||||||
|
getrandom(randbuf, sizeof(randbuf), 0);
|
||||||
|
ebml_writer_put(&this->wr, 0x73A4, EBML_BINARY, (EBMLPrimitive) {.binary = {.ptr = randbuf, .length = sizeof(randbuf)}});
|
||||||
|
|
||||||
|
// TimestampScale
|
||||||
|
ebml_writer_put(&this->wr, 0x2AD7B1, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 1000000});
|
||||||
|
|
||||||
|
// MuxingApp
|
||||||
|
ebml_writer_put(&this->wr, 0x4D80, EBML_STRING, (EBMLPrimitive) {.string = "cuticle"});
|
||||||
|
|
||||||
|
// WritingApp
|
||||||
|
ebml_writer_put(&this->wr, 0x5741, EBML_STRING, (EBMLPrimitive) {.string = "cuticle"});
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
|
||||||
|
// Tracks
|
||||||
|
ebml_writer_push(&this->wr, 0x1654AE6B);
|
||||||
|
if(this->videoBacklog) {
|
||||||
|
// TrackEntry
|
||||||
|
ebml_writer_push(&this->wr, 0xAE);
|
||||||
|
// TrackNumber
|
||||||
|
ebml_writer_put(&this->wr, 0xD7, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = this->videoTrack});
|
||||||
|
|
||||||
|
// TrackUID
|
||||||
|
getrandom(randbuf, sizeof(uint64_t), 0);
|
||||||
|
ebml_writer_put(&this->wr, 0x73C5, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = *(uint64_t*) &randbuf[0]});
|
||||||
|
|
||||||
|
// TrackType
|
||||||
|
ebml_writer_put(&this->wr, 0x83, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 1});
|
||||||
|
|
||||||
|
// FlagLacing
|
||||||
|
ebml_writer_put(&this->wr, 0x9C, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 0});
|
||||||
|
|
||||||
|
CHiBSFrame *setup = &this->videoBacklog->data[0];
|
||||||
|
|
||||||
|
assert(setup->flags & CUTIHI_BS_SETUP_PACKET);
|
||||||
|
|
||||||
|
uint16_t width = 0, height = 0;
|
||||||
|
|
||||||
|
if(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_VP8BS) {
|
||||||
|
ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "V_VP8"});
|
||||||
|
|
||||||
|
CHiVPxBSSettings *settings = setup->ptr;
|
||||||
|
width = settings->width;
|
||||||
|
height = settings->height;
|
||||||
|
|
||||||
|
setup = NULL;
|
||||||
|
CHi_BS_Pop(this->videoBacklog, 1);
|
||||||
|
} else if(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_VP9BS) {
|
||||||
|
ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "V_VP9"});
|
||||||
|
|
||||||
|
CHiVPxBSSettings *settings = setup->ptr;
|
||||||
|
width = settings->width;
|
||||||
|
height = settings->height;
|
||||||
|
|
||||||
|
setup = NULL;
|
||||||
|
CHi_BS_Pop(this->videoBacklog, 1);
|
||||||
|
} else if(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_H264BS) {
|
||||||
|
ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "V_MPEG4/ISO/AVC"});
|
||||||
|
|
||||||
|
size_t szRet, srcSzEnd;
|
||||||
|
uint8_t *extradata = annexb_to_extradata(setup->ptr, setup->ptr + setup->sz, &szRet, &srcSzEnd);
|
||||||
|
|
||||||
|
// CodecPrivate
|
||||||
|
ebml_writer_put(&this->wr, 0x63A2, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = szRet, .ptr = extradata}});
|
||||||
|
|
||||||
|
get_dimensions_from_extradata(extradata, &width, &height);
|
||||||
|
|
||||||
|
free(extradata);
|
||||||
|
|
||||||
|
// Frame still contains keyframe because of legacy crap
|
||||||
|
memmove(setup->ptr, setup->ptr + srcSzEnd, setup->sz - srcSzEnd);
|
||||||
|
setup->sz -= srcSzEnd;
|
||||||
|
setup->flags &= ~CUTIHI_BS_SETUP_PACKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(width > 0 && height > 0);
|
||||||
|
|
||||||
|
// Video
|
||||||
|
ebml_writer_push(&this->wr, 0xE0);
|
||||||
|
// PixelWidth
|
||||||
|
ebml_writer_put(&this->wr, 0xB0, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = width});
|
||||||
|
|
||||||
|
// PixelHeight
|
||||||
|
ebml_writer_put(&this->wr, 0xBA, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = height});
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->audioBacklog) {
|
||||||
|
// TrackEntry
|
||||||
|
ebml_writer_push(&this->wr, 0xAE);
|
||||||
|
// TrackNumber
|
||||||
|
ebml_writer_put(&this->wr, 0xD7, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = this->audioTrack});
|
||||||
|
|
||||||
|
// TrackUID
|
||||||
|
getrandom(randbuf, sizeof(uint64_t), 0);
|
||||||
|
ebml_writer_put(&this->wr, 0x73C5, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = *(uint64_t*) &randbuf[0]});
|
||||||
|
|
||||||
|
// TrackType
|
||||||
|
ebml_writer_put(&this->wr, 0x83, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 2});
|
||||||
|
|
||||||
|
// FlagLacing
|
||||||
|
ebml_writer_put(&this->wr, 0x9C, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 0});
|
||||||
|
|
||||||
|
assert(this->audioBacklog->data[0].flags & CUTIHI_BS_SETUP_PACKET);
|
||||||
|
|
||||||
|
if(CHi_Crawl(&pubn->sinks[1])->type == CUTIHI_VAL_OPUSBS) {
|
||||||
|
ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "A_OPUS"});
|
||||||
|
|
||||||
|
CHiBSFrame *opusHead = &this->audioBacklog->data[0];
|
||||||
|
|
||||||
|
// CodecPrivate
|
||||||
|
ebml_writer_put(&this->wr, 0x63A2, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = opusHead->sz, .ptr = opusHead->ptr}});
|
||||||
|
} 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"});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
ebml_writer_push(&this->wr, 0xE1);
|
||||||
|
// SamplingFrequency
|
||||||
|
ebml_writer_put(&this->wr, 0xB0, EBML_FLOAT4, (EBMLPrimitive) {.flt4 = 48000});
|
||||||
|
|
||||||
|
// Channels
|
||||||
|
ebml_writer_put(&this->wr, 0x9F, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 1});
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
|
||||||
|
CHi_BS_Pop(this->audioBacklog, 1);
|
||||||
|
}
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
|
||||||
|
this->codecPrivatesFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MTR_END("CHi", "muxmkv_perform");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
static void muxmkv_destroy(CHiPubNode *pubn) {
|
||||||
|
struct Internal *this = (void*) pubn;
|
||||||
|
|
||||||
|
CHi_BS_Free(this->audioBacklog);
|
||||||
|
this->audioBacklog = NULL;
|
||||||
|
|
||||||
|
CHi_BS_Free(this->videoBacklog);
|
||||||
|
this->videoBacklog = NULL;
|
||||||
|
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
static size_t ebml_write(EBMLWriter *this, const void *data, size_t length) {
|
||||||
|
return fwrite(data, 1, length, this->ud);
|
||||||
|
}
|
||||||
|
static void *ebml_alloc(EBMLWriter *this, void *data, size_t length) {
|
||||||
|
return realloc(data, length);
|
||||||
|
}
|
||||||
|
static int muxmkv_start(CHiPubNode *pubn) {
|
||||||
|
struct Internal *this = (void*) pubn;
|
||||||
|
|
||||||
|
this->currentClusterTimecode = 0;
|
||||||
|
this->codecPrivatesFound = false;
|
||||||
|
this->beforeFirstCluster = true;
|
||||||
|
|
||||||
|
int trackNum = 1;
|
||||||
|
|
||||||
|
if(pubn->sinks[0].linked.to) {
|
||||||
|
this->videoBacklog = CHi_BS_Empty();
|
||||||
|
this->videoTrack = trackNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pubn->sinks[1].linked.to) {
|
||||||
|
this->audioBacklog = CHi_BS_Empty();
|
||||||
|
this->audioTrack = trackNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->fd = fopen(CHi_Crawl(&pubn->sinks[CUTIHI_MUXWEBM_IN_FILENAME])->data.text, "wb");
|
||||||
|
|
||||||
|
if(!this->fd) {
|
||||||
|
CHi_AddError(pubn, "invalid file", CUTIHI_MUXWEBM_IN_FILENAME);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ebml_writer_init(&this->wr);
|
||||||
|
|
||||||
|
this->wr.ud = this->fd;
|
||||||
|
this->wr.eventWrite = ebml_write;
|
||||||
|
this->wr.eventAlloc = ebml_alloc;
|
||||||
|
|
||||||
|
ebml_writer_push(&this->wr, 0x1A45DFA3);
|
||||||
|
ebml_writer_put(&this->wr, 0x4282, EBML_STRING, (EBMLPrimitive) {.string = "matroska"});
|
||||||
|
ebml_writer_put(&this->wr, 0x42F2, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 4});
|
||||||
|
ebml_writer_put(&this->wr, 0x42F3, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 8});
|
||||||
|
ebml_writer_put(&this->wr, 0x4287, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 2});
|
||||||
|
ebml_writer_put(&this->wr, 0x4285, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 2});
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
static int muxmkv_stop(CHiPubNode *pubn) {
|
||||||
|
struct Internal *this = (void*) pubn;
|
||||||
|
|
||||||
|
while(this->wr.currentDepth) {
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(!ebml_writer_flush(&this->wr));
|
||||||
|
|
||||||
|
ebml_writer_free(&this->wr);
|
||||||
|
|
||||||
|
fclose(this->fd);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
CUTIVIS CHiPubNode *CHi_MuxMatroska() {
|
||||||
|
struct Internal *n = calloc(1, sizeof(*n));
|
||||||
|
n->pub.type = CUTIHI_T('CExp','Mkv ');
|
||||||
|
n->pub.Start = muxmkv_start;
|
||||||
|
n->pub.Perform = muxmkv_perform;
|
||||||
|
n->pub.Destroy = muxmkv_destroy;
|
||||||
|
n->pub.Stop = muxmkv_stop;
|
||||||
|
n->pub.sinks = calloc(sizeof(*n->pub.sinks), n->pub.sinkCount = 3);
|
||||||
|
n->pub.sourceCount = 0;
|
||||||
|
n->pub.sources = NULL;
|
||||||
|
return &n->pub;
|
||||||
|
}
|
||||||
657
hi/node.c
657
hi/node.c
@@ -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>
|
||||||
@@ -19,55 +17,10 @@
|
|||||||
|
|
||||||
#include"linearity.h"
|
#include"linearity.h"
|
||||||
|
|
||||||
static size_t bisect(const void *key, const void *base, size_t nmemb, size_t size, ssize_t(*compar)(const void*, const void*)) {
|
#include"node_internal.h"
|
||||||
size_t low = 0, high = nmemb;
|
|
||||||
|
|
||||||
while(low < high) {
|
|
||||||
size_t middle = (low + high) / 2;
|
|
||||||
if(compar((const void*) ((uintptr_t) base + size * middle), key) < 0) {
|
|
||||||
low = middle + 1;
|
|
||||||
} else {
|
|
||||||
high = middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return low;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t float_compar(const void *A, const void *B) {
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
float a = *(float*) A;
|
#include<stb_image.h>
|
||||||
float b = *(float*) B;
|
|
||||||
return (a > b) - (a < b);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int adjacencycmp(const void *a, const void *b) {
|
|
||||||
size_t v = (uintptr_t) ((CHiAdjacency*) a)[0] - (uintptr_t) ((CHiAdjacency*) b)[0];
|
|
||||||
return v ? v : (uintptr_t) ((CHiAdjacency*) a)[1] - (uintptr_t) ((CHiAdjacency*) b)[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void adjacency_add(CHiPubNode *source, CHiPubNode *sink) {
|
|
||||||
CHiNodeGraph *ng = source->ng;
|
|
||||||
|
|
||||||
if(ng->adjacencyCount == ng->adjacencyCapacity) {
|
|
||||||
ng->adjacencies = realloc(ng->adjacencies, sizeof(CHiAdjacency) * (ng->adjacencyCapacity *= 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
ng->adjacencies[ng->adjacencyCount][0] = source;
|
|
||||||
ng->adjacencies[ng->adjacencyCount][1] = sink;
|
|
||||||
ng->adjacencyCount++;
|
|
||||||
|
|
||||||
qsort(ng->adjacencies, ng->adjacencyCount, sizeof(CHiAdjacency), adjacencycmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void adjacency_remove(CHiPubNode *source, CHiPubNode *sink) {
|
|
||||||
CHiNodeGraph *ng = source->ng;
|
|
||||||
|
|
||||||
CHiAdjacency *adj = bsearch(&(CHiAdjacency) {source, sink}, ng->adjacencies, ng->adjacencyCount, sizeof(CHiAdjacency), adjacencycmp);
|
|
||||||
if(adj) {
|
|
||||||
memmove(adj, adj + 1, sizeof(CHiAdjacency) * (ng->adjacencyCount - (adj - ng->adjacencies) - 1));
|
|
||||||
ng->adjacencyCount--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CUTIVIS CHiNodeGraph *CHi_NewNodeGraph() {
|
CUTIVIS CHiNodeGraph *CHi_NewNodeGraph() {
|
||||||
static int inited = 0;
|
static int inited = 0;
|
||||||
@@ -125,9 +78,9 @@ CUTIVIS CHiNodeGraph *CHi_NodeGraphReset(CHiNodeGraph *ng) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CUTIVIS CHiValue *CHi_Crawl(CHiValue *v) {
|
CUTIVIS CHiValue *CHi_Crawl(CHiValue *v) {
|
||||||
while(v->type == CUTIHI_VAL_LINKED || v->type == CUTIHI_VAL_KEYED) {
|
while(v->linked.to || v->type == CUTIHI_VAL_KEYED) {
|
||||||
if(v->type == CUTIHI_VAL_LINKED) {
|
if(v->linked.to) {
|
||||||
v = &v->data.linked.to->sources[v->data.linked.idx];
|
v = &v->linked.to->sources[v->linked.idx];
|
||||||
} else if(v->type == CUTIHI_VAL_KEYED) {
|
} else if(v->type == CUTIHI_VAL_KEYED) {
|
||||||
v = &v->data.keyed->current;
|
v = &v->data.keyed->current;
|
||||||
}
|
}
|
||||||
@@ -163,8 +116,8 @@ static int dfs_visit(size_t *resultCount, CHiPubNode ***result, CHiPubNode *n) {
|
|||||||
n->_dfsmark = 1;
|
n->_dfsmark = 1;
|
||||||
|
|
||||||
for(size_t s = 0; s < n->sinkCount; s++) {
|
for(size_t s = 0; s < n->sinkCount; s++) {
|
||||||
if(n->sinks[s].type == CUTIHI_VAL_LINKED) {
|
if(n->sinks[s].linked.to) {
|
||||||
if(!dfs_visit(resultCount, result, n->sinks[s].data.linked.to)) {
|
if(!dfs_visit(resultCount, result, n->sinks[s].linked.to)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,28 +162,34 @@ CUTIVIS int CHi_ConfigureSink(CHiPubNode *n, size_t i, CHiValue v) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(v.type == CUTIHI_VAL_LINKED && n == v.data.linked.to) return 0;
|
if(v.linked.to && n == v.linked.to) return 0;
|
||||||
|
|
||||||
CHiValue old = n->sinks[i];
|
CHiValue old = n->sinks[i];
|
||||||
|
|
||||||
if(old.type == CUTIHI_VAL_LINKED) {
|
if(old.linked.to) {
|
||||||
adjacency_remove(old.data.linked.to, n);
|
adjacency_remove(old.linked.to, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(v.linked.to) {
|
||||||
|
// Overwrite only the link-related things to keep the old values in case the link is removed
|
||||||
|
n->sinks[i].linked = v.linked;
|
||||||
|
} else {
|
||||||
|
n->sinks[i] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if viable
|
// Check if viable
|
||||||
n->sinks[i] = v;
|
|
||||||
if(n->ng && !topological_sort(n->ng)) {
|
if(n->ng && !topological_sort(n->ng)) {
|
||||||
n->sinks[i] = old;
|
n->sinks[i] = old;
|
||||||
|
|
||||||
if(old.type == CUTIHI_VAL_LINKED) {
|
if(old.linked.to) {
|
||||||
adjacency_add(old.data.linked.to, n);
|
adjacency_add(old.linked.to, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(v.type == CUTIHI_VAL_LINKED) {
|
if(v.linked.to) {
|
||||||
adjacency_add(v.data.linked.to, n);
|
adjacency_add(v.linked.to, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
CHi_MakeDirty(n->ng, n);
|
CHi_MakeDirty(n->ng, n);
|
||||||
@@ -351,22 +310,6 @@ CUTIVIS void CHi_SetDuration(CHiNodeGraph *ng, float d) {
|
|||||||
ng->duration = d;
|
ng->duration = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
CUTIVIS int CHi_Hysteresis(CHiPubNode *root) {
|
|
||||||
if(root->ng->compilationStatus != CUTIHI_COMP_READY) return 0;
|
|
||||||
|
|
||||||
for(size_t s = 0; s < root->sinkCount; s++) {
|
|
||||||
if(root->sinks[s].type == CUTIHI_VAL_LINKED) {
|
|
||||||
CHi_Hysteresis(root->sinks[s].data.linked.to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if(!root->clean) {
|
|
||||||
root->Perform(root);
|
|
||||||
//}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool error_changes(CHiPubNode *n) {
|
static bool error_changes(CHiPubNode *n) {
|
||||||
for(int e = 0; e < CUTIHI_MAX_ERRORS; e++) {
|
for(int e = 0; e < CUTIHI_MAX_ERRORS; e++) {
|
||||||
if(n->errors.active[e] != n->errors.activeLast[e]) {
|
if(n->errors.active[e] != n->errors.activeLast[e]) {
|
||||||
@@ -384,9 +327,7 @@ static void save_errors(CHiPubNode *n) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void perform_step(CHiNodeGraph *ng) {
|
CUTIVIS int CHi_Hysteresis(CHiNodeGraph *ng) {
|
||||||
pthread_mutex_lock(&ng->mut);
|
|
||||||
|
|
||||||
for(size_t nIdx = 0; nIdx < ng->count; nIdx++) {
|
for(size_t nIdx = 0; nIdx < ng->count; nIdx++) {
|
||||||
save_errors(ng->nodes[nIdx]);
|
save_errors(ng->nodes[nIdx]);
|
||||||
}
|
}
|
||||||
@@ -394,14 +335,25 @@ static void perform_step(CHiNodeGraph *ng) {
|
|||||||
for(size_t nIdx = 0; nIdx < ng->count; nIdx++) {
|
for(size_t nIdx = 0; nIdx < ng->count; nIdx++) {
|
||||||
CHiPubNode *n = ng->nodes[nIdx];
|
CHiPubNode *n = ng->nodes[nIdx];
|
||||||
|
|
||||||
n->Perform(n);
|
// The existence of n->Start implies the node has external side-effects and should not be executed
|
||||||
|
if(ng->compilationStatus == CUTIHI_COMP_RUNNING || n->Start == NULL) {
|
||||||
if(error_changes(n)) {
|
n->Perform(n);
|
||||||
if(ng->eventOnError)
|
|
||||||
ng->eventOnError(ng, n);
|
if(error_changes(n)) {
|
||||||
|
if(ng->eventOnError)
|
||||||
|
ng->eventOnError(ng, n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void perform_step(CHiNodeGraph *ng) {
|
||||||
|
pthread_mutex_lock(&ng->mut);
|
||||||
|
|
||||||
|
CHi_Hysteresis(ng);
|
||||||
|
|
||||||
if(ng->eventOnFrameComplete) {
|
if(ng->eventOnFrameComplete) {
|
||||||
ng->eventOnFrameComplete(ng);
|
ng->eventOnFrameComplete(ng);
|
||||||
}
|
}
|
||||||
@@ -570,63 +522,56 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(node->sinks[CUTIHI_IMAGE_IN_FILE].type != CUTIHI_VAL_TEXT) {
|
if(node->sinks[CUTIHI_IMAGE_IN_FILE].type != CUTIHI_VAL_TEXT) {
|
||||||
node->errors.active[0] = true;
|
CHi_AddError(node, "invalid type", 0);
|
||||||
strncpy(node->errors.code[0], "invalid type", CUTIHI_ERR_SIZE);
|
|
||||||
node->errors.sink[0] = CUTIHI_IMAGE_IN_FILE;
|
|
||||||
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);
|
||||||
node->errors.active[0] = true;
|
if(!data) {
|
||||||
strncpy(node->errors.code[0], "invalid file", CUTIHI_ERR_SIZE);
|
CHi_AddError(node, "invalid file", 0);
|
||||||
node->errors.sink[0] = CUTIHI_IMAGE_IN_FILE;
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -634,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,32 +633,42 @@ 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];
|
||||||
|
|
||||||
CHiImage *img = CHi_Image_New(2, 4, 8 * w, w, h, NULL);
|
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);
|
||||||
if(CHi_Node_Active(node)) {
|
if(CHi_Node_Active(node)) {
|
||||||
for(size_t i = 0; i < w * h; i++) {
|
for(size_t y = 0; y < h; y++) {
|
||||||
img->data16[i * 4 + 0] = sink->data.vec4[2] * 65535;
|
for(size_t x = 0; x < w; x++) {
|
||||||
img->data16[i * 4 + 1] = sink->data.vec4[1] * 65535;
|
img->data16[y * img->stride / 2 + x * 4 + 0] = color->data.vec4[2] * 65535;
|
||||||
img->data16[i * 4 + 2] = sink->data.vec4[0] * 65535;
|
img->data16[y * img->stride / 2 + x * 4 + 1] = color->data.vec4[1] * 65535;
|
||||||
img->data16[i * 4 + 3] = 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node->sources->data.sample = img;
|
node->sources->data.sample = img;
|
||||||
@@ -726,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;
|
||||||
}
|
}
|
||||||
@@ -764,9 +709,7 @@ static int modulate_perform(CHiPubNode *node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(imgsrc->type != CUTIHI_VAL_SAMPLE) {
|
if(imgsrc->type != CUTIHI_VAL_SAMPLE) {
|
||||||
node->errors.active[0] = true;
|
CHi_AddError(node, "invalid type", 0);
|
||||||
strncpy(node->errors.code[0], "invalid type", CUTIHI_ERR_SIZE);
|
|
||||||
node->errors.sink[0] = 0;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -775,11 +718,15 @@ static int modulate_perform(CHiPubNode *node) {
|
|||||||
|
|
||||||
if(!CHi_Node_Active(node)) {
|
if(!CHi_Node_Active(node)) {
|
||||||
node->sources->data.sample = NULL;
|
node->sources->data.sample = NULL;
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHiImage *src = imgsrc->data.sample;
|
CHiImage *src = imgsrc->data.sample;
|
||||||
|
|
||||||
|
if(!src) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
assert(src->stride % 16 == 0);
|
assert(src->stride % 16 == 0);
|
||||||
|
|
||||||
CHiImage *dst = CHi_Image_New(2, 4, src->stride, src->width, src->height, NULL);
|
CHiImage *dst = CHi_Image_New(2, 4, src->stride, src->width, src->height, NULL);
|
||||||
@@ -899,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;
|
||||||
@@ -917,49 +859,6 @@ CUTIVIS CHiPubNode *CHi_Modulate() {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_keyed_values(CHiNodeGraph *ng) {
|
|
||||||
for(size_t kfsIdx = 0; kfsIdx < ng->keyframesList.count; kfsIdx++) {
|
|
||||||
CHiKeyframes *kfs = ng->keyframesList.keyframes[kfsIdx];
|
|
||||||
|
|
||||||
kfs->current.type = kfs->type;
|
|
||||||
|
|
||||||
float now = ng->time;
|
|
||||||
|
|
||||||
size_t idx = bisect(&now, kfs->times, kfs->count, sizeof(now), float_compar);
|
|
||||||
|
|
||||||
if(idx == 0) {
|
|
||||||
kfs->current.data = kfs->values[idx];
|
|
||||||
|
|
||||||
if(kfs->current.type == CUTIHI_VAL_VEC4 && kfs->extrapolationMode == CUTIHI_EXTRAPOLATION_CONSTANT) {
|
|
||||||
kfs->current.data.vec4[0] += (now - kfs->times[0]) * kfs->extrapolationParameter[0];
|
|
||||||
kfs->current.data.vec4[1] += (now - kfs->times[0]) * kfs->extrapolationParameter[1];
|
|
||||||
kfs->current.data.vec4[2] += (now - kfs->times[0]) * kfs->extrapolationParameter[2];
|
|
||||||
kfs->current.data.vec4[3] += (now - kfs->times[0]) * kfs->extrapolationParameter[3];
|
|
||||||
}
|
|
||||||
} else if(idx == kfs->count) {
|
|
||||||
kfs->current.data = kfs->values[idx - 1];
|
|
||||||
|
|
||||||
if(kfs->current.type == CUTIHI_VAL_VEC4 && kfs->extrapolationMode == CUTIHI_EXTRAPOLATION_CONSTANT) {
|
|
||||||
kfs->current.data.vec4[0] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[0];
|
|
||||||
kfs->current.data.vec4[1] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[1];
|
|
||||||
kfs->current.data.vec4[2] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[2];
|
|
||||||
kfs->current.data.vec4[3] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[3];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(kfs->type == CUTIHI_VAL_VEC4) {
|
|
||||||
float alpha = (now - kfs->times[idx - 1]) / (kfs->times[idx] - kfs->times[idx - 1]);
|
|
||||||
|
|
||||||
kfs->current.data.vec4[0] = kfs->values[idx - 1].vec4[0] + (kfs->values[idx].vec4[0] - kfs->values[idx - 1].vec4[0]) * alpha;
|
|
||||||
kfs->current.data.vec4[1] = kfs->values[idx - 1].vec4[1] + (kfs->values[idx].vec4[1] - kfs->values[idx - 1].vec4[1]) * alpha;
|
|
||||||
kfs->current.data.vec4[2] = kfs->values[idx - 1].vec4[2] + (kfs->values[idx].vec4[2] - kfs->values[idx - 1].vec4[2]) * alpha;
|
|
||||||
kfs->current.data.vec4[3] = kfs->values[idx - 1].vec4[3] + (kfs->values[idx].vec4[3] - kfs->values[idx - 1].vec4[3]) * alpha;
|
|
||||||
} else {
|
|
||||||
kfs->current.data = kfs->values[idx - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int time_perform(CHiPubNode *node) {
|
static int time_perform(CHiPubNode *node) {
|
||||||
node->sources->type = CUTIHI_VAL_VEC4;
|
node->sources->type = CUTIHI_VAL_VEC4;
|
||||||
node->sources->data.vec4[0] = node->ng->time;
|
node->sources->data.vec4[0] = node->ng->time;
|
||||||
@@ -979,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1179,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]);
|
||||||
|
|
||||||
@@ -1193,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);
|
||||||
@@ -1284,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;
|
||||||
@@ -1300,279 +1201,75 @@ CUTIVIS CHiPubNode *CHi_ChromaKey() {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void save_chival(CHiNodeGraph *ng, CHiSaveWriter writer, CHiValType type, CHiValueRaw data, void *ud) {
|
|
||||||
if(type == CUTIHI_VAL_TEXT) {
|
|
||||||
size_t len = strlen(data.text);
|
|
||||||
writer(ud, &(uint32_t) {len}, sizeof(uint32_t));
|
|
||||||
writer(ud, data.text, len);
|
|
||||||
} else if(type == CUTIHI_VAL_VEC4) {
|
|
||||||
writer(ud, data.vec4, sizeof(data.vec4));
|
|
||||||
} else if(type == CUTIHI_VAL_LINKED) {
|
|
||||||
size_t index;
|
|
||||||
for(index = 0; index < ng->count; index++) {
|
|
||||||
if(ng->nodes[index] == data.linked.to) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(index < ng->count);
|
|
||||||
|
|
||||||
writer(ud, &(uint64_t) {index}, sizeof(uint64_t));
|
|
||||||
writer(ud, &(uint16_t) {data.linked.idx}, sizeof(uint16_t));
|
|
||||||
} else if(type == CUTIHI_VAL_KEYED) {
|
|
||||||
size_t index;
|
|
||||||
for(index = 0; index < ng->keyframesList.count; index++) {
|
|
||||||
if(ng->keyframesList.keyframes[index] == data.keyed) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(index < ng->count);
|
|
||||||
|
|
||||||
writer(ud, &(uint64_t) {index}, sizeof(uint64_t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void load_chival(CHiNodeGraph *ng, CHiLoadReader reader, CHiValType type, CHiValueRaw *data, void *ud) {
|
|
||||||
if(type == CUTIHI_VAL_TEXT) {
|
|
||||||
uint32_t len;
|
|
||||||
reader(ud, &len, sizeof(len));
|
|
||||||
|
|
||||||
data->text = malloc(len + 1);
|
|
||||||
|
|
||||||
reader(ud, data->text, len);
|
|
||||||
|
|
||||||
data->text[len] = 0;
|
|
||||||
} else if(type == CUTIHI_VAL_VEC4) {
|
|
||||||
reader(ud, data->vec4, sizeof(data->vec4));
|
|
||||||
} else if(type == CUTIHI_VAL_LINKED) {
|
|
||||||
uint64_t index;
|
|
||||||
reader(ud, &index, sizeof(index));
|
|
||||||
|
|
||||||
data->linked.to = ng->nodes[index];
|
|
||||||
|
|
||||||
uint16_t idx;
|
|
||||||
reader(ud, &idx, sizeof(idx));
|
|
||||||
|
|
||||||
data->linked.idx = idx;
|
|
||||||
} else if(type == CUTIHI_VAL_KEYED) {
|
|
||||||
uint64_t index;
|
|
||||||
reader(ud, &index, sizeof(index));
|
|
||||||
|
|
||||||
data->keyed = ng->keyframesList.keyframes[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CUTIVIS int CHi_NodeGraphSave(CHiNodeGraph *ng, CHiSaveWriter writer, void *ud) {
|
|
||||||
writer(ud, "\x71\x74\xCE\xA0", 4);
|
|
||||||
|
|
||||||
writer(ud, &(float) {ng->duration}, sizeof(float));
|
|
||||||
writer(ud, &(float) {ng->time}, sizeof(float));
|
|
||||||
|
|
||||||
writer(ud, &(uint64_t) {ng->keyframesList.count}, sizeof(uint64_t));
|
|
||||||
|
|
||||||
for(size_t i = 0; i < ng->keyframesList.count; i++) {
|
|
||||||
CHiKeyframes *kfs = ng->keyframesList.keyframes[i];
|
|
||||||
|
|
||||||
writer(ud, &(uint16_t) {kfs->type}, sizeof(uint16_t));
|
|
||||||
|
|
||||||
writer(ud, &(uint64_t) {kfs->count}, sizeof(uint64_t));
|
|
||||||
|
|
||||||
writer(ud, kfs->times, sizeof(*kfs->times) * kfs->count);
|
|
||||||
|
|
||||||
for(size_t k = 0; k < kfs->count; k++) {
|
|
||||||
save_chival(ng, writer, kfs->type, kfs->values[k], ud);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer(ud, &(uint16_t) {kfs->extrapolationMode}, sizeof(uint16_t));
|
|
||||||
writer(ud, kfs->extrapolationParameter, sizeof(kfs->extrapolationParameter));
|
|
||||||
}
|
|
||||||
|
|
||||||
writer(ud, &(uint64_t) {ng->count}, sizeof(uint64_t));
|
|
||||||
|
|
||||||
for(size_t i = 0; i < ng->count; i++) {
|
|
||||||
CHiPubNode *node = ng->nodes[i];
|
|
||||||
|
|
||||||
writer(ud, &(uint64_t) {node->type}, sizeof(uint64_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
for(size_t i = 0; i < ng->count; i++) {
|
|
||||||
CHiPubNode *node = ng->nodes[i];
|
|
||||||
|
|
||||||
if(node->Save) {
|
|
||||||
node->Save(node, ud, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer(ud, &(uint16_t) {node->sinkCount}, sizeof(uint16_t));
|
|
||||||
|
|
||||||
for(size_t sink = 0; sink < node->sinkCount; sink++) {
|
|
||||||
writer(ud, &(uint16_t) {node->sinks[sink].type}, sizeof(uint16_t));
|
|
||||||
|
|
||||||
save_chival(ng, writer, node->sinks[sink].type, node->sinks[sink].data, ud);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CUTIVIS int CHi_NodeGraphLoad(CHiNodeGraph *ng, CHiLoadReader reader, void *ud) {
|
|
||||||
{
|
|
||||||
char magic[4];
|
|
||||||
reader(ud, magic, sizeof(magic));
|
|
||||||
if(memcmp(magic, "\x71\x74\xCE\xA0", 4)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CHi_NodeGraphReset(ng);
|
|
||||||
|
|
||||||
reader(ud, &ng->duration, sizeof(float));
|
|
||||||
reader(ud, &ng->time, sizeof(float));
|
|
||||||
|
|
||||||
{
|
|
||||||
uint64_t count;
|
|
||||||
reader(ud, &count, sizeof(count));
|
|
||||||
ng->keyframesList.count = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(size_t i = 0; i < ng->keyframesList.count; i++) {
|
|
||||||
CHiKeyframes *kfs = ng->keyframesList.keyframes[i] = calloc(1, sizeof(*kfs));
|
|
||||||
|
|
||||||
{
|
|
||||||
uint16_t type;
|
|
||||||
reader(ud, &type, sizeof(type));
|
|
||||||
kfs->type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
uint64_t count;
|
|
||||||
reader(ud, &count, sizeof(count));
|
|
||||||
kfs->count = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
kfs->times = calloc(kfs->count, sizeof(*kfs->times));
|
|
||||||
|
|
||||||
reader(ud, kfs->times, kfs->count * sizeof(*kfs->times));
|
|
||||||
|
|
||||||
for(size_t k = 0; k < kfs->count; k++) {
|
|
||||||
load_chival(ng, reader, kfs->type, &kfs->values[k], ud);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
uint16_t extrap;
|
|
||||||
reader(ud, &extrap, sizeof(extrap));
|
|
||||||
kfs->extrapolationMode = extrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
reader(ud, kfs->extrapolationParameter, sizeof(kfs->extrapolationParameter));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
uint64_t count;
|
|
||||||
reader(ud, &count, sizeof(count));
|
|
||||||
ng->count = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
ng->capacity = ng->count < 8 ? 8 : ng->count;
|
|
||||||
|
|
||||||
ng->nodes = calloc(ng->capacity, sizeof(*ng->nodes));
|
|
||||||
|
|
||||||
for(size_t i = 0; i < ng->count; i++) {
|
|
||||||
uint64_t type;
|
|
||||||
|
|
||||||
reader(ud, &type, sizeof(type));
|
|
||||||
|
|
||||||
CHiPubNode *n = NULL;
|
|
||||||
|
|
||||||
if(type == CUTIHI_T('CPre','view')) {
|
|
||||||
n = CHi_Preview();
|
|
||||||
} else if(type == CUTIHI_T('CMix','er ')) {
|
|
||||||
n = CHi_Mixer();
|
|
||||||
} else if(type == CUTIHI_T('CTex','t ')) {
|
|
||||||
n = CHi_Text();
|
|
||||||
} else if(type == CUTIHI_T('CTim','e ')) {
|
|
||||||
n = CHi_Time();
|
|
||||||
} else if(type == CUTIHI_T('CMod','ulat')) {
|
|
||||||
n = CHi_Modulate();
|
|
||||||
} else if(type == CUTIHI_T('CCns','tCol')) {
|
|
||||||
n = CHi_ConstantSample();
|
|
||||||
} else if(type == CUTIHI_T('CEmb','ed ')) {
|
|
||||||
n = CHi_Embed();
|
|
||||||
} else if(type == CUTIHI_T('CIma','ge ')) {
|
|
||||||
n = CHi_Image();
|
|
||||||
} else if(type == CUTIHI_T('CWin','dow ')) {
|
|
||||||
n = CHi_Window();
|
|
||||||
} else if(type == CUTIHI_T('CInA','udio')) {
|
|
||||||
n = CHi_Microphone();
|
|
||||||
} else if(type == CUTIHI_T('CExp','Wave')) {
|
|
||||||
n = CHi_ExportWav();
|
|
||||||
} else if(type == CUTIHI_T('CMov','ie ')) {
|
|
||||||
n = CHi_Movie();
|
|
||||||
} else if(type == CUTIHI_T('CEnc','GVP8')) {
|
|
||||||
n = CHi_EncodeVP8();
|
|
||||||
} else if(type == CUTIHI_T('CEnc','GVP9')) {
|
|
||||||
n = CHi_EncodeVP9();
|
|
||||||
} else if(type == CUTIHI_T('CExp','Webm')) {
|
|
||||||
n = CHi_MuxWebm();
|
|
||||||
} else if(type == CUTIHI_T('CKey','hook')) {
|
|
||||||
n = CHi_Keyhook();
|
|
||||||
} else if(type == CUTIHI_T('CKey','hook')) {
|
|
||||||
n = CHi_Keyhook();
|
|
||||||
} else if(type == CUTIHI_T('CEnc','Opus')) {
|
|
||||||
n = CHi_EncodeOpus();
|
|
||||||
} else if(type == CUTIHI_T('CWeb','Cam ')) {
|
|
||||||
n = CHi_Camera();
|
|
||||||
} else if(type == CUTIHI_T('CCmp','nScl')) {
|
|
||||||
n = CHi_ComponentScale();
|
|
||||||
} else if(type == CUTIHI_T('CEnc','H264')) {
|
|
||||||
n = CHi_EncodeH264();
|
|
||||||
} else if(type == CUTIHI_T('CStr','RTMP')) {
|
|
||||||
n = CHi_StreamRTMP();
|
|
||||||
} else if(type == CUTIHI_T('CEnc','AACL')) {
|
|
||||||
n = CHi_EncodeAAC();
|
|
||||||
}
|
|
||||||
|
|
||||||
n->ng = ng;
|
|
||||||
|
|
||||||
if(!n) {
|
|
||||||
CHi_NodeGraphReset(ng);
|
|
||||||
puts("Error: Unknown node type!");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ng->nodes[i] = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(size_t i = 0; i < ng->count; i++) {
|
|
||||||
CHiPubNode *n = ng->nodes[i];
|
|
||||||
|
|
||||||
{
|
|
||||||
uint16_t u16;
|
|
||||||
reader(ud, &u16, sizeof(u16));
|
|
||||||
n->sinkCount = u16;
|
|
||||||
}
|
|
||||||
|
|
||||||
n->sinks = calloc(n->sinkCount, sizeof(*n->sinks));
|
|
||||||
|
|
||||||
for(size_t s = 0; s < n->sinkCount; s++) {
|
|
||||||
{
|
|
||||||
uint16_t u16;
|
|
||||||
reader(ud, &u16, sizeof(u16));
|
|
||||||
n->sinks[s].type = u16;
|
|
||||||
}
|
|
||||||
|
|
||||||
load_chival(ng, reader, n->sinks[s].type, &n->sinks[s].data, ud);
|
|
||||||
|
|
||||||
if(n->sinks[s].type == CUTIHI_VAL_LINKED) {
|
|
||||||
adjacency_add(n->sinks[s].data.linked.to, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update_keyed_values(ng);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CUTIVIS bool CHi_Node_Active(CHiPubNode *pubn) {
|
CUTIVIS bool CHi_Node_Active(CHiPubNode *pubn) {
|
||||||
float now = CHi_Time_Get(pubn->ng);
|
float now = CHi_Time_Get(pubn->ng);
|
||||||
return pubn->lifespan.start <= now && (pubn->lifespan.end == 0 || now < pubn->lifespan.end);
|
return pubn->lifespan.start <= now && (pubn->lifespan.end == 0 || now < pubn->lifespan.end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CUTIVIS void CHi_Node_Destroy(CHiPubNode *pubn) {
|
||||||
|
for(size_t ni = 0; ni < pubn->ng->count; ni++) {
|
||||||
|
for(size_t si = 0; si < pubn->ng->nodes[ni]->sinkCount; si++) {
|
||||||
|
if(pubn->ng->nodes[ni]->sinks[si].linked.to == pubn) {
|
||||||
|
adjacency_remove(pubn, pubn->ng->nodes[ni]);
|
||||||
|
|
||||||
|
pubn->ng->nodes[ni]->sinks[si].linked.to = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t si = 0; si < pubn->sinkCount; si++) {
|
||||||
|
if(pubn->sinks[si].linked.to) {
|
||||||
|
adjacency_remove(pubn->sinks[si].linked.to, pubn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ni;
|
||||||
|
for(ni = 0; ni < pubn->ng->count; ni++) {
|
||||||
|
if(pubn->ng->nodes[ni] == pubn) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(ni < pubn->ng->count);
|
||||||
|
|
||||||
|
if(pubn->Destroy) {
|
||||||
|
pubn->Destroy(pubn);
|
||||||
|
} else {
|
||||||
|
free(pubn);
|
||||||
|
}
|
||||||
|
|
||||||
|
memmove(&pubn->ng->nodes[ni], &pubn->ng->nodes[ni + 1], sizeof(*pubn->ng->nodes) * (pubn->ng->count - ni - 1));
|
||||||
|
|
||||||
|
pubn->ng->count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
CUTIVIS void CHi_AddError(CHiPubNode *node, const char *err, size_t sink) {
|
||||||
|
for(size_t i = 0; i < CUTIHI_MAX_ERRORS; i++) {
|
||||||
|
if(!node->errors.active[i]) {
|
||||||
|
node->errors.active[i] = true;
|
||||||
|
strncpy(node->errors.code[i], "invalid type", CUTIHI_ERR_SIZE);
|
||||||
|
node->errors.sink[i] = sink;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|||||||
42
hi/node.h
42
hi/node.h
@@ -21,12 +21,18 @@ extern "C" {
|
|||||||
#define CUTIHI_T(a, b) ((((uint64_t) b) << 32) | a
|
#define CUTIHI_T(a, b) ((((uint64_t) b) << 32) | a
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct EBMLWriter;
|
||||||
|
struct CHiPubNode;
|
||||||
|
|
||||||
typedef size_t(*CHiSaveWriter)(void *ud, const void *data, size_t len);
|
typedef size_t(*CHiSaveWriter)(void *ud, const void *data, size_t len);
|
||||||
|
typedef void(*CHiSaveCustomNodeData)(void *ud, struct EBMLWriter*, struct CHiPubNode*);
|
||||||
|
|
||||||
typedef size_t(*CHiLoadReader)(void *ud, void *data, size_t len);
|
typedef size_t(*CHiLoadReader)(void *ud, void *data, size_t len);
|
||||||
|
typedef void(*CHiLoadCustomNodeData)(void *ud, uint64_t nodeIdx, uint64_t elId, const void *buf, size_t length);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CUTIHI_VAL_NONE = 0,
|
CUTIHI_VAL_NONE = 0,
|
||||||
CUTIHI_VAL_LINKED = 1,
|
// CUTIHI_VAL_LINKED = 1,
|
||||||
CUTIHI_VAL_KEYED = 2,
|
CUTIHI_VAL_KEYED = 2,
|
||||||
CUTIHI_VAL_SAMPLE = 3,
|
CUTIHI_VAL_SAMPLE = 3,
|
||||||
CUTIHI_VAL_TEXT = 4,
|
CUTIHI_VAL_TEXT = 4,
|
||||||
@@ -47,10 +53,6 @@ struct CHiPubNode;
|
|||||||
struct CHiKeyframes;
|
struct CHiKeyframes;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
|
||||||
struct CHiPubNode *to;
|
|
||||||
uint8_t idx;
|
|
||||||
} linked;
|
|
||||||
struct CHiImage *sample;
|
struct CHiImage *sample;
|
||||||
char *text;
|
char *text;
|
||||||
float vec4[4];
|
float vec4[4];
|
||||||
@@ -61,6 +63,10 @@ typedef union {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
CHiValType type;
|
CHiValType type;
|
||||||
|
struct {
|
||||||
|
struct CHiPubNode *to;
|
||||||
|
uint8_t idx;
|
||||||
|
} linked;
|
||||||
CHiValueRaw data;
|
CHiValueRaw data;
|
||||||
} CHiValue;
|
} CHiValue;
|
||||||
|
|
||||||
@@ -78,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);
|
||||||
@@ -104,6 +108,8 @@ typedef struct CHiPubNode {
|
|||||||
} lifespan;
|
} lifespan;
|
||||||
|
|
||||||
char _dfsmark;
|
char _dfsmark;
|
||||||
|
|
||||||
|
uint8_t impl[];
|
||||||
} CHiPubNode;
|
} CHiPubNode;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -167,8 +173,8 @@ typedef struct CHiNodeGraph {
|
|||||||
|
|
||||||
CUTIVIS CHiNodeGraph *CHi_NewNodeGraph();
|
CUTIVIS CHiNodeGraph *CHi_NewNodeGraph();
|
||||||
CUTIVIS CHiNodeGraph *CHi_NodeGraphReset(CHiNodeGraph *ng);
|
CUTIVIS CHiNodeGraph *CHi_NodeGraphReset(CHiNodeGraph *ng);
|
||||||
CUTIVIS int CHi_NodeGraphSave(CHiNodeGraph *ng, CHiSaveWriter writer, void *ud);
|
CUTIVIS int CHi_NodeGraphSave(CHiNodeGraph *ng, CHiSaveWriter, CHiSaveCustomNodeData, void *ud);
|
||||||
CUTIVIS int CHi_NodeGraphLoad(CHiNodeGraph *ng, CHiLoadReader reader, void *ud);
|
CUTIVIS int CHi_NodeGraphLoad(CHiNodeGraph *ng, CHiLoadReader, CHiLoadCustomNodeData, void *ud);
|
||||||
|
|
||||||
CUTIVIS void CHi_RegisterNode(CHiNodeGraph*, CHiPubNode*);
|
CUTIVIS void CHi_RegisterNode(CHiNodeGraph*, CHiPubNode*);
|
||||||
CUTIVIS void CHi_MakeDirty(CHiNodeGraph*, CHiPubNode*);
|
CUTIVIS void CHi_MakeDirty(CHiNodeGraph*, CHiPubNode*);
|
||||||
@@ -181,7 +187,7 @@ CUTIVIS size_t CHi_GetClosestKeyframe(CHiNodeGraph *ng, CHiKeyframes *kfs, float
|
|||||||
CUTIVIS void CHi_SetExtrapolationMode(CHiNodeGraph *ng, CHiPubNode *n, size_t sinkIdx, CHiExtrapolationMode mode, float* params);
|
CUTIVIS void CHi_SetExtrapolationMode(CHiNodeGraph *ng, CHiPubNode *n, size_t sinkIdx, CHiExtrapolationMode mode, float* params);
|
||||||
CUTIVIS void CHi_DeleteKeyframe(CHiNodeGraph *ng, CHiKeyframes *kfs, size_t idx);
|
CUTIVIS void CHi_DeleteKeyframe(CHiNodeGraph *ng, CHiKeyframes *kfs, size_t idx);
|
||||||
|
|
||||||
CUTIVIS int CHi_Hysteresis(CHiPubNode*);
|
CUTIVIS int CHi_Hysteresis(CHiNodeGraph*);
|
||||||
|
|
||||||
CUTIVIS void CHi_SetDuration(CHiNodeGraph *ng, float);
|
CUTIVIS void CHi_SetDuration(CHiNodeGraph *ng, float);
|
||||||
CUTIVIS void CHi_BeginCompilation(CHiNodeGraph *ng);
|
CUTIVIS void CHi_BeginCompilation(CHiNodeGraph *ng);
|
||||||
@@ -240,10 +246,10 @@ CUTIVIS int CHi_MuxWebm_Stop(CHiPubNode*);
|
|||||||
#define CUTIHI_WINDOW_OUT_SAMPLE 1
|
#define CUTIHI_WINDOW_OUT_SAMPLE 1
|
||||||
CUTIVIS CHiPubNode *CHi_Window();
|
CUTIVIS CHiPubNode *CHi_Window();
|
||||||
|
|
||||||
CUTIVIS size_t CHi_Window_GetSourceCount();
|
CUTIVIS size_t CHi_Window_GetList(void **buf);
|
||||||
CUTIVIS const char *CHi_Window_GetSourceName(size_t);
|
CUTIVIS const char *CHi_Window_GetName(void *buf, size_t i);
|
||||||
CUTIVIS uintptr_t CHi_Window_GetSourceData(size_t);
|
CUTIVIS size_t CHi_Window_GetHandle(void *buf, size_t i);
|
||||||
CUTIVIS size_t CHi_Window_GetNextSource(size_t);
|
CUTIVIS void CHi_Window_FreeList(void *buf);
|
||||||
|
|
||||||
#define CUTIHI_MICROPHONE_IN_NAME 0
|
#define CUTIHI_MICROPHONE_IN_NAME 0
|
||||||
#define CUTIHI_MICROPHONE_OUT_AUDIO 1
|
#define CUTIHI_MICROPHONE_OUT_AUDIO 1
|
||||||
@@ -275,12 +281,20 @@ CUTIVIS CHiPubNode *CHi_StreamRTMP();
|
|||||||
|
|
||||||
CUTIVIS CHiPubNode *CHi_EncodeAAC();
|
CUTIVIS CHiPubNode *CHi_EncodeAAC();
|
||||||
|
|
||||||
|
CUTIVIS CHiPubNode *CHi_MuxMatroska();
|
||||||
|
|
||||||
CUTIVIS CHiValue *CHi_Crawl(CHiValue*);
|
CUTIVIS CHiValue *CHi_Crawl(CHiValue*);
|
||||||
|
|
||||||
//CUTIVIS void CHi_Save(CHiNodeGraph *ng);
|
//CUTIVIS void CHi_Save(CHiNodeGraph *ng);
|
||||||
|
|
||||||
CUTIVIS bool CHi_Node_Active(CHiPubNode*);
|
CUTIVIS bool CHi_Node_Active(CHiPubNode*);
|
||||||
|
|
||||||
|
CUTIVIS void CHi_Node_Destroy(CHiPubNode*);
|
||||||
|
|
||||||
|
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
|
||||||
|
|||||||
96
hi/node_internal.h
Normal file
96
hi/node_internal.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include"node.h"
|
||||||
|
|
||||||
|
static inline size_t bisect(const void *key, const void *base, size_t nmemb, size_t size, ssize_t(*compar)(const void*, const void*)) {
|
||||||
|
size_t low = 0, high = nmemb;
|
||||||
|
|
||||||
|
while(low < high) {
|
||||||
|
size_t middle = (low + high) / 2;
|
||||||
|
if(compar((const void*) ((uintptr_t) base + size * middle), key) < 0) {
|
||||||
|
low = middle + 1;
|
||||||
|
} else {
|
||||||
|
high = middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return low;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ssize_t float_compar(const void *A, const void *B) {
|
||||||
|
float a = *(float*) A;
|
||||||
|
float b = *(float*) B;
|
||||||
|
return (a > b) - (a < b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int adjacencycmp(const void *a, const void *b) {
|
||||||
|
size_t v = (uintptr_t) ((CHiAdjacency*) a)[0] - (uintptr_t) ((CHiAdjacency*) b)[0];
|
||||||
|
return v ? v : (uintptr_t) ((CHiAdjacency*) a)[1] - (uintptr_t) ((CHiAdjacency*) b)[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void adjacency_add(CHiPubNode *source, CHiPubNode *sink) {
|
||||||
|
CHiNodeGraph *ng = source->ng;
|
||||||
|
|
||||||
|
if(ng->adjacencyCount == ng->adjacencyCapacity) {
|
||||||
|
ng->adjacencies = realloc(ng->adjacencies, sizeof(CHiAdjacency) * (ng->adjacencyCapacity *= 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
ng->adjacencies[ng->adjacencyCount][0] = source;
|
||||||
|
ng->adjacencies[ng->adjacencyCount][1] = sink;
|
||||||
|
ng->adjacencyCount++;
|
||||||
|
|
||||||
|
qsort(ng->adjacencies, ng->adjacencyCount, sizeof(CHiAdjacency), adjacencycmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void adjacency_remove(CHiPubNode *source, CHiPubNode *sink) {
|
||||||
|
CHiNodeGraph *ng = source->ng;
|
||||||
|
|
||||||
|
CHiAdjacency *adj = bsearch(&(CHiAdjacency) {source, sink}, ng->adjacencies, ng->adjacencyCount, sizeof(CHiAdjacency), adjacencycmp);
|
||||||
|
if(adj) {
|
||||||
|
memmove(adj, adj + 1, sizeof(CHiAdjacency) * (ng->adjacencyCount - (adj - ng->adjacencies) - 1));
|
||||||
|
ng->adjacencyCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void update_keyed_values(CHiNodeGraph *ng) {
|
||||||
|
for(size_t kfsIdx = 0; kfsIdx < ng->keyframesList.count; kfsIdx++) {
|
||||||
|
CHiKeyframes *kfs = ng->keyframesList.keyframes[kfsIdx];
|
||||||
|
|
||||||
|
kfs->current.type = kfs->type;
|
||||||
|
|
||||||
|
float now = ng->time;
|
||||||
|
|
||||||
|
size_t idx = bisect(&now, kfs->times, kfs->count, sizeof(now), float_compar);
|
||||||
|
|
||||||
|
if(idx == 0) {
|
||||||
|
kfs->current.data = kfs->values[idx];
|
||||||
|
|
||||||
|
if(kfs->current.type == CUTIHI_VAL_VEC4 && kfs->extrapolationMode == CUTIHI_EXTRAPOLATION_CONSTANT) {
|
||||||
|
kfs->current.data.vec4[0] += (now - kfs->times[0]) * kfs->extrapolationParameter[0];
|
||||||
|
kfs->current.data.vec4[1] += (now - kfs->times[0]) * kfs->extrapolationParameter[1];
|
||||||
|
kfs->current.data.vec4[2] += (now - kfs->times[0]) * kfs->extrapolationParameter[2];
|
||||||
|
kfs->current.data.vec4[3] += (now - kfs->times[0]) * kfs->extrapolationParameter[3];
|
||||||
|
}
|
||||||
|
} else if(idx == kfs->count) {
|
||||||
|
kfs->current.data = kfs->values[idx - 1];
|
||||||
|
|
||||||
|
if(kfs->current.type == CUTIHI_VAL_VEC4 && kfs->extrapolationMode == CUTIHI_EXTRAPOLATION_CONSTANT) {
|
||||||
|
kfs->current.data.vec4[0] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[0];
|
||||||
|
kfs->current.data.vec4[1] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[1];
|
||||||
|
kfs->current.data.vec4[2] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[2];
|
||||||
|
kfs->current.data.vec4[3] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[3];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(kfs->type == CUTIHI_VAL_VEC4) {
|
||||||
|
float alpha = (now - kfs->times[idx - 1]) / (kfs->times[idx] - kfs->times[idx - 1]);
|
||||||
|
|
||||||
|
kfs->current.data.vec4[0] = kfs->values[idx - 1].vec4[0] + (kfs->values[idx].vec4[0] - kfs->values[idx - 1].vec4[0]) * alpha;
|
||||||
|
kfs->current.data.vec4[1] = kfs->values[idx - 1].vec4[1] + (kfs->values[idx].vec4[1] - kfs->values[idx - 1].vec4[1]) * alpha;
|
||||||
|
kfs->current.data.vec4[2] = kfs->values[idx - 1].vec4[2] + (kfs->values[idx].vec4[2] - kfs->values[idx - 1].vec4[2]) * alpha;
|
||||||
|
kfs->current.data.vec4[3] = kfs->values[idx - 1].vec4[3] + (kfs->values[idx].vec4[3] - kfs->values[idx - 1].vec4[3]) * alpha;
|
||||||
|
} else {
|
||||||
|
kfs->current.data = kfs->values[idx - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
hi/opus.c
52
hi/opus.c
@@ -14,6 +14,8 @@ struct CHiEncodeOpusNode {
|
|||||||
|
|
||||||
size_t timestamp;
|
size_t timestamp;
|
||||||
|
|
||||||
|
bool firstFrame;
|
||||||
|
|
||||||
OpusEncoder *enc;
|
OpusEncoder *enc;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,24 +24,50 @@ static int encodeopus_perform(CHiPubNode *pubn) {
|
|||||||
|
|
||||||
CHiImage *newpcm = CHi_Crawl(&pubn->sinks[0])->data.sample;
|
CHiImage *newpcm = CHi_Crawl(&pubn->sinks[0])->data.sample;
|
||||||
|
|
||||||
if(n->pcmSamples + newpcm->width > n->pcmCapacity) {
|
if(newpcm) {
|
||||||
n->pcmbuf = realloc(n->pcmbuf, sizeof(*n->pcmbuf) * (n->pcmCapacity = ((n->pcmSamples + newpcm->width + 1023) & ~1023)));
|
if(n->pcmSamples + newpcm->width > n->pcmCapacity) {
|
||||||
|
n->pcmbuf = realloc(n->pcmbuf, sizeof(*n->pcmbuf) * (n->pcmCapacity = ((n->pcmSamples + newpcm->width + 1023) & ~1023)));
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&n->pcmbuf[n->pcmSamples], newpcm->data16, sizeof(*n->pcmbuf) * newpcm->width);
|
||||||
|
n->pcmSamples += newpcm->width;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&n->pcmbuf[n->pcmSamples], newpcm->data16, sizeof(*n->pcmbuf) * newpcm->width);
|
CHiBSFrames *frames = calloc(1, sizeof(*frames));
|
||||||
n->pcmSamples += newpcm->width;
|
|
||||||
|
|
||||||
CHiBSFrames *frames = malloc(sizeof(*frames));
|
|
||||||
frames->count = 0;
|
frames->count = 0;
|
||||||
|
|
||||||
|
if(!n->firstFrame) {
|
||||||
|
frames = CHi_BS_Grow(frames, 1);
|
||||||
|
|
||||||
|
struct OpusHead {
|
||||||
|
uint8_t magic[8];
|
||||||
|
uint8_t version;
|
||||||
|
uint8_t channelCount;
|
||||||
|
uint16_t preSkip;
|
||||||
|
uint32_t inputSampleRate;
|
||||||
|
uint16_t outputGain;
|
||||||
|
uint8_t mappingFamily;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct OpusHead *head = calloc(1, sizeof(*head));
|
||||||
|
*head = (struct OpusHead) {{'O', 'p', 'u', 's', 'H', 'e', 'a', 'd'}, 1, 1, 3840, 48000, 0, 0};
|
||||||
|
|
||||||
|
frames->data[0].timestamp = 0;
|
||||||
|
frames->data[0].flags = CUTIHI_BS_SETUP_PACKET;
|
||||||
|
frames->data[0].ptr = head;
|
||||||
|
frames->data[0].sz = sizeof(*head);
|
||||||
|
|
||||||
|
n->firstFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
size_t samp;
|
size_t samp;
|
||||||
for(samp = 0; samp + 960 <= n->pcmSamples; samp += 960) {
|
for(samp = 0; samp + 960 <= n->pcmSamples; samp += 960) {
|
||||||
frames = realloc(frames, sizeof(*frames) + sizeof(*frames->data) * (frames->count + 1));
|
frames = CHi_BS_Grow(frames, 1);
|
||||||
frames->data[frames->count].timestamp = n->timestamp;
|
|
||||||
frames->data[frames->count].ptr = malloc(1276);
|
frames->data[frames->count - 1].timestamp = n->timestamp;
|
||||||
frames->data[frames->count].sz = opus_encode(n->enc, &n->pcmbuf[samp], 960, frames->data[frames->count].ptr, 1276);
|
frames->data[frames->count - 1].ptr = malloc(1276);
|
||||||
if(frames->data[frames->count].sz < 0) puts("OPUS ERR");
|
frames->data[frames->count - 1].sz = opus_encode(n->enc, &n->pcmbuf[samp], 960, frames->data[frames->count - 1].ptr, 1276);
|
||||||
frames->count++;
|
if(frames->data[frames->count - 1].sz < 0) puts("OPUS ERR");
|
||||||
|
|
||||||
n->timestamp += 20;
|
n->timestamp += 20;
|
||||||
}
|
}
|
||||||
|
|||||||
149
hi/relay_win.c
Normal file
149
hi/relay_win.c
Normal 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;
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
382
hi/serialize.c
Normal file
382
hi/serialize.c
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
#include"node.h"
|
||||||
|
|
||||||
|
#include<eebie/writer.h>
|
||||||
|
#include<eebie/reader.h>
|
||||||
|
#include<assert.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
#include"node_internal.h"
|
||||||
|
#include<endian.h>
|
||||||
|
|
||||||
|
#define ID_PROJECT 0x10000000
|
||||||
|
#define ID_DURATION 0x4000
|
||||||
|
#define ID_TIME 0x4001
|
||||||
|
#define ID_KEYFRAMES 0x200000
|
||||||
|
#define ID_CHIVAL 0x80
|
||||||
|
#define ID_CHIVAL_TYPE 0x81
|
||||||
|
#define ID_LINK 0x82
|
||||||
|
#define ID_DATA 0x83
|
||||||
|
#define ID_EXTRAPOLATION_MODE 0x4002
|
||||||
|
#define ID_EXTRAPOLATION_PARAM 0x4003
|
||||||
|
#define ID_NODE 0x84
|
||||||
|
#define ID_NODE_TYPE 0x85
|
||||||
|
#define ID_LIFESPAN_START 0x4004
|
||||||
|
#define ID_LIFESPAN_END 0x4005
|
||||||
|
|
||||||
|
static void put_chival(CHiNodeGraph *ng, EBMLWriter *ebml, CHiValue val) {
|
||||||
|
ebml_writer_push(ebml, ID_CHIVAL);
|
||||||
|
ebml_writer_put(ebml, ID_CHIVAL_TYPE, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = val.type});
|
||||||
|
|
||||||
|
if(val.linked.to) {
|
||||||
|
size_t index;
|
||||||
|
for(index = 0; index < ng->count; index++) {
|
||||||
|
if(ng->nodes[index] == val.linked.to) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(index < ng->count);
|
||||||
|
|
||||||
|
ebml_writer_put(ebml, ID_LINK, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = (index << 16) | (val.linked.idx)});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(val.type == CUTIHI_VAL_TEXT) {
|
||||||
|
ebml_writer_put(ebml, ID_DATA, EBML_BINARY, (EBMLPrimitive) {.binary = {.ptr = val.data.text, .length = strlen(val.data.text)}});
|
||||||
|
} else if(val.type == CUTIHI_VAL_VEC4) {
|
||||||
|
ebml_writer_put(ebml, ID_DATA, EBML_BINARY, (EBMLPrimitive) {.binary = {.ptr = val.data.vec4, .length = sizeof(val.data.vec4)}});
|
||||||
|
} else if(val.type == CUTIHI_VAL_KEYED) {
|
||||||
|
size_t index;
|
||||||
|
for(index = 0; index < ng->keyframesList.count; index++) {
|
||||||
|
if(ng->keyframesList.keyframes[index] == val.data.keyed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(index < ng->count);
|
||||||
|
|
||||||
|
ebml_writer_put(ebml, ID_DATA, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = index});
|
||||||
|
}
|
||||||
|
ebml_writer_pop(ebml);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t ebml_write_callback(EBMLWriter *ebml, const void *buf, size_t length) {
|
||||||
|
CHiSaveWriter writer = ((void**) ebml->ud)[0];
|
||||||
|
void *ud = ((void**) ebml->ud)[1];
|
||||||
|
return writer(ud, buf, length);
|
||||||
|
}
|
||||||
|
static void *ebml_alloc_callback(EBMLWriter *ebml, void *buf, size_t length) {
|
||||||
|
return realloc(buf, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUTIVIS int CHi_NodeGraphSave(CHiNodeGraph *ng, CHiSaveWriter writer, CHiSaveCustomNodeData customNodeData, void *ud) {
|
||||||
|
EBMLWriter ebml;
|
||||||
|
ebml_writer_init(&ebml);
|
||||||
|
|
||||||
|
void *ebmlud[2] = {writer, ud};
|
||||||
|
|
||||||
|
ebml.ud = ebmlud;
|
||||||
|
ebml.eventWrite = ebml_write_callback;
|
||||||
|
ebml.eventAlloc = ebml_alloc_callback;
|
||||||
|
|
||||||
|
ebml_writer_push(&ebml, 0x1A45DFA3);
|
||||||
|
ebml_writer_put(&ebml, 0x4286, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 1});
|
||||||
|
ebml_writer_put(&ebml, 0x4282, EBML_STRING, (EBMLPrimitive) {.string = "cuticle"});
|
||||||
|
ebml_writer_pop(&ebml);
|
||||||
|
|
||||||
|
ebml_writer_push(&ebml, ID_PROJECT);
|
||||||
|
if(ng->duration != -1) {
|
||||||
|
ebml_writer_put(&ebml, ID_DURATION, EBML_FLOAT4, (EBMLPrimitive) {.flt4 = ng->duration});
|
||||||
|
}
|
||||||
|
ebml_writer_put(&ebml, ID_TIME, EBML_FLOAT4, (EBMLPrimitive) {.flt4 = ng->time});
|
||||||
|
|
||||||
|
for(size_t i = 0; i < ng->keyframesList.count; i++) {
|
||||||
|
CHiKeyframes *kfs = ng->keyframesList.keyframes[i];
|
||||||
|
|
||||||
|
ebml_writer_push(&ebml, ID_KEYFRAMES);
|
||||||
|
//ebml_writer_put(&ebml, ID_CHIVAL_TYPE, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = kfs->type});
|
||||||
|
|
||||||
|
for(size_t k = 0; k < kfs->count; k++) {
|
||||||
|
ebml_writer_put(&ebml, ID_TIME, EBML_FLOAT4, (EBMLPrimitive) {.flt4 = kfs->times[k]});
|
||||||
|
put_chival(ng, &ebml, (CHiValue) {.type = kfs->type, .linked = {}, .data = kfs->values[k]});
|
||||||
|
}
|
||||||
|
|
||||||
|
ebml_writer_put(&ebml, ID_EXTRAPOLATION_MODE, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = kfs->extrapolationMode});
|
||||||
|
ebml_writer_put(&ebml, ID_EXTRAPOLATION_PARAM, EBML_BINARY, (EBMLPrimitive) {.binary = {.ptr = &kfs->extrapolationParameter, sizeof(kfs->extrapolationParameter)}});
|
||||||
|
ebml_writer_pop(&ebml);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < ng->count; i++) {
|
||||||
|
CHiPubNode *node = ng->nodes[i];
|
||||||
|
|
||||||
|
ebml_writer_push(&ebml, ID_NODE);
|
||||||
|
ebml_writer_put(&ebml, ID_NODE_TYPE, EBML_BINARY, (EBMLPrimitive) {.binary = {.ptr = &node->type, sizeof(node->type)}});
|
||||||
|
|
||||||
|
if(node->lifespan.start != 0) {
|
||||||
|
ebml_writer_put(&ebml, ID_LIFESPAN_START, EBML_FLOAT4, (EBMLPrimitive) {.flt4 = node->lifespan.start});
|
||||||
|
}
|
||||||
|
if(node->lifespan.end != 0) {
|
||||||
|
ebml_writer_put(&ebml, ID_LIFESPAN_END, EBML_FLOAT4, (EBMLPrimitive) {.flt4 = node->lifespan.end});
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t sink = 0; sink < node->sinkCount; sink++) {
|
||||||
|
put_chival(ng, &ebml, node->sinks[sink]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(customNodeData) {
|
||||||
|
customNodeData(ud, &ebml, node);
|
||||||
|
}
|
||||||
|
ebml_writer_pop(&ebml);
|
||||||
|
}
|
||||||
|
ebml_writer_pop(&ebml);
|
||||||
|
|
||||||
|
while(!ebml_writer_flush(&ebml));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LoadState {
|
||||||
|
uint8_t *accum;
|
||||||
|
size_t accumSize;
|
||||||
|
|
||||||
|
CHiNodeGraph *ng;
|
||||||
|
|
||||||
|
void *ud;
|
||||||
|
CHiLoadCustomNodeData customNodeData;
|
||||||
|
|
||||||
|
// Filled in by callbacks
|
||||||
|
CHiValue activeVal;
|
||||||
|
|
||||||
|
// Filled in by callbacks
|
||||||
|
size_t activeNodeNextSinkIdx;
|
||||||
|
CHiPubNode *activeNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
static EBMLElementType ebml_enter_callback(EBMLReader *ebml, uint64_t id, uint64_t length) {
|
||||||
|
struct LoadState *state = ebml->ud;
|
||||||
|
|
||||||
|
if(id == ID_KEYFRAMES) {
|
||||||
|
state->ng->keyframesList.keyframes = realloc(state->ng->keyframesList.keyframes, sizeof(*state->ng->keyframesList.keyframes) * (++state->ng->keyframesList.count));
|
||||||
|
state->ng->keyframesList.keyframes[state->ng->keyframesList.count - 1] = calloc(1, sizeof(CHiKeyframes));
|
||||||
|
} else if(id == ID_CHIVAL) {
|
||||||
|
memset(&state->activeVal, 0, sizeof(state->activeVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (id == ID_PROJECT || id == ID_KEYFRAMES || id == ID_CHIVAL || id == ID_NODE || id == 0x5000) ? EBML_TREE : EBML_BINARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ebml_data_callback(EBMLReader *ebml, const uint8_t *data, size_t length) {
|
||||||
|
struct LoadState *state = ebml->ud;
|
||||||
|
|
||||||
|
state->accum = realloc(state->accum, state->accumSize + length);
|
||||||
|
memcpy(state->accum + state->accumSize, data, length);
|
||||||
|
state->accumSize += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ebml_exit_callback(EBMLReader *ebml) {
|
||||||
|
struct LoadState *state = ebml->ud;
|
||||||
|
|
||||||
|
uint64_t parent = ebml->currentDepth >= 2 ? ebml->idStack[ebml->currentDepth - 2] : 0;
|
||||||
|
uint64_t id = ebml->idStack[ebml->currentDepth - 1];
|
||||||
|
|
||||||
|
if(id == ID_NODE) {
|
||||||
|
state->activeNode = NULL;
|
||||||
|
} else if(parent == 0x5000 && state->customNodeData) {
|
||||||
|
assert(state->activeNode == state->ng->nodes[state->ng->count - 1]);
|
||||||
|
|
||||||
|
state->customNodeData(state->ud, state->ng->count - 1, id, state->accum, state->accumSize);
|
||||||
|
} else if(parent == ID_NODE) {
|
||||||
|
if(id == ID_NODE_TYPE) {
|
||||||
|
assert(state->accumSize <= 8);
|
||||||
|
|
||||||
|
uint64_t type = 0;
|
||||||
|
memcpy((char*) &type + sizeof(type) - state->accumSize, state->accum, state->accumSize);
|
||||||
|
|
||||||
|
CHiPubNode *n = NULL;
|
||||||
|
|
||||||
|
if(type == CUTIHI_T('CPre','view')) {
|
||||||
|
n = CHi_Preview();
|
||||||
|
} else if(type == CUTIHI_T('CMix','er ')) {
|
||||||
|
n = CHi_Mixer();
|
||||||
|
} else if(type == CUTIHI_T('CTex','t ')) {
|
||||||
|
n = CHi_Text();
|
||||||
|
} else if(type == CUTIHI_T('CTim','e ')) {
|
||||||
|
n = CHi_Time();
|
||||||
|
} else if(type == CUTIHI_T('CMod','ulat')) {
|
||||||
|
n = CHi_Modulate();
|
||||||
|
} else if(type == CUTIHI_T('CCns','tCol')) {
|
||||||
|
n = CHi_ConstantSample();
|
||||||
|
} else if(type == CUTIHI_T('CEmb','ed ')) {
|
||||||
|
n = CHi_Embed();
|
||||||
|
} else if(type == CUTIHI_T('CIma','ge ')) {
|
||||||
|
n = CHi_Image();
|
||||||
|
} else if(type == CUTIHI_T('CWin','dow ')) {
|
||||||
|
n = CHi_Window();
|
||||||
|
} else if(type == CUTIHI_T('CInA','udio')) {
|
||||||
|
n = CHi_Microphone();
|
||||||
|
} else if(type == CUTIHI_T('CExp','Wave')) {
|
||||||
|
n = CHi_ExportWav();
|
||||||
|
} else if(type == CUTIHI_T('CMov','ie ')) {
|
||||||
|
n = CHi_Movie();
|
||||||
|
} else if(type == CUTIHI_T('CEnc','GVP8')) {
|
||||||
|
n = CHi_EncodeVP8();
|
||||||
|
} else if(type == CUTIHI_T('CEnc','GVP9')) {
|
||||||
|
n = CHi_EncodeVP9();
|
||||||
|
// } else if(type == CUTIHI_T('CExp','Webm')) {
|
||||||
|
// n = CHi_MuxWebm();
|
||||||
|
} else if(type == CUTIHI_T('CKey','hook')) {
|
||||||
|
n = CHi_Keyhook();
|
||||||
|
} else if(type == CUTIHI_T('CKey','hook')) {
|
||||||
|
n = CHi_Keyhook();
|
||||||
|
} else if(type == CUTIHI_T('CEnc','Opus')) {
|
||||||
|
n = CHi_EncodeOpus();
|
||||||
|
} else if(type == CUTIHI_T('CWeb','Cam ')) {
|
||||||
|
n = CHi_Camera();
|
||||||
|
} else if(type == CUTIHI_T('CCmp','nScl')) {
|
||||||
|
n = CHi_ComponentScale();
|
||||||
|
} else if(type == CUTIHI_T('CEnc','H264')) {
|
||||||
|
n = CHi_EncodeH264();
|
||||||
|
} else if(type == CUTIHI_T('CStr','RTMP')) {
|
||||||
|
n = CHi_StreamRTMP();
|
||||||
|
} else if(type == CUTIHI_T('CEnc','AACL')) {
|
||||||
|
n = CHi_EncodeAAC();
|
||||||
|
} else if(type == CUTIHI_T('CExp','Mkv ')) {
|
||||||
|
n = CHi_MuxMatroska();
|
||||||
|
}
|
||||||
|
|
||||||
|
n->ng = state->ng;
|
||||||
|
|
||||||
|
if(!n) {
|
||||||
|
CHi_NodeGraphReset(state->ng);
|
||||||
|
assert(0 && "Unknown node type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
state->ng->nodes = realloc(state->ng->nodes, sizeof(*state->ng->nodes) * (++state->ng->count));
|
||||||
|
state->ng->nodes[state->ng->count - 1] = n;
|
||||||
|
|
||||||
|
state->activeNodeNextSinkIdx = 0;
|
||||||
|
state->activeNode = n;
|
||||||
|
} else if(id == ID_LIFESPAN_START) {
|
||||||
|
state->activeNode->lifespan.start = *(float*) state->accum;
|
||||||
|
} else if(id == ID_LIFESPAN_END) {
|
||||||
|
state->activeNode->lifespan.end = *(float*) state->accum;
|
||||||
|
} else if(id == ID_CHIVAL) {
|
||||||
|
if(state->activeNodeNextSinkIdx >= state->activeNode->sinkCount) {
|
||||||
|
state->activeNode->sinks = realloc(state->activeNode->sinks, sizeof(*state->activeNode->sinks) * (state->activeNode->sinkCount = (state->activeNodeNextSinkIdx + 1)));
|
||||||
|
}
|
||||||
|
state->activeNode->sinks[state->activeNodeNextSinkIdx++] = state->activeVal;
|
||||||
|
}
|
||||||
|
} else if(parent == ID_CHIVAL) {
|
||||||
|
if(id == ID_CHIVAL_TYPE) {
|
||||||
|
state->activeVal.type = *(uint8_t*) state->accum;
|
||||||
|
} else if(id == ID_LINK) {
|
||||||
|
assert(state->accumSize <= 8);
|
||||||
|
|
||||||
|
uint64_t data = 0;
|
||||||
|
memcpy((char*) &data + sizeof(data) - state->accumSize, state->accum, state->accumSize);
|
||||||
|
data = htobe64(data);
|
||||||
|
|
||||||
|
uint16_t srcIdx = data & 0xFFFF;
|
||||||
|
uint64_t nodeIdx = data >> 16;
|
||||||
|
|
||||||
|
// linked.to is supposed to be a pointer but not all nodes are
|
||||||
|
// available at this stage, so we'll fix it up after the EBML
|
||||||
|
// parsing finishes
|
||||||
|
state->activeVal.linked.to = (void*) (nodeIdx + 1);
|
||||||
|
state->activeVal.linked.idx = srcIdx;
|
||||||
|
} else if(id == ID_DATA) {
|
||||||
|
if(state->activeVal.type == CUTIHI_VAL_TEXT) {
|
||||||
|
state->activeVal.data.text = calloc(1, state->accumSize + 1);
|
||||||
|
memcpy(state->activeVal.data.text, state->accum, state->accumSize);
|
||||||
|
} else if(state->activeVal.type == CUTIHI_VAL_VEC4) {
|
||||||
|
assert(state->accumSize <= 16 && state->accumSize % 4 == 0);
|
||||||
|
|
||||||
|
memcpy(state->activeVal.data.vec4, state->accum, state->accumSize);
|
||||||
|
} else if(state->activeVal.type == CUTIHI_VAL_KEYED) {
|
||||||
|
assert(state->accumSize <= 8);
|
||||||
|
|
||||||
|
uint64_t idx = 0;
|
||||||
|
memcpy((char*) &idx + sizeof(idx) - state->accumSize, state->accum, state->accumSize);
|
||||||
|
idx = htobe64(idx);
|
||||||
|
|
||||||
|
// keyed is supposed to be a pointer but keyframes may not
|
||||||
|
// be available at this stage, so we'll fix it up after the
|
||||||
|
// EBML parsing finishes
|
||||||
|
state->activeVal.data.keyed = (void*) idx;
|
||||||
|
} else {
|
||||||
|
assert(0 && "Cannot load CHiValue data.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(parent == ID_KEYFRAMES) {
|
||||||
|
CHiKeyframes *kfs = state->ng->keyframesList.keyframes[state->ng->keyframesList.count - 1];
|
||||||
|
if(id == ID_TIME) {
|
||||||
|
kfs->times = realloc(kfs->times, sizeof(*kfs->times) * (++kfs->count));
|
||||||
|
kfs->values = realloc(kfs->values, sizeof(*kfs->values) * kfs->count);
|
||||||
|
|
||||||
|
kfs->times[kfs->count - 1] = *(float*) state->accum;
|
||||||
|
} else if(id == ID_CHIVAL) {
|
||||||
|
assert(state->activeVal.linked.to == NULL);
|
||||||
|
|
||||||
|
kfs->values[kfs->count - 1] = state->activeVal.data;
|
||||||
|
} else if(id == ID_EXTRAPOLATION_MODE) {
|
||||||
|
kfs->extrapolationMode = *(uint8_t*) state->accum;
|
||||||
|
} else if(id == ID_EXTRAPOLATION_PARAM) {
|
||||||
|
assert(state->accumSize == 16);
|
||||||
|
|
||||||
|
memcpy(&kfs->extrapolationParameter, state->accum, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state->accumSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CUTIVIS int CHi_NodeGraphLoad(CHiNodeGraph *ng, CHiLoadReader reader, CHiLoadCustomNodeData customNodeData, void *ud) {
|
||||||
|
CHi_NodeGraphReset(ng);
|
||||||
|
|
||||||
|
ng->time = 0;
|
||||||
|
ng->duration = -1;
|
||||||
|
|
||||||
|
EBMLReader ebml;
|
||||||
|
ebml_reader_init(&ebml);
|
||||||
|
|
||||||
|
struct LoadState state = {.ng = ng, .ud = ud, .customNodeData = customNodeData};
|
||||||
|
ebml.ud = &state;
|
||||||
|
|
||||||
|
ebml.eventEnterElement = ebml_enter_callback;
|
||||||
|
ebml.eventDataChunk = ebml_data_callback;
|
||||||
|
ebml.eventExitElement = ebml_exit_callback;
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
uint8_t buf[4096];
|
||||||
|
while(1) {
|
||||||
|
size_t readBytes = reader(ud, buf + i, sizeof(buf) - i);
|
||||||
|
|
||||||
|
i += readBytes;
|
||||||
|
|
||||||
|
size_t fedBytes = ebml_reader_feed(&ebml, buf, i);
|
||||||
|
|
||||||
|
memmove(buf, buf + fedBytes, sizeof(buf) - fedBytes);
|
||||||
|
|
||||||
|
i -= fedBytes;
|
||||||
|
|
||||||
|
if(readBytes == 0 && fedBytes == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix-up links/adjacencies
|
||||||
|
for(size_t i = 0; i < ng->count; i++) {
|
||||||
|
CHiPubNode *n = ng->nodes[i];
|
||||||
|
for(size_t s = 0; s < n->sinkCount; s++) {
|
||||||
|
if(n->sinks[s].linked.to) {
|
||||||
|
n->sinks[s].linked.to = ng->nodes[(uintptr_t) n->sinks[s].linked.to - 1];
|
||||||
|
|
||||||
|
adjacency_add(n->sinks[s].linked.to, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(n->sinks[s].type == CUTIHI_VAL_KEYED) {
|
||||||
|
n->sinks[s].data.keyed = ng->keyframesList.keyframes[(uintptr_t) n->sinks[s].data.keyed];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_keyed_values(ng);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
48
hi/vpxenc.c
48
hi/vpxenc.c
@@ -32,6 +32,8 @@ typedef struct CHiEncodeVP9Node {
|
|||||||
uint8_t *outY, *outU, *outV;
|
uint8_t *outY, *outU, *outV;
|
||||||
uint16_t strideY, strideU, strideV;
|
uint16_t strideY, strideU, strideV;
|
||||||
|
|
||||||
|
bool firstFrame;
|
||||||
|
|
||||||
vpx_codec_iface_t *iface;
|
vpx_codec_iface_t *iface;
|
||||||
} CHiEncodeVP9Node;
|
} CHiEncodeVP9Node;
|
||||||
|
|
||||||
@@ -40,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;
|
||||||
@@ -70,20 +72,33 @@ static int encodevpx_perform(CHiPubNode *pub) {
|
|||||||
|
|
||||||
vpx_codec_encode(&node->codec, &vpxraw, CHi_Time_Get(pub->ng) * 1000.f, 1, 0, VPX_DL_REALTIME);
|
vpx_codec_encode(&node->codec, &vpxraw, CHi_Time_Get(pub->ng) * 1000.f, 1, 0, VPX_DL_REALTIME);
|
||||||
|
|
||||||
CHiBSFrames *ret = malloc(sizeof(CHiBSFrames));
|
CHiBSFrames *ret = calloc(1, sizeof(CHiBSFrames));
|
||||||
ret->count = 0;
|
|
||||||
|
if(!node->firstFrame) {
|
||||||
|
ret = CHi_BS_Grow(ret, 1);
|
||||||
|
|
||||||
|
CHiVPxBSSettings *s = calloc(1, sizeof(*s));
|
||||||
|
s->width = vpxraw.w;
|
||||||
|
s->height = vpxraw.h;
|
||||||
|
|
||||||
|
ret->data[0].timestamp = 0;
|
||||||
|
ret->data[0].sz = sizeof(CHiVPxBSSettings);
|
||||||
|
ret->data[0].flags = CUTIHI_BS_SETUP_PACKET;
|
||||||
|
ret->data[0].ptr = s;
|
||||||
|
|
||||||
|
node->firstFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
vpx_codec_iter_t iter = NULL;
|
vpx_codec_iter_t iter = NULL;
|
||||||
const vpx_codec_cx_pkt_t *pkt;
|
const vpx_codec_cx_pkt_t *pkt;
|
||||||
while((pkt = vpx_codec_get_cx_data(&node->codec, &iter)) != NULL) {
|
while((pkt = vpx_codec_get_cx_data(&node->codec, &iter)) != NULL) {
|
||||||
if(pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
if(pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
||||||
ret = (CHiBSFrames*) realloc(ret, sizeof(CHiBSFrames) + sizeof(CHiBSFrame) * (ret->count + 1));
|
ret = CHi_BS_Grow(ret, 1);
|
||||||
ret->data[ret->count].timestamp = pkt->data.frame.pts;
|
ret->data[ret->count - 1].timestamp = pkt->data.frame.pts;
|
||||||
ret->data[ret->count].sz = pkt->data.frame.sz;
|
ret->data[ret->count - 1].sz = pkt->data.frame.sz;
|
||||||
ret->data[ret->count].flags = pkt->data.frame.flags & VPX_FRAME_IS_KEY;
|
ret->data[ret->count - 1].flags = pkt->data.frame.flags & VPX_FRAME_IS_KEY;
|
||||||
ret->data[ret->count].ptr = malloc(ret->data[ret->count].sz);
|
ret->data[ret->count - 1].ptr = malloc(ret->data[ret->count - 1].sz);
|
||||||
memcpy(ret->data[ret->count].ptr, pkt->data.frame.buf, ret->data[ret->count].sz);
|
memcpy(ret->data[ret->count - 1].ptr, pkt->data.frame.buf, ret->data[ret->count - 1].sz);
|
||||||
ret->count++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,22 +125,25 @@ static int encodevpx_start(CHiPubNode *pubn) {
|
|||||||
vpx_codec_enc_config_default(node->iface, &node->cfg, 0);
|
vpx_codec_enc_config_default(node->iface, &node->cfg, 0);
|
||||||
node->cfg.g_w = firstFrame->width;
|
node->cfg.g_w = firstFrame->width;
|
||||||
node->cfg.g_h = firstFrame->height;
|
node->cfg.g_h = firstFrame->height;
|
||||||
|
node->cfg.g_profile = 0;
|
||||||
node->cfg.g_timebase.num = 1;
|
node->cfg.g_timebase.num = 1;
|
||||||
node->cfg.g_timebase.den = 30;
|
node->cfg.g_timebase.den = 30;
|
||||||
node->cfg.g_lag_in_frames = 0;
|
node->cfg.g_lag_in_frames = 0;
|
||||||
node->cfg.g_threads = 8;
|
node->cfg.g_threads = 4;
|
||||||
node->cfg.kf_mode = VPX_KF_AUTO;
|
node->cfg.kf_mode = VPX_KF_AUTO;
|
||||||
node->cfg.kf_max_dist = 300;
|
node->cfg.kf_max_dist = 300;
|
||||||
node->cfg.rc_end_usage = VPX_VBR;
|
node->cfg.rc_end_usage = VPX_VBR;
|
||||||
node->cfg.rc_target_bitrate = 512;
|
node->cfg.rc_target_bitrate = 2000;
|
||||||
node->cfg.rc_min_quantizer = 4;
|
node->cfg.rc_min_quantizer = 0;
|
||||||
node->cfg.rc_max_quantizer = 48;
|
node->cfg.rc_max_quantizer = 32;
|
||||||
|
node->cfg.rc_undershoot_pct = 95;
|
||||||
|
|
||||||
vpx_codec_enc_init(&node->codec, node->iface, &node->cfg, 0);
|
vpx_codec_enc_init(&node->codec, node->iface, &node->cfg, 0);
|
||||||
vpx_codec_control(&node->codec, VP8E_SET_CPUUSED, 8);
|
vpx_codec_control(&node->codec, VP8E_SET_CPUUSED, 4);
|
||||||
vpx_codec_control(&node->codec, VP9E_SET_ROW_MT, 1);
|
vpx_codec_control(&node->codec, VP9E_SET_ROW_MT, 1);
|
||||||
vpx_codec_control(&node->codec, VP9E_SET_TILE_COLUMNS, 2);
|
vpx_codec_control(&node->codec, VP9E_SET_TILE_COLUMNS, 2);
|
||||||
vpx_codec_control(&node->codec, VP9E_SET_TILE_ROWS, 1);
|
vpx_codec_control(&node->codec, VP9E_SET_TILE_ROWS, 1);
|
||||||
|
vpx_codec_control(&node->codec, VP9E_SET_NOISE_SENSITIVITY, 0);
|
||||||
vpx_codec_control(&node->codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN);
|
vpx_codec_control(&node->codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN);
|
||||||
|
|
||||||
node->strideY = (node->cfg.g_w + 64) & ~63;
|
node->strideY = (node->cfg.g_w + 64) & ~63;
|
||||||
|
|||||||
112
hi/webcam.c
112
hi/webcam.c
@@ -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
136
hi/webcam_v4l2.c
Normal 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;
|
||||||
|
}
|
||||||
@@ -43,7 +43,7 @@ static int muxwebm_perform(CHiPubNode *pubn) {
|
|||||||
|
|
||||||
CHiMuxWebmNode *alln = (CHiMuxWebmNode*) pubn;
|
CHiMuxWebmNode *alln = (CHiMuxWebmNode*) pubn;
|
||||||
|
|
||||||
if(pubn->sinks[1].data.linked.to) {
|
if(pubn->sinks[1].linked.to) {
|
||||||
CHiBSFrames *opus = CHi_Crawl(&pubn->sinks[1])->data.bitstream;
|
CHiBSFrames *opus = CHi_Crawl(&pubn->sinks[1])->data.bitstream;
|
||||||
for(size_t i = 0; i < opus->count; i++) {
|
for(size_t i = 0; i < opus->count; i++) {
|
||||||
alln->audioBacklog.push(opus->data[i]);
|
alln->audioBacklog.push(opus->data[i]);
|
||||||
@@ -57,7 +57,7 @@ static int muxwebm_perform(CHiPubNode *pubn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while(pubn->sinks[1].data.linked.to && alln->audioBacklog.size() > 0 && alln->videoBacklog.size() > 0 && alln->audioBacklog.front().timestamp <= alln->videoBacklog.front().timestamp) {
|
while(pubn->sinks[1].linked.to && alln->audioBacklog.size() > 0 && alln->videoBacklog.size() > 0 && alln->audioBacklog.front().timestamp <= alln->videoBacklog.front().timestamp) {
|
||||||
Frame frame;
|
Frame frame;
|
||||||
frame.Init((const uint8_t*) alln->audioBacklog.front().ptr, alln->audioBacklog.front().sz);
|
frame.Init((const uint8_t*) alln->audioBacklog.front().ptr, alln->audioBacklog.front().sz);
|
||||||
frame.set_track_number(alln->audioTrack);
|
frame.set_track_number(alln->audioTrack);
|
||||||
@@ -68,7 +68,7 @@ static int muxwebm_perform(CHiPubNode *pubn) {
|
|||||||
alln->audioBacklog.pop();
|
alln->audioBacklog.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pubn->sinks[1].data.linked.to == NULL || (alln->audioBacklog.size() > 0 && alln->videoBacklog.size() > 0 && alln->audioBacklog.front().timestamp >= alln->videoBacklog.front().timestamp)) {
|
if(pubn->sinks[1].linked.to == NULL || (alln->audioBacklog.size() > 0 && alln->videoBacklog.size() > 0 && alln->audioBacklog.front().timestamp >= alln->videoBacklog.front().timestamp)) {
|
||||||
Frame frame;
|
Frame frame;
|
||||||
if(!frame.Init((const uint8_t*) alln->videoBacklog.front().ptr, alln->videoBacklog.front().sz)) puts("INIT FAIL");
|
if(!frame.Init((const uint8_t*) alln->videoBacklog.front().ptr, alln->videoBacklog.front().sz)) puts("INIT FAIL");
|
||||||
frame.set_track_number(alln->videoTrack);
|
frame.set_track_number(alln->videoTrack);
|
||||||
@@ -122,7 +122,7 @@ CUTIVIS int CHi_MuxWebm_Start(CHiPubNode *pubn) {
|
|||||||
alln->seg.GetSegmentInfo()->set_writing_app("Cuticle");
|
alln->seg.GetSegmentInfo()->set_writing_app("Cuticle");
|
||||||
|
|
||||||
/* Hack into first frame to get resolution */
|
/* Hack into first frame to get resolution */
|
||||||
CHiPubNode *evp9 = pubn->sinks[0].data.linked.to;
|
CHiPubNode *evp9 = pubn->sinks[0].linked.to;
|
||||||
CHiImage *firstFrame = (CHiImage*) CHi_Crawl(&evp9->sinks[0])->data.sample;
|
CHiImage *firstFrame = (CHiImage*) CHi_Crawl(&evp9->sinks[0])->data.sample;
|
||||||
|
|
||||||
alln->videoTrack = alln->seg.AddVideoTrack(firstFrame->width, firstFrame->height, 0);
|
alln->videoTrack = alln->seg.AddVideoTrack(firstFrame->width, firstFrame->height, 0);
|
||||||
@@ -137,7 +137,7 @@ CUTIVIS int CHi_MuxWebm_Start(CHiPubNode *pubn) {
|
|||||||
track->SetColour(colourspace);
|
track->SetColour(colourspace);
|
||||||
alln->seg.CuesTrack(alln->videoTrack);
|
alln->seg.CuesTrack(alln->videoTrack);
|
||||||
|
|
||||||
if(pubn->sinks[1].data.linked.to) {
|
if(pubn->sinks[1].linked.to) {
|
||||||
struct __attribute__((packed)) {
|
struct __attribute__((packed)) {
|
||||||
uint32_t magic1;
|
uint32_t magic1;
|
||||||
uint32_t magic2;
|
uint32_t magic2;
|
||||||
|
|||||||
140
hi/window_scl.cpp
Normal file
140
hi/window_scl.cpp
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#include"node.h"
|
||||||
|
|
||||||
|
#include<stdlib.h>
|
||||||
|
|
||||||
|
#include<ScreenCapture.h>
|
||||||
|
#include<mutex>
|
||||||
|
#include<atomic>
|
||||||
|
|
||||||
|
#include<tmmintrin.h>
|
||||||
|
#include<smmintrin.h>
|
||||||
|
|
||||||
|
#include<time.h>
|
||||||
|
|
||||||
|
#include<string.h>
|
||||||
|
|
||||||
|
#include"img.h"
|
||||||
|
|
||||||
|
#include"linearity.h"
|
||||||
|
|
||||||
|
#include"minitrace.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
CHiPubNode pub;
|
||||||
|
|
||||||
|
char* lastWindowString;
|
||||||
|
std::shared_ptr<SL::Screen_Capture::IScreenCaptureManager> config;
|
||||||
|
|
||||||
|
std::mutex mut;
|
||||||
|
std::vector<CHiImage*> images;
|
||||||
|
std::atomic<bool> ignoreAlpha;
|
||||||
|
} CHiWindowNode;
|
||||||
|
|
||||||
|
static int window_perform(CHiPubNode *n) {
|
||||||
|
CHiWindowNode *w = (CHiWindowNode*) n;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if(w->lastWindowString == nullptr || strcmp(w->lastWindowString, expectedTitle)) {
|
||||||
|
if(w->lastWindowString) {
|
||||||
|
free(w->lastWindowString);
|
||||||
|
}
|
||||||
|
w->lastWindowString = strdup(expectedTitle);
|
||||||
|
|
||||||
|
w->config = SL::Screen_Capture::CreateCaptureConfiguration([=](){
|
||||||
|
for(SL::Screen_Capture::Window& window : SL::Screen_Capture::GetWindows()) {
|
||||||
|
if(!strcmp(window.Name, w->lastWindowString)) {
|
||||||
|
return std::vector<SL::Screen_Capture::Window>{window};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::vector<SL::Screen_Capture::Window>{};
|
||||||
|
})->onNewFrame([=](const SL::Screen_Capture::Image& img, const SL::Screen_Capture::Window& window){
|
||||||
|
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);
|
||||||
|
|
||||||
|
bool ignoreAlpha = w->ignoreAlpha.load();
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
|
for(size_t y = 0; y < new_image->height; y++) {
|
||||||
|
uint8_t buf[16] = {};
|
||||||
|
for(size_t x = 0; x < new_image->width; x += 2) {
|
||||||
|
memcpy(buf, &img.Data[y * new_image->width + x], 8);
|
||||||
|
|
||||||
|
__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));
|
||||||
|
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_store_si128((__m128i*) ((uintptr_t) new_image->data8 + y * new_image->stride + x * 8), c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock{w->mut};
|
||||||
|
while(w->images.size() > 0) {
|
||||||
|
CHi_Image_Free(w->images.front());
|
||||||
|
w->images.erase(w->images.begin());
|
||||||
|
}
|
||||||
|
w->images.push_back(new_image);
|
||||||
|
})->start_capturing();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock{w->mut};
|
||||||
|
if(w->images.size() > 0) {
|
||||||
|
if(n->sources[0].data.sample) {
|
||||||
|
CHi_Image_Free(n->sources[0].data.sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
n->sources[0].type = CUTIHI_VAL_SAMPLE;
|
||||||
|
n->sources[0].data.sample = w->images.front();
|
||||||
|
|
||||||
|
w->images.erase(w->images.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
MTR_END("CHi", "window_perform");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void window_destroy(CHiPubNode *pubn) {
|
||||||
|
CHiWindowNode *n = (CHiWindowNode*) pubn;
|
||||||
|
delete n;
|
||||||
|
}
|
||||||
|
|
||||||
|
CUTIVIS CHiPubNode *CHi_Window() {
|
||||||
|
auto *n = new CHiWindowNode();
|
||||||
|
n->pub.type = CUTIHI_T('CWin','dow ');
|
||||||
|
n->pub.Start = n->pub.Stop = NULL;
|
||||||
|
n->pub.Perform = window_perform;
|
||||||
|
n->pub.Destroy = window_destroy;
|
||||||
|
n->pub.sinkCount = 1;
|
||||||
|
n->pub.sinks = (CHiValue*) calloc(sizeof(*n->pub.sinks), 2);
|
||||||
|
n->pub.sourceCount = 1;
|
||||||
|
n->pub.sources = (CHiValue*) calloc(sizeof(*n->pub.sources), 1);
|
||||||
|
|
||||||
|
return &n->pub;
|
||||||
|
}
|
||||||
|
|
||||||
|
CUTIVIS size_t CHi_Window_GetList(void **buf) {
|
||||||
|
auto vec = SL::Screen_Capture::GetWindows();
|
||||||
|
|
||||||
|
*buf = calloc(vec.size(), sizeof(vec[0]));
|
||||||
|
memcpy(*buf, &vec[0], sizeof(vec[0]) * vec.size());
|
||||||
|
|
||||||
|
return vec.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
CUTIVIS const char *CHi_Window_GetName(void *buf, size_t i) {
|
||||||
|
return ((SL::Screen_Capture::Window*) buf)[i].Name;
|
||||||
|
}
|
||||||
|
CUTIVIS size_t CHi_Window_GetHandle(void *buf, size_t i) {
|
||||||
|
return ((SL::Screen_Capture::Window*) buf)[i].Handle;
|
||||||
|
}
|
||||||
|
CUTIVIS void CHi_Window_FreeList(void *buf) {
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
|
|
||||||
#include"minitrace.h"
|
#include"minitrace.h"
|
||||||
|
|
||||||
static Display *d;
|
static thread_local Display *d;
|
||||||
static Window root;
|
static thread_local Window root;
|
||||||
|
|
||||||
static int find_window(Display *d, Window *w, const char *contains) {
|
static int find_window(Display *d, Window *w, const char *contains) {
|
||||||
if(contains) {
|
if(contains) {
|
||||||
@@ -40,7 +40,7 @@ static int find_window(Display *d, Window *w, const char *contains) {
|
|||||||
for(int i = 0; i < numItems; i++) {
|
for(int i = 0; i < numItems; i++) {
|
||||||
status = XGetWMName(d, list[i], &windowName);
|
status = XGetWMName(d, list[i], &windowName);
|
||||||
if(status >= Success) {
|
if(status >= Success) {
|
||||||
if(windowName.value && strstr(windowName.value, contains)) {
|
if(windowName.value && strstr((const char*) windowName.value, contains)) {
|
||||||
*w = list[i];
|
*w = list[i];
|
||||||
found = 1;
|
found = 1;
|
||||||
break;
|
break;
|
||||||
@@ -59,17 +59,20 @@ static int find_window(Display *d, Window *w, const char *contains) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
CHiPubNode pub;
|
|
||||||
|
|
||||||
Window xcache;
|
Window xcache;
|
||||||
XImage *ximg;
|
XImage *ximg;
|
||||||
XShmSegmentInfo shminfo;
|
XShmSegmentInfo shminfo;
|
||||||
|
|
||||||
CHiImage *vcache;
|
CHiImage *vcache;
|
||||||
} CHiWindowNode;
|
} WindowImpl;
|
||||||
|
|
||||||
static int window_perform(CHiPubNode *n) {
|
static int window_perform(CHiPubNode *n) {
|
||||||
CHiWindowNode *w = (CHiWindowNode*) n;
|
auto *w = (WindowImpl*) n->impl;
|
||||||
|
|
||||||
|
if(!d) {
|
||||||
|
d = XOpenDisplay(NULL);
|
||||||
|
root = RootWindow(d, DefaultScreen(d));
|
||||||
|
}
|
||||||
|
|
||||||
MTR_BEGIN("CHi", "window_perform");
|
MTR_BEGIN("CHi", "window_perform");
|
||||||
|
|
||||||
@@ -86,11 +89,11 @@ static int window_perform(CHiPubNode *n) {
|
|||||||
w->ximg = XShmCreateImage(d, attrs.visual, 32, ZPixmap, NULL, &w->shminfo, attrs.width, attrs.height);
|
w->ximg = XShmCreateImage(d, attrs.visual, 32, ZPixmap, NULL, &w->shminfo, attrs.width, attrs.height);
|
||||||
stride = ((w->ximg->bytes_per_line + 15) & ~15);
|
stride = ((w->ximg->bytes_per_line + 15) & ~15);
|
||||||
w->shminfo.shmid = shmget(IPC_PRIVATE, stride * w->ximg->height, IPC_CREAT | 0777);
|
w->shminfo.shmid = shmget(IPC_PRIVATE, stride * w->ximg->height, IPC_CREAT | 0777);
|
||||||
w->shminfo.shmaddr = w->ximg->data = shmat(w->shminfo.shmid, 0, 0);
|
w->shminfo.shmaddr = w->ximg->data = (char*) shmat(w->shminfo.shmid, 0, 0);
|
||||||
w->shminfo.readOnly = False;
|
w->shminfo.readOnly = False;
|
||||||
XShmAttach(d, &w->shminfo);
|
XShmAttach(d, &w->shminfo);
|
||||||
|
|
||||||
w->vcache = CHi_Image_New(2, 4, 8 * attrs.width, attrs.width, attrs.height, NULL);
|
w->vcache = CHi_Image_New(2, 4, (8 * attrs.width + 15) & ~15, attrs.width, attrs.height, NULL);
|
||||||
} else {
|
} else {
|
||||||
stride = ((w->ximg->bytes_per_line + 15) & ~15);
|
stride = ((w->ximg->bytes_per_line + 15) & ~15);
|
||||||
}
|
}
|
||||||
@@ -100,12 +103,17 @@ static int window_perform(CHiPubNode *n) {
|
|||||||
|
|
||||||
XShmGetImage(d, w->xcache, w->ximg, 0, 0, AllPlanes);
|
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
|
// Turn u8 image to u16
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for(size_t y = 0; y < w->vcache->height; y++) {
|
for(size_t y = 0; y < w->vcache->height; y++) {
|
||||||
for(size_t x = 0; x < w->vcache->width; x += 2) {
|
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));
|
__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));
|
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_storeu_si128((__m128i*) ((uintptr_t) w->vcache->data16 + y * w->vcache->stride + x * 8), c);
|
_mm_storeu_si128((__m128i*) ((uintptr_t) w->vcache->data16 + y * w->vcache->stride + x * 8), c);
|
||||||
}
|
}
|
||||||
@@ -120,7 +128,7 @@ static int window_perform(CHiPubNode *n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void window_destroy(CHiPubNode *pubn) {
|
static void window_destroy(CHiPubNode *pubn) {
|
||||||
CHiWindowNode *n = (void*) pubn;
|
auto *n = (WindowImpl*) pubn->impl;
|
||||||
|
|
||||||
if(n->vcache) {
|
if(n->vcache) {
|
||||||
XShmDetach(d, &n->shminfo);
|
XShmDetach(d, &n->shminfo);
|
||||||
@@ -132,45 +140,25 @@ static void window_destroy(CHiPubNode *pubn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CUTIVIS CHiPubNode *CHi_Window() {
|
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) {
|
if(!d) {
|
||||||
d = XOpenDisplay(NULL);
|
d = XOpenDisplay(NULL);
|
||||||
root = RootWindow(d, DefaultScreen(d));
|
root = RootWindow(d, DefaultScreen(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
CHiWindowNode *n = calloc(1, sizeof(*n));
|
|
||||||
n->pub.type = CUTIHI_T('CWin','dow ');
|
|
||||||
n->pub.Start = n->pub.Stop = NULL;
|
|
||||||
n->pub.Perform = window_perform;
|
|
||||||
n->pub.Destroy = window_destroy;
|
|
||||||
n->pub.sinkCount = 1;
|
|
||||||
n->pub.sinks = calloc(sizeof(*n->pub.sinks), 1);
|
|
||||||
n->pub.sourceCount = 1;
|
|
||||||
n->pub.sources = calloc(sizeof(*n->pub.sources), 1);
|
|
||||||
|
|
||||||
n->xcache = 0;
|
|
||||||
n->vcache = NULL;
|
|
||||||
|
|
||||||
return &n->pub;
|
|
||||||
}
|
|
||||||
|
|
||||||
// All of the following are ews
|
|
||||||
|
|
||||||
CUTIVIS size_t CHi_Window_GetSourceCount() {
|
|
||||||
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
|
|
||||||
Atom actualType;
|
|
||||||
int format;
|
|
||||||
unsigned long numItems, bytesAfter;
|
|
||||||
|
|
||||||
Window *list;
|
|
||||||
|
|
||||||
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
|
|
||||||
|
|
||||||
//XFree(list);
|
|
||||||
|
|
||||||
return status >= Success ? numItems : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CUTIVIS const char *CHi_Window_GetSourceName(size_t idx) {
|
|
||||||
int found = 0;
|
int found = 0;
|
||||||
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
|
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
|
||||||
Atom actualType;
|
Atom actualType;
|
||||||
@@ -183,36 +171,35 @@ CUTIVIS const char *CHi_Window_GetSourceName(size_t idx) {
|
|||||||
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
|
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
|
||||||
|
|
||||||
if(status >= Success) {
|
if(status >= Success) {
|
||||||
//XFree(list);
|
WindowListDatum *data = (WindowListDatum*) calloc(numItems, sizeof(*data));
|
||||||
|
size_t successfulWindows = 0;
|
||||||
|
|
||||||
status = XGetWMName(d, list[idx], &windowName);
|
for(size_t i = 0; i < numItems; i++) {
|
||||||
if(status >= Success) {
|
status = XGetWMName(d, list[i], &windowName);
|
||||||
found = 1;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return found ? strdup(windowName.value ? windowName.value : "") : NULL;
|
*buf = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
CUTIVIS uintptr_t CHi_Window_GetSourceData(size_t idx) {
|
|
||||||
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
|
|
||||||
Atom actualType;
|
|
||||||
int format;
|
|
||||||
unsigned long numItems, bytesAfter;
|
|
||||||
|
|
||||||
Window *list;
|
|
||||||
|
|
||||||
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
|
|
||||||
|
|
||||||
if(status >= Success) {
|
|
||||||
Window ret = list[idx];
|
|
||||||
//XFree(list);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CUTIVIS size_t CHi_Window_GetNextSource(size_t i) {
|
CUTIVIS const char *CHi_Window_GetName(void *buf, size_t i) {
|
||||||
return i + 1;
|
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);
|
||||||
}
|
}
|
||||||
466
ui/frame.cpp
466
ui/frame.cpp
@@ -22,13 +22,32 @@
|
|||||||
#include"timeline.h"
|
#include"timeline.h"
|
||||||
#include<functional>
|
#include<functional>
|
||||||
#include<algorithm>
|
#include<algorithm>
|
||||||
|
#include<eebie/writer.h>
|
||||||
|
|
||||||
#define SSE_MATHFUN_WITH_CODE
|
#define SSE_MATHFUN_WITH_CODE
|
||||||
#include<hi/kumb.h>
|
#include<hi/kumb.h>
|
||||||
|
|
||||||
#include<hi/linearity.h>
|
#include<hi/linearity.h>
|
||||||
|
|
||||||
static Frame *globaldis;
|
extern Frame *globaldis;
|
||||||
|
|
||||||
|
// The Preview node is actually a NOOP
|
||||||
|
// Must inject our own function when either loading or creating projects
|
||||||
|
static int INJECTED_PREVIEW_FUNC(CHiPubNode *preview) {
|
||||||
|
CHiValue *val = CHi_Crawl(&preview->sinks[0]);
|
||||||
|
|
||||||
|
if(val->type == CUTIHI_VAL_SAMPLE && 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;
|
||||||
|
};
|
||||||
|
|
||||||
std::string node_name_from_id(uint64_t id) {
|
std::string node_name_from_id(uint64_t id) {
|
||||||
static std::unordered_map<uint64_t, std::string> NODE_ID_NAMES = {
|
static std::unordered_map<uint64_t, std::string> NODE_ID_NAMES = {
|
||||||
@@ -38,7 +57,7 @@ std::string node_name_from_id(uint64_t id) {
|
|||||||
{CUTIHI_T('CTim','e '), "Time"},
|
{CUTIHI_T('CTim','e '), "Time"},
|
||||||
{CUTIHI_T('CMod','ulat'), "Modulate"},
|
{CUTIHI_T('CMod','ulat'), "Modulate"},
|
||||||
{CUTIHI_T('CCns','tCol'), "Constant"},
|
{CUTIHI_T('CCns','tCol'), "Constant"},
|
||||||
{CUTIHI_T('CEmb','ed '), "Frame"},
|
{CUTIHI_T('CEmb','ed '), "Layer"},
|
||||||
{CUTIHI_T('CIma','ge '), "Image"},
|
{CUTIHI_T('CIma','ge '), "Image"},
|
||||||
{CUTIHI_T('CWin','dow '), "Window"},
|
{CUTIHI_T('CWin','dow '), "Window"},
|
||||||
{CUTIHI_T('CInA','udio'), "Microphone"},
|
{CUTIHI_T('CInA','udio'), "Microphone"},
|
||||||
@@ -55,6 +74,7 @@ std::string node_name_from_id(uint64_t id) {
|
|||||||
{CUTIHI_T('CChr','omaK'), "Chroma Key"},
|
{CUTIHI_T('CChr','omaK'), "Chroma Key"},
|
||||||
{CUTIHI_T('CStr','RTMP'), "Stream RTMP"},
|
{CUTIHI_T('CStr','RTMP'), "Stream RTMP"},
|
||||||
{CUTIHI_T('CEnc','AACL'), "Encode AAC-LC"},
|
{CUTIHI_T('CEnc','AACL'), "Encode AAC-LC"},
|
||||||
|
{CUTIHI_T('CExp','Mkv '), "Mux Matroska"},
|
||||||
};
|
};
|
||||||
|
|
||||||
auto nameit = NODE_ID_NAMES.find(id);
|
auto nameit = NODE_ID_NAMES.find(id);
|
||||||
@@ -85,10 +105,9 @@ static void ShapeGrNode(GrNode *gn) {
|
|||||||
gn->sources = {{"Audio", GrNode::Port::Type::SAMPLE}};
|
gn->sources = {{"Audio", GrNode::Port::Type::SAMPLE}};
|
||||||
|
|
||||||
int numSinks = gn->logical->sinkCount;
|
int numSinks = gn->logical->sinkCount;
|
||||||
while(numSinks > 0 && gn->logical->sinks[numSinks - 1].type == CUTIHI_VAL_NONE) {
|
if(gn->logical->sinks[numSinks - 1].type == CUTIHI_VAL_NONE) {
|
||||||
numSinks--;
|
numSinks++;
|
||||||
}
|
}
|
||||||
numSinks++;
|
|
||||||
|
|
||||||
gn->sinks.clear();
|
gn->sinks.clear();
|
||||||
for(int s = 0; s < numSinks; s++) {
|
for(int s = 0; s < numSinks; s++) {
|
||||||
@@ -108,7 +127,7 @@ static void ShapeGrNode(GrNode *gn) {
|
|||||||
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
|
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
|
||||||
} else if(gn->logical->type == CUTIHI_T('CEmb','ed ')) {
|
} else if(gn->logical->type == CUTIHI_T('CEmb','ed ')) {
|
||||||
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
|
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
|
||||||
gn->sinks = {{"Frame", GrNode::Port::Type::SAMPLE, true}};
|
gn->sinks = {{"Background", GrNode::Port::Type::SAMPLE, true}};
|
||||||
|
|
||||||
int numSubs = 0;
|
int numSubs = 0;
|
||||||
while(gn->logical->sinkCount > (1 + numSubs * 3 + 0) && gn->logical->sinks[1 + numSubs * 3 + 0].type != CUTIHI_VAL_NONE) {
|
while(gn->logical->sinkCount > (1 + numSubs * 3 + 0) && gn->logical->sinks[1 + numSubs * 3 + 0].type != CUTIHI_VAL_NONE) {
|
||||||
@@ -122,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}};
|
||||||
@@ -146,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}};
|
||||||
@@ -169,6 +188,9 @@ static void ShapeGrNode(GrNode *gn) {
|
|||||||
} else if(gn->logical->type == CUTIHI_T('CEnc','AACL')) {
|
} else if(gn->logical->type == CUTIHI_T('CEnc','AACL')) {
|
||||||
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 ')) {
|
||||||
|
gn->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVING}};
|
||||||
|
gn->sources = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
gn->Fit();
|
gn->Fit();
|
||||||
@@ -220,76 +242,40 @@ Frame::Frame() : wxFrame(NULL, wxID_ANY, "Cuticle", wxDefaultPosition, {wxSystem
|
|||||||
wxFileDialog save{this, "Save", "", "", "Cuticle Project (*.ctc)|*.ctc", wxFD_SAVE | wxFD_OVERWRITE_PROMPT};
|
wxFileDialog save{this, "Save", "", "", "Cuticle Project (*.ctc)|*.ctc", wxFD_SAVE | wxFD_OVERWRITE_PROMPT};
|
||||||
|
|
||||||
if(save.ShowModal() == wxID_OK) {
|
if(save.ShowModal() == wxID_OK) {
|
||||||
FILE *f = fopen(save.GetPath().mb_str(), "wb");
|
struct UD {
|
||||||
|
FILE *f;
|
||||||
|
Frame *frame;
|
||||||
|
} ud;
|
||||||
|
|
||||||
CHi_NodeGraphSave(graph->backendNG, +[](void *ud, const void *data, size_t len){
|
wxString path = save.GetPath();
|
||||||
auto f = (FILE*) ud;
|
if(!path.EndsWith(".ctc")) {
|
||||||
|
path += ".ctc";
|
||||||
return fwrite(data, 1, len, f);
|
|
||||||
}, f);
|
|
||||||
|
|
||||||
for(size_t n = 0; n < graph->backendNG->count; n++) {
|
|
||||||
GrNode *gn = graph->get_graphical(graph->backendNG->nodes[n]);
|
|
||||||
|
|
||||||
int32_t pos[2] = {gn->GetPosition().x, gn->GetPosition().y};
|
|
||||||
fwrite(pos, sizeof(pos), 1, f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
ud.f = fopen(path.mb_str(), "wb");
|
||||||
|
ud.frame = this;
|
||||||
|
|
||||||
|
CHi_NodeGraphSave(graph->backendNG, +[](void *udPtr, const void *data, size_t len){
|
||||||
|
auto &ud = *(UD*) udPtr;
|
||||||
|
return fwrite(data, 1, len, ud.f);
|
||||||
|
}, +[](void *udPtr, EBMLWriter *ebml, CHiPubNode *node){
|
||||||
|
auto &ud = *(UD*) udPtr;
|
||||||
|
|
||||||
|
GrNode *gn = ud.frame->graph->get_graphical(node);
|
||||||
|
|
||||||
|
int32_t pos[2] = {gn->GetPosition().x, gn->GetPosition().y};
|
||||||
|
ebml_writer_push(ebml, 0x5000);
|
||||||
|
ebml_writer_put(ebml, 0x5001, EBML_BINARY, (EBMLPrimitive) {.binary = {.ptr = (uint8_t*) &pos, sizeof(pos)}});
|
||||||
|
ebml_writer_pop(ebml);
|
||||||
|
}, &ud);
|
||||||
|
|
||||||
|
fclose(ud.f);
|
||||||
}
|
}
|
||||||
} else if(ev.GetId() == wxID_OPEN) {
|
} else if(ev.GetId() == wxID_OPEN) {
|
||||||
wxFileDialog load{this, "Load", "", "", "Cuticle Project (*.ctc)|*.ctc", wxFD_OPEN | wxFD_FILE_MUST_EXIST};
|
wxFileDialog load{this, "Load", "", "", "Cuticle Project (*.ctc)|*.ctc", wxFD_OPEN | wxFD_FILE_MUST_EXIST};
|
||||||
|
|
||||||
if(load.ShowModal() == wxID_OK) {
|
if(load.ShowModal() == wxID_OK) {
|
||||||
FILE *f = fopen(load.GetPath().mb_str(), "rb");
|
LoadProject(load.GetPath().utf8_string());
|
||||||
|
|
||||||
if(CHi_NodeGraphLoad(graph->backendNG, +[](void *ud, void *data, size_t len){
|
|
||||||
auto f = (FILE*) ud;
|
|
||||||
|
|
||||||
return fread(data, 1, len, f);
|
|
||||||
}, f) == 0) {
|
|
||||||
|
|
||||||
for(GrNode *gnode : graph->gnodes) {
|
|
||||||
gnode->Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
graph->gnodes.clear();
|
|
||||||
graph->links.clear();
|
|
||||||
|
|
||||||
for(size_t n = 0; n < graph->backendNG->count; n++) {
|
|
||||||
GrNode *gnode = new GrNode(graph);
|
|
||||||
|
|
||||||
int32_t pos[2];
|
|
||||||
fread(pos, sizeof(pos), 1, f);
|
|
||||||
|
|
||||||
gnode->SetPosition({pos[0], pos[1]});
|
|
||||||
|
|
||||||
gnode->logical = graph->backendNG->nodes[n];
|
|
||||||
|
|
||||||
ShapeGrNode(gnode);
|
|
||||||
|
|
||||||
graph->gnodes.push_back(gnode);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(size_t n = 0; n < graph->backendNG->count; n++) {
|
|
||||||
CHiPubNode *node = graph->backendNG->nodes[n];
|
|
||||||
|
|
||||||
for(size_t s = 0; s < node->sinkCount; s++) {
|
|
||||||
if(node->sinks[s].type == CUTIHI_VAL_LINKED) {
|
|
||||||
graph->links.push_back(NodeGraph::Link{graph->gnodes[n], s, *std::find_if(graph->gnodes.begin(), graph->gnodes.end(), [=](GrNode *gn){ return gn->logical == node->sinks[s].data.linked.to; }), node->sinks[s].data.linked.idx});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbar.duration->SetSeconds(std::max(0.f, graph->backendNG->duration));
|
|
||||||
|
|
||||||
if(graph->backendNG->duration <= 0) {
|
|
||||||
toolbar.durationEnable->SetValue(false);
|
|
||||||
toolbar.duration->Enable(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -321,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();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -343,27 +314,6 @@ Frame::Frame() : wxFrame(NULL, wxID_ANY, "Cuticle", wxDefaultPosition, {wxSystem
|
|||||||
tlba->AddControl(toolbar.duration);
|
tlba->AddControl(toolbar.duration);
|
||||||
tlba->AddControl(toolbar.btnPerform);
|
tlba->AddControl(toolbar.btnPerform);
|
||||||
|
|
||||||
graph->backendNG->eventOnStopComplete = +[](CHiNodeGraph *ng){
|
|
||||||
wxTheApp->CallAfter([ng](){
|
|
||||||
wxButton *btn = ((Frame*) ng->ud)->toolbar.btnPerform;
|
|
||||||
btn->Enable();
|
|
||||||
btn->SetLabel("Compile");
|
|
||||||
|
|
||||||
auto tlba = ((Frame*) ng->ud)->tlba;
|
|
||||||
tlba->EnableTool(wxID_SAVE, true);
|
|
||||||
tlba->EnableTool(wxID_OPEN, true);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
graph->backendNG->eventOnError = +[](CHiNodeGraph *ng, CHiPubNode *n) {
|
|
||||||
wxTheApp->CallAfter([ng, n](){
|
|
||||||
auto frame = (Frame*) ng->ud;
|
|
||||||
|
|
||||||
GrNode *gn = frame->graph->get_graphical(n);
|
|
||||||
gn->Refresh();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
tlba->Realize();
|
tlba->Realize();
|
||||||
|
|
||||||
aui.SetFlags(wxAUI_MGR_LIVE_RESIZE | wxAUI_MGR_DEFAULT);
|
aui.SetFlags(wxAUI_MGR_LIVE_RESIZE | wxAUI_MGR_DEFAULT);
|
||||||
@@ -371,6 +321,7 @@ Frame::Frame() : wxFrame(NULL, wxID_ANY, "Cuticle", wxDefaultPosition, {wxSystem
|
|||||||
|
|
||||||
Centre();
|
Centre();
|
||||||
|
|
||||||
|
LoadProject("");
|
||||||
graph->CreatePreviewNode();
|
graph->CreatePreviewNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,6 +329,104 @@ Frame::~Frame() {
|
|||||||
aui.UnInit();
|
aui.UnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Frame::LoadProject(std::string filename) {
|
||||||
|
struct UD {
|
||||||
|
FILE *f;
|
||||||
|
Frame *frame;
|
||||||
|
std::unordered_map<uint64_t, wxPoint> nodePositions;
|
||||||
|
} ud = {};
|
||||||
|
|
||||||
|
if(filename != "") {
|
||||||
|
ud.f = fopen(filename.c_str(), "rb");
|
||||||
|
}
|
||||||
|
ud.frame = this;
|
||||||
|
|
||||||
|
if(filename == "" || CHi_NodeGraphLoad(graph->backendNG, +[](void *udPtr, void *data, size_t len){
|
||||||
|
auto &ud = *(UD*) udPtr;
|
||||||
|
return fread(data, 1, len, ud.f);
|
||||||
|
}, +[](void *udPtr, uint64_t nodeIdx, uint64_t elId, const void *data, size_t len){
|
||||||
|
auto &ud = *(UD*) udPtr;
|
||||||
|
if(elId == 0x5001) {
|
||||||
|
auto pos = (const int32_t*) data;
|
||||||
|
ud.nodePositions.emplace(nodeIdx, wxPoint{pos[0], pos[1]});
|
||||||
|
}
|
||||||
|
}, &ud) == 0) {
|
||||||
|
|
||||||
|
for(GrNode *gnode : graph->gnodes) {
|
||||||
|
gnode->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
graph->gnodes.clear();
|
||||||
|
graph->links.clear();
|
||||||
|
|
||||||
|
for(size_t n = 0; n < graph->backendNG->count; n++) {
|
||||||
|
GrNode *gnode = new GrNode(graph);
|
||||||
|
|
||||||
|
gnode->SetPosition(ud.nodePositions[n]);
|
||||||
|
|
||||||
|
gnode->logical = graph->backendNG->nodes[n];
|
||||||
|
|
||||||
|
ShapeGrNode(gnode);
|
||||||
|
|
||||||
|
if(gnode->logical->type == CUTIHI_T('CPre', 'view')) {
|
||||||
|
gnode->logical->Perform = INJECTED_PREVIEW_FUNC;
|
||||||
|
}
|
||||||
|
|
||||||
|
graph->gnodes.push_back(gnode);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t n = 0; n < graph->backendNG->count; n++) {
|
||||||
|
CHiPubNode *node = graph->backendNG->nodes[n];
|
||||||
|
|
||||||
|
for(size_t s = 0; s < node->sinkCount; s++) {
|
||||||
|
if(node->sinks[s].linked.to) {
|
||||||
|
graph->links.push_back(NodeGraph::Link{graph->gnodes[n], s, *std::find_if(graph->gnodes.begin(), graph->gnodes.end(), [=](GrNode *gn){ return gn->logical == node->sinks[s].linked.to; }), node->sinks[s].linked.idx});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbar.duration->SetSeconds(std::max(0.f, graph->backendNG->duration));
|
||||||
|
|
||||||
|
if(graph->backendNG->duration <= 0) {
|
||||||
|
toolbar.durationEnable->SetValue(false);
|
||||||
|
toolbar.duration->Enable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
timeline->ResetRows();
|
||||||
|
|
||||||
|
graph->backendNG->eventOnStopComplete = +[](CHiNodeGraph *ng){
|
||||||
|
wxTheApp->CallAfter([ng](){
|
||||||
|
wxButton *btn = ((Frame*) ng->ud)->toolbar.btnPerform;
|
||||||
|
btn->Enable();
|
||||||
|
btn->SetLabel("Compile");
|
||||||
|
|
||||||
|
auto tlba = ((Frame*) ng->ud)->tlba;
|
||||||
|
tlba->EnableTool(wxID_SAVE, true);
|
||||||
|
tlba->EnableTool(wxID_OPEN, true);
|
||||||
|
|
||||||
|
auto stba = ((Frame*) ng->ud)->stba;
|
||||||
|
stba->SetStatusText("Compilation has ended.");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
graph->backendNG->eventOnError = +[](CHiNodeGraph *ng, CHiPubNode *n) {
|
||||||
|
wxTheApp->CallAfter([ng, n](){
|
||||||
|
auto frame = (Frame*) ng->ud;
|
||||||
|
|
||||||
|
GrNode *gn = frame->graph->get_graphical(n);
|
||||||
|
gn->Refresh();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ud.f) {
|
||||||
|
fclose(ud.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force everything in the graph to be computed so the preview immediately shows up
|
||||||
|
CHi_Hysteresis(graph->backendNG);
|
||||||
|
}
|
||||||
|
|
||||||
bool GrNode::MouseOverPort(wxPoint point, bool &source, int &i) {
|
bool GrNode::MouseOverPort(wxPoint point, bool &source, int &i) {
|
||||||
if(point.y < 26 || point.x < 0 || point.x > GetSize().x) return false;
|
if(point.y < 26 || point.x < 0 || point.x > GetSize().x) return false;
|
||||||
|
|
||||||
@@ -396,7 +445,7 @@ void GrNode::MakeKeyframe(int sinkIdx) {
|
|||||||
|
|
||||||
CHi_MakeKeyframe(ng->backendNG, this->logical, sinkIdx);
|
CHi_MakeKeyframe(ng->backendNG, this->logical, sinkIdx);
|
||||||
|
|
||||||
((Frame*) ng->GetParent())->timeline->Refresh();
|
globaldis->timeline->ResetRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool has_errors(CHiPubNode *pn) {
|
static bool has_errors(CHiPubNode *pn) {
|
||||||
@@ -429,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}
|
||||||
@@ -457,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}
|
||||||
@@ -584,7 +633,7 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
|
|||||||
if(dlg.ShowModal() == wxID_OK) {
|
if(dlg.ShowModal() == wxID_OK) {
|
||||||
pthread_mutex_lock(&this->logical->ng->mut);
|
pthread_mutex_lock(&this->logical->ng->mut);
|
||||||
|
|
||||||
CHiValue newv;
|
CHiValue newv = {};
|
||||||
newv.type = CUTIHI_VAL_VEC4;
|
newv.type = CUTIHI_VAL_VEC4;
|
||||||
newv.data.vec4[0] = dlg.GetColourData().GetColour().Red() / 255.f;
|
newv.data.vec4[0] = dlg.GetColourData().GetColour().Red() / 255.f;
|
||||||
newv.data.vec4[1] = dlg.GetColourData().GetColour().Green() / 255.f;
|
newv.data.vec4[1] = dlg.GetColourData().GetColour().Green() / 255.f;
|
||||||
@@ -595,12 +644,12 @@ 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);
|
||||||
|
|
||||||
CHiValue newv;
|
CHiValue newv = {};
|
||||||
newv.type = CUTIHI_VAL_TEXT;
|
newv.type = CUTIHI_VAL_TEXT;
|
||||||
newv.data.text = strdup(dlg.GetPath().utf8_str());
|
newv.data.text = strdup(dlg.GetPath().utf8_str());
|
||||||
CHi_ConfigureSink(this->logical, p, newv);
|
CHi_ConfigureSink(this->logical, p, newv);
|
||||||
@@ -608,12 +657,12 @@ 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);
|
||||||
|
|
||||||
CHiValue newv;
|
CHiValue newv = {};
|
||||||
newv.type = CUTIHI_VAL_TEXT;
|
newv.type = CUTIHI_VAL_TEXT;
|
||||||
newv.data.text = strdup(dlg.GetPath().utf8_str());
|
newv.data.text = strdup(dlg.GetPath().utf8_str());
|
||||||
CHi_ConfigureSink(this->logical, p, newv);
|
CHi_ConfigureSink(this->logical, p, newv);
|
||||||
@@ -624,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_KEY_DOWN, [=](wxKeyEvent &ev){
|
tc->Bind(wxEVT_TEXT_ENTER, [=](wxCommandEvent &ev){
|
||||||
if(ev.GetKeyCode() == WXK_RETURN) {
|
double d;
|
||||||
double d;
|
if(tc->GetValue().ToDouble(&d)) {
|
||||||
if(tc->GetValue().ToDouble(&d)) {
|
pthread_mutex_lock(&this->logical->ng->mut);
|
||||||
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);
|
CHiValue newv = *CHi_Crawl(&this->logical->sinks[p]);
|
||||||
ctrls->operator[]((it - ctrls->begin() + 1) % ctrls->size())->SetFocus();
|
newv.type = CUTIHI_VAL_VEC4;
|
||||||
ctrls->erase(it);
|
newv.data.vec4[i] = d;
|
||||||
|
CHi_ConfigureSink(this->logical, p, newv);
|
||||||
CallAfter([tc](){tc->Destroy();});
|
|
||||||
parent->Dirtify(this);
|
auto it = std::find(ctrls->begin(), ctrls->end(), tc);
|
||||||
|
ctrls->operator[]((it - ctrls->begin() + 1) % ctrls->size())->SetFocus();
|
||||||
pthread_mutex_unlock(&this->logical->ng->mut);
|
ctrls->erase(it);
|
||||||
}
|
|
||||||
} else if(ev.GetKeyCode() == WXK_TAB) {
|
CallAfter([tc](){tc->Destroy();});
|
||||||
|
parent->Dirtify(this);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&this->logical->ng->mut);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tc->Bind(wxEVT_KEY_DOWN, [=](wxKeyEvent &ev){
|
||||||
|
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);
|
||||||
@@ -655,13 +705,13 @@ 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){
|
||||||
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);
|
||||||
@@ -672,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;
|
||||||
@@ -700,7 +748,7 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
|
|||||||
if(dlg.ShowModal() == wxID_OK) {
|
if(dlg.ShowModal() == wxID_OK) {
|
||||||
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;
|
||||||
newv.data.text = strdup(CHi_Microphone_GetSourceName((size_t) (uintptr_t) dlg.GetSelectionData()));
|
newv.data.text = strdup(CHi_Microphone_GetSourceName((size_t) (uintptr_t) dlg.GetSelectionData()));
|
||||||
CHi_ConfigureSink(this->logical, p, newv);
|
CHi_ConfigureSink(this->logical, p, newv);
|
||||||
@@ -712,8 +760,10 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
|
|||||||
std::vector<const char*> choicesOrig;
|
std::vector<const char*> choicesOrig;
|
||||||
std::vector<wxString> choices;
|
std::vector<wxString> choices;
|
||||||
|
|
||||||
for(size_t i = CHi_Window_GetNextSource(-1); i < CHi_Window_GetSourceCount(); i = CHi_Window_GetNextSource(i)) {
|
void *wbuf;
|
||||||
auto name = CHi_Window_GetSourceName(i);
|
size_t windowCount = CHi_Window_GetList(&wbuf);
|
||||||
|
for(size_t i = 0; i < windowCount; i++) {
|
||||||
|
const char *name = CHi_Window_GetName(wbuf, i);
|
||||||
|
|
||||||
choicesOrig.push_back(name);
|
choicesOrig.push_back(name);
|
||||||
choices.push_back(wxString::FromUTF8(name));
|
choices.push_back(wxString::FromUTF8(name));
|
||||||
@@ -723,7 +773,7 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
|
|||||||
if(dlg.ShowModal() == wxID_OK) {
|
if(dlg.ShowModal() == wxID_OK) {
|
||||||
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;
|
||||||
newv.data.text = strdup(choicesOrig[dlg.GetSelection()]);
|
newv.data.text = strdup(choicesOrig[dlg.GetSelection()]);
|
||||||
CHi_ConfigureSink(this->logical, p, newv);
|
CHi_ConfigureSink(this->logical, p, newv);
|
||||||
@@ -731,6 +781,28 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -766,7 +838,7 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
|
|||||||
if(!isSource) {
|
if(!isSource) {
|
||||||
pthread_mutex_lock(&daNode->ng->mut);
|
pthread_mutex_lock(&daNode->ng->mut);
|
||||||
|
|
||||||
CHiValue val;
|
CHiValue val = {};
|
||||||
val.type = CUTIHI_VAL_NONE;
|
val.type = CUTIHI_VAL_NONE;
|
||||||
CHi_ConfigureSink(daNode, daPortIdx, val);
|
CHi_ConfigureSink(daNode, daPortIdx, val);
|
||||||
|
|
||||||
@@ -775,6 +847,10 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
|
|||||||
|
|
||||||
parent->Dirtify(this);
|
parent->Dirtify(this);
|
||||||
parent->Refresh();
|
parent->Refresh();
|
||||||
|
} else {
|
||||||
|
auto ng = (NodeGraph*) GetParent();
|
||||||
|
|
||||||
|
ng->DestroyNode(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -812,7 +888,7 @@ ImageViewer::ImageViewer(Frame *f) : wxPanel(f, wxID_ANY) {
|
|||||||
Bind(wxEVT_PAINT, [this](wxPaintEvent &ev){
|
Bind(wxEVT_PAINT, [this](wxPaintEvent &ev){
|
||||||
if(bm.IsOk()) {
|
if(bm.IsOk()) {
|
||||||
wxPaintDC dc(this);
|
wxPaintDC dc(this);
|
||||||
dc.DrawBitmap(bm, pos);
|
dc.DrawBitmap(bm, pos + (GetSize() - bm.GetSize()) / 2);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Bind(wxEVT_MIDDLE_DOWN, [this](wxMouseEvent &ev){
|
Bind(wxEVT_MIDDLE_DOWN, [this](wxMouseEvent &ev){
|
||||||
@@ -852,8 +928,19 @@ __attribute__((optimize("O3"))) static uint8_t *bgra64torgb24(uint8_t *orig, siz
|
|||||||
|
|
||||||
__m128i z[8] = {};
|
__m128i z[8] = {};
|
||||||
for(int zi = 0; zi < 8; zi++) {
|
for(int zi = 0; zi < 8; zi++) {
|
||||||
z[zi] = _mm_loadu_si128((__m128i*) temp + zi);
|
// Alpha-blend with checkered background.
|
||||||
z[zi] = apply_gamma_epi16(z[zi], _mm_set_ps(1, 1 / 2.2f, 1 / 2.2f, 1 / 2.2f));
|
__m128i bottom = (((x + zi) / 4 ^ y / 4) & 1)
|
||||||
|
? _mm_set_epi16(0xFFFF, 0x8000, 0x8000, 0x8000, 0xFFFF, 0x8000, 0x8000, 0x8000)
|
||||||
|
: _mm_set_epi16(0xFFFF, 0x4000, 0x4000, 0x4000, 0xFFFF, 0x4000, 0x4000, 0x4000);
|
||||||
|
|
||||||
|
__m128i top = _mm_loadu_si128((__m128i*) temp + zi);
|
||||||
|
|
||||||
|
__m128i alpha = _mm_shuffle_epi8(top, _mm_set_epi8(15, 14, 15, 14, 15, 14, 15, 14, 7, 6, 7, 6, 7, 6, 7, 6));
|
||||||
|
__m128i invAlpha = _mm_sub_epi16(_mm_set1_epi16(0xFFFF), alpha);
|
||||||
|
|
||||||
|
__m128i result = _mm_add_epi16(_mm_mulhi_epu16(top, alpha), _mm_mulhi_epu16(bottom, invAlpha));
|
||||||
|
|
||||||
|
z[zi] = apply_gamma_epi16(result, _mm_set_ps(1, 1 / 2.2f, 1 / 2.2f, 1 / 2.2f));
|
||||||
}
|
}
|
||||||
|
|
||||||
__m128i a = _mm_shuffle_epi8(z[0], _mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 9, 11, 13, 1, 3, 5));
|
__m128i a = _mm_shuffle_epi8(z[0], _mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 9, 11, 13, 1, 3, 5));
|
||||||
@@ -899,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;
|
||||||
@@ -922,7 +1009,7 @@ NodeGraph::NodeGraph(Frame *f) : wxPanel(f, wxID_ANY) {
|
|||||||
int idEncodeVp9 = menuExports->Append(wxID_ANY, "Encode VP9", "")->GetId();
|
int idEncodeVp9 = menuExports->Append(wxID_ANY, "Encode VP9", "")->GetId();
|
||||||
int idEncodeOpus = menuExports->Append(wxID_ANY, "Encode Opus", "")->GetId();
|
int idEncodeOpus = menuExports->Append(wxID_ANY, "Encode Opus", "")->GetId();
|
||||||
int idEncodeAAC = menuExports->Append(wxID_ANY, "Encode AAC-LC", "")->GetId();
|
int idEncodeAAC = menuExports->Append(wxID_ANY, "Encode AAC-LC", "")->GetId();
|
||||||
int idMuxWebm = menuExports->Append(wxID_ANY, "Mux WebM", "")->GetId();
|
int idMuxMatroska = menuExports->Append(wxID_ANY, "Mux Matroska", "")->GetId();
|
||||||
int idMuxWav = menuExports->Append(wxID_ANY, "Muv Wav", "")->GetId();
|
int idMuxWav = menuExports->Append(wxID_ANY, "Muv Wav", "")->GetId();
|
||||||
int idStreamRTMP = menuExports->Append(wxID_ANY, "Stream RTMP", "")->GetId();
|
int idStreamRTMP = menuExports->Append(wxID_ANY, "Stream RTMP", "")->GetId();
|
||||||
|
|
||||||
@@ -991,9 +1078,6 @@ NodeGraph::NodeGraph(Frame *f) : wxPanel(f, wxID_ANY) {
|
|||||||
} else if(ev.GetId() == idEncodeVp8) {
|
} else if(ev.GetId() == idEncodeVp8) {
|
||||||
noed = new GrNode(this);
|
noed = new GrNode(this);
|
||||||
noed->logical = CHi_EncodeVP8();
|
noed->logical = CHi_EncodeVP8();
|
||||||
} else if(ev.GetId() == idMuxWebm) {
|
|
||||||
noed = new GrNode(this);
|
|
||||||
noed->logical = CHi_MuxWebm();
|
|
||||||
} else if(ev.GetId() == idWindow) {
|
} else if(ev.GetId() == idWindow) {
|
||||||
noed = new GrNode(this);
|
noed = new GrNode(this);
|
||||||
noed->logical = CHi_Window();
|
noed->logical = CHi_Window();
|
||||||
@@ -1033,6 +1117,9 @@ NodeGraph::NodeGraph(Frame *f) : wxPanel(f, wxID_ANY) {
|
|||||||
} else if(ev.GetId() == idEncodeAAC) {
|
} else if(ev.GetId() == idEncodeAAC) {
|
||||||
noed = new GrNode(this);
|
noed = new GrNode(this);
|
||||||
noed->logical = CHi_EncodeAAC();
|
noed->logical = CHi_EncodeAAC();
|
||||||
|
} else if(ev.GetId() == idMuxMatroska) {
|
||||||
|
noed = new GrNode(this);
|
||||||
|
noed->logical = CHi_MuxMatroska();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(noed) {
|
if(noed) {
|
||||||
@@ -1044,7 +1131,7 @@ NodeGraph::NodeGraph(Frame *f) : wxPanel(f, wxID_ANY) {
|
|||||||
|
|
||||||
after();
|
after();
|
||||||
|
|
||||||
((Frame*) GetParent())->timeline->ResetRows();
|
globaldis->timeline->ResetRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&backendNG->mut);
|
pthread_mutex_unlock(&backendNG->mut);
|
||||||
@@ -1110,17 +1197,16 @@ NodeGraph::~NodeGraph() {
|
|||||||
void NodeGraph::Alinken(Link l) {
|
void NodeGraph::Alinken(Link l) {
|
||||||
pthread_mutex_lock(&backendNG->mut);
|
pthread_mutex_lock(&backendNG->mut);
|
||||||
|
|
||||||
CHiValue newv;
|
CHiValue newv = {};
|
||||||
newv.type = CUTIHI_VAL_LINKED;
|
newv.linked.to = l.output->logical;
|
||||||
newv.data.linked.to = l.output->logical;
|
newv.linked.idx = l.o;
|
||||||
newv.data.linked.idx = l.o;
|
|
||||||
|
|
||||||
int err = CHi_ConfigureSink(l.input->logical, l.i, newv);
|
int err = CHi_ConfigureSink(l.input->logical, l.i, newv);
|
||||||
|
|
||||||
pthread_mutex_unlock(&backendNG->mut);
|
pthread_mutex_unlock(&backendNG->mut);
|
||||||
|
|
||||||
if(!err) {
|
if(!err) {
|
||||||
((Frame*) GetParent())->stba->SetStatusText("Uh oh! Hur-hur, there's a WAACKY cycle! Can't do, sorry friend.");
|
globaldis->stba->SetStatusText("Uh oh! Hur-hur, there's a WAACKY cycle! Can't do, sorry friend.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1157,7 +1243,7 @@ void NodeGraph::Dirtify(GrNode *g) {
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
CHi_Hysteresis(gnodes[0]->logical);
|
CHi_Hysteresis(backendNG);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const NodeGraph::Link &l, const NodeGraph::Link &r) {
|
bool operator==(const NodeGraph::Link &l, const NodeGraph::Link &r) {
|
||||||
@@ -1193,17 +1279,23 @@ void NodeGraph::CreatePreviewNode() {
|
|||||||
|
|
||||||
v->SetPosition(wxPoint{GetSize().x - v->GetSize().x - 8, 8});
|
v->SetPosition(wxPoint{GetSize().x - v->GetSize().x - 8, 8});
|
||||||
|
|
||||||
// The Preview node is actually a NOOP
|
v->logical->Perform = INJECTED_PREVIEW_FUNC;
|
||||||
// Must inject our own function in Perform
|
}
|
||||||
v->logical->Perform = +[](CHiPubNode *preview){
|
|
||||||
CHiValue *val = CHi_Crawl(&preview->sinks[0]);
|
void NodeGraph::DestroyNode(GrNode *gn) {
|
||||||
|
for(auto it = links.begin(); it != links.end();) {
|
||||||
if(val->type == CUTIHI_VAL_SAMPLE && val->data.sample) {
|
if(it->input == gn || it->output == gn) {
|
||||||
globaldis->viewer->SetImage(val->data.sample);
|
it = links.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return 1;
|
|
||||||
};
|
gnodes.erase(std::find(gnodes.begin(), gnodes.end(), gn));
|
||||||
|
|
||||||
|
wxTheApp->CallAfter([=](){
|
||||||
|
gn->Destroy();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
GrNode *NodeGraph::get_graphical(CHiPubNode *n) {
|
GrNode *NodeGraph::get_graphical(CHiPubNode *n) {
|
||||||
|
|||||||
@@ -43,13 +43,15 @@ struct Frame : wxFrame {
|
|||||||
|
|
||||||
Frame();
|
Frame();
|
||||||
virtual ~Frame();
|
virtual ~Frame();
|
||||||
|
|
||||||
|
void LoadProject(std::string filename);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GrNode : wxPanel {
|
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;
|
||||||
|
|
||||||
@@ -136,6 +138,8 @@ struct NodeGraph : wxPanel {
|
|||||||
|
|
||||||
void CreatePreviewNode();
|
void CreatePreviewNode();
|
||||||
|
|
||||||
|
void DestroyNode(GrNode*);
|
||||||
|
|
||||||
GrNode *get_graphical(CHiPubNode*);
|
GrNode *get_graphical(CHiPubNode*);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,16 @@
|
|||||||
|
|
||||||
#include"frame.h"
|
#include"frame.h"
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
wxIMPLEMENT_APP(App);
|
wxIMPLEMENT_APP(App);
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include"frame.h"
|
#include"frame.h"
|
||||||
|
|
||||||
|
extern Frame *globaldis;
|
||||||
|
|
||||||
static wxBitmap bmpKf;
|
static wxBitmap bmpKf;
|
||||||
static wxBitmap bmpKfExtrap;
|
static wxBitmap bmpKfExtrap;
|
||||||
|
|
||||||
@@ -38,7 +40,7 @@ bool Timeline::MouseOverKF(wxPoint p, CHiKeyframes* &kfs, size_t &kfIdxRet) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto f = (Frame*) GetParent();
|
auto f = globaldis;
|
||||||
|
|
||||||
kfs = row->kfs;
|
kfs = row->kfs;
|
||||||
|
|
||||||
@@ -59,7 +61,7 @@ bool Timeline::MouseOverKF(wxPoint p, CHiKeyframes* &kfs, size_t &kfIdxRet) {
|
|||||||
void Timeline::ResetRows() {
|
void Timeline::ResetRows() {
|
||||||
rows.clear();
|
rows.clear();
|
||||||
|
|
||||||
auto frame = (Frame*) GetParent();
|
auto frame = globaldis;
|
||||||
|
|
||||||
auto kfsList = &frame->graph->backendNG->keyframesList;
|
auto kfsList = &frame->graph->backendNG->keyframesList;
|
||||||
|
|
||||||
@@ -92,13 +94,15 @@ void Timeline::ResetRows() {
|
|||||||
|
|
||||||
y += row.h;
|
y += row.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
float Timeline::SnapTime(float t) {
|
float Timeline::SnapTime(float t) {
|
||||||
float minDist = FLT_MAX;
|
float minDist = FLT_MAX;
|
||||||
float newT = t;
|
float newT = t;
|
||||||
|
|
||||||
auto f = (Frame*) GetParent();
|
auto f = globaldis;
|
||||||
|
|
||||||
std::vector<float> times;
|
std::vector<float> times;
|
||||||
for(Timeline::Row &row : rows) {
|
for(Timeline::Row &row : rows) {
|
||||||
@@ -149,7 +153,7 @@ Timeline::Timeline(struct Frame *parent) : wxPanel(parent, wxID_ANY) {
|
|||||||
Bind(wxEVT_LEFT_DOWN, [=](wxMouseEvent &ev){
|
Bind(wxEVT_LEFT_DOWN, [=](wxMouseEvent &ev){
|
||||||
Timeline::Row *row = GetRow(ev.GetPosition().y);
|
Timeline::Row *row = GetRow(ev.GetPosition().y);
|
||||||
|
|
||||||
auto f = (Frame*) GetParent();
|
auto f = globaldis;
|
||||||
|
|
||||||
float t = (ev.GetX() + camX - ZERO_TIME_BASE) / (float) scale;
|
float t = (ev.GetX() + camX - ZERO_TIME_BASE) / (float) scale;
|
||||||
if(ev.ControlDown()) {
|
if(ev.ControlDown()) {
|
||||||
@@ -196,14 +200,14 @@ Timeline::Timeline(struct Frame *parent) : wxPanel(parent, wxID_ANY) {
|
|||||||
t = SnapTime(t);
|
t = SnapTime(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
row->gn->logical->lifespan.end = t;
|
row->gn->logical->lifespan.end = t < 0 ? 0 : t;
|
||||||
|
|
||||||
Refresh();
|
Refresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Bind(wxEVT_MOTION, [=](wxMouseEvent &ev){
|
Bind(wxEVT_MOTION, [=](wxMouseEvent &ev){
|
||||||
auto f = (Frame*) GetParent();
|
auto f = globaldis;
|
||||||
|
|
||||||
if(HasCapture()) {
|
if(HasCapture()) {
|
||||||
if(captureMode == Timeline::CaptureMode::CAM) {
|
if(captureMode == Timeline::CaptureMode::CAM) {
|
||||||
@@ -255,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:
|
||||||
@@ -319,7 +323,7 @@ Timeline::Timeline(struct Frame *parent) : wxPanel(parent, wxID_ANY) {
|
|||||||
|
|
||||||
menu.Bind(wxEVT_MENU, [=](wxCommandEvent &ev){
|
menu.Bind(wxEVT_MENU, [=](wxCommandEvent &ev){
|
||||||
if(ev.GetId() == idDel) {
|
if(ev.GetId() == idDel) {
|
||||||
auto f = (Frame*) GetParent();
|
auto f = globaldis;
|
||||||
|
|
||||||
CHi_DeleteKeyframe(f->graph->backendNG, kfs, kfIdx);
|
CHi_DeleteKeyframe(f->graph->backendNG, kfs, kfIdx);
|
||||||
|
|
||||||
@@ -337,7 +341,7 @@ Timeline::Timeline(struct Frame *parent) : wxPanel(parent, wxID_ANY) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::Paint(wxPaintEvent &ev) {
|
void Timeline::Paint(wxPaintEvent &ev) {
|
||||||
auto frame = (Frame*) GetParent();
|
auto frame = globaldis;
|
||||||
|
|
||||||
wxPaintDC dc{this};
|
wxPaintDC dc{this};
|
||||||
|
|
||||||
@@ -405,6 +409,10 @@ void Timeline::Paint(wxPaintEvent &ev) {
|
|||||||
|
|
||||||
if(end == 0) {
|
if(end == 0) {
|
||||||
end = gn->logical->ng->duration;
|
end = gn->logical->ng->duration;
|
||||||
|
if(end == -1) {
|
||||||
|
// Hack :)
|
||||||
|
end = 3600 * 10;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
start *= scale;
|
start *= scale;
|
||||||
|
|||||||
Reference in New Issue
Block a user