Move to EBML-based project file format
This commit is contained in:
parent
33c9207947
commit
b6e6713860
8
hi/mkv.c
8
hi/mkv.c
@ -294,14 +294,14 @@ static int muxmkv_perform(CHiPubNode *pubn) {
|
|||||||
|
|
||||||
struct Internal *this = (void*) pubn;
|
struct Internal *this = (void*) pubn;
|
||||||
|
|
||||||
if(pubn->sinks[0].data.linked.to) {
|
if(pubn->sinks[0].linked.to) {
|
||||||
CHiBSFrames *vp9bs = CHi_Crawl(&pubn->sinks[0])->data.bitstream;
|
CHiBSFrames *vp9bs = CHi_Crawl(&pubn->sinks[0])->data.bitstream;
|
||||||
if(vp9bs) {
|
if(vp9bs) {
|
||||||
this->videoBacklog = CHi_BS_Combine(this->videoBacklog, vp9bs);
|
this->videoBacklog = CHi_BS_Combine(this->videoBacklog, vp9bs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
if(opus) {
|
if(opus) {
|
||||||
this->audioBacklog = CHi_BS_Combine(this->audioBacklog, opus);
|
this->audioBacklog = CHi_BS_Combine(this->audioBacklog, opus);
|
||||||
@ -550,12 +550,12 @@ static int muxmkv_start(CHiPubNode *pubn) {
|
|||||||
|
|
||||||
int trackNum = 1;
|
int trackNum = 1;
|
||||||
|
|
||||||
if(pubn->sinks[0].data.linked.to) {
|
if(pubn->sinks[0].linked.to) {
|
||||||
this->videoBacklog = CHi_BS_Empty();
|
this->videoBacklog = CHi_BS_Empty();
|
||||||
this->videoTrack = trackNum++;
|
this->videoTrack = trackNum++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pubn->sinks[1].data.linked.to) {
|
if(pubn->sinks[1].linked.to) {
|
||||||
this->audioBacklog = CHi_BS_Empty();
|
this->audioBacklog = CHi_BS_Empty();
|
||||||
this->audioTrack = trackNum++;
|
this->audioTrack = trackNum++;
|
||||||
}
|
}
|
||||||
|
478
hi/node.c
478
hi/node.c
@ -19,55 +19,7 @@
|
|||||||
|
|
||||||
#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) {
|
|
||||||
float a = *(float*) A;
|
|
||||||
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 +77,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 +115,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 +161,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 +309,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 +326,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,6 +334,8 @@ 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];
|
||||||
|
|
||||||
|
// 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) {
|
||||||
n->Perform(n);
|
n->Perform(n);
|
||||||
|
|
||||||
if(error_changes(n)) {
|
if(error_changes(n)) {
|
||||||
@ -401,6 +343,15 @@ static void perform_step(CHiNodeGraph *ng) {
|
|||||||
ng->eventOnError(ng, n);
|
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);
|
||||||
@ -917,49 +868,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;
|
||||||
@ -1300,281 +1208,55 @@ 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();
|
|
||||||
} else if(type == CUTIHI_T('CExp','Mkv ')) {
|
|
||||||
n = CHi_MuxMatroska();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
22
hi/node.h
22
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;
|
||||||
|
|
||||||
@ -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);
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
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;
|
||||||
|
}
|
@ -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;
|
||||||
|
237
ui/frame.cpp
237
ui/frame.cpp
@ -22,13 +22,26 @@
|
|||||||
#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) {
|
||||||
|
globaldis->viewer->SetImage(val->data.sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = {
|
||||||
@ -224,76 +237,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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -347,6 +324,86 @@ 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);
|
||||||
|
|
||||||
|
tlba->Realize();
|
||||||
|
|
||||||
|
aui.SetFlags(wxAUI_MGR_LIVE_RESIZE | wxAUI_MGR_DEFAULT);
|
||||||
|
aui.Update();
|
||||||
|
|
||||||
|
Centre();
|
||||||
|
|
||||||
|
LoadProject("");
|
||||||
|
graph->CreatePreviewNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame::~Frame() {
|
||||||
|
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){
|
graph->backendNG->eventOnStopComplete = +[](CHiNodeGraph *ng){
|
||||||
wxTheApp->CallAfter([ng](){
|
wxTheApp->CallAfter([ng](){
|
||||||
wxButton *btn = ((Frame*) ng->ud)->toolbar.btnPerform;
|
wxButton *btn = ((Frame*) ng->ud)->toolbar.btnPerform;
|
||||||
@ -367,19 +424,14 @@ Frame::Frame() : wxFrame(NULL, wxID_ANY, "Cuticle", wxDefaultPosition, {wxSystem
|
|||||||
gn->Refresh();
|
gn->Refresh();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
tlba->Realize();
|
if(ud.f) {
|
||||||
|
fclose(ud.f);
|
||||||
|
}
|
||||||
|
|
||||||
aui.SetFlags(wxAUI_MGR_LIVE_RESIZE | wxAUI_MGR_DEFAULT);
|
// Force everything in the graph to be computed so the preview immediately shows up
|
||||||
aui.Update();
|
CHi_Hysteresis(graph->backendNG);
|
||||||
|
|
||||||
Centre();
|
|
||||||
|
|
||||||
graph->CreatePreviewNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
Frame::~Frame() {
|
|
||||||
aui.UnInit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GrNode::MouseOverPort(wxPoint point, bool &source, int &i) {
|
bool GrNode::MouseOverPort(wxPoint point, bool &source, int &i) {
|
||||||
@ -400,7 +452,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) {
|
||||||
@ -1048,7 +1100,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);
|
||||||
@ -1114,17 +1166,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1161,7 +1212,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) {
|
||||||
@ -1197,17 +1248,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]);
|
|
||||||
|
|
||||||
if(val->type == CUTIHI_VAL_SAMPLE && val->data.sample) {
|
void NodeGraph::DestroyNode(GrNode *gn) {
|
||||||
globaldis->viewer->SetImage(val->data.sample);
|
for(auto it = links.begin(); it != links.end();) {
|
||||||
|
if(it->input == gn || it->output == gn) {
|
||||||
|
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,6 +43,8 @@ struct Frame : wxFrame {
|
|||||||
|
|
||||||
Frame();
|
Frame();
|
||||||
virtual ~Frame();
|
virtual ~Frame();
|
||||||
|
|
||||||
|
void LoadProject(std::string filename);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GrNode : wxPanel {
|
struct GrNode : wxPanel {
|
||||||
@ -136,6 +138,8 @@ struct NodeGraph : wxPanel {
|
|||||||
|
|
||||||
void CreatePreviewNode();
|
void CreatePreviewNode();
|
||||||
|
|
||||||
|
void DestroyNode(GrNode*);
|
||||||
|
|
||||||
GrNode *get_graphical(CHiPubNode*);
|
GrNode *get_graphical(CHiPubNode*);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include"frame.h"
|
#include"frame.h"
|
||||||
|
|
||||||
|
Frame *globaldis;
|
||||||
|
|
||||||
struct App : wxApp {
|
struct App : wxApp {
|
||||||
virtual bool OnInit() {
|
virtual bool OnInit() {
|
||||||
(new Frame())->Show(true);
|
(new Frame())->Show(true);
|
||||||
|
@ -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) {
|
||||||
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user