Use spatial histogram image input for HyperHDR instead of solid color

This commit is contained in:
matze19999 committed 2026-10-06 18:15:10 +02:00
1 parent 90c4c88942
commit ae01879d45
13 files changed
+80 -14

No files matched your search

+6 -2
View File
@@ -25,6 +25,7 @@
#include "autostart.h"
#include "retry.h"
#include "target.h"
#include "spatial_image.h"
#define SERVICE "org.webosbrew.histogramhyperion.service"
#define PRIORITY 149
typedef struct {int code;char *message;const char *file;int line;const char *func;void *padding;unsigned long magic;} LSError;
@@ -196,7 +197,7 @@ static bool status(void *h,void *m,void *ctx){
(void)ctx;char json[4096],safe[512],endpoint[80];size_t n=0;
snprintf(endpoint,sizeof(endpoint),strchr(target.host,':')?"[%s]:%u":"%s:%u",target.host,target.port);
for(size_t i=0;last_error[i] && n+2<sizeof(safe);i++){char c=last_error[i];if(c=='"'||c=='\\')safe[n++]='\\';if((unsigned char)c>=32)safe[n++]=c;}safe[n]=0;
int used=snprintf(json,sizeof(json),"{\"returnValue\":true,\"version\":\"0.8.0\",\"running\":%s,\"colorValid\":%s,\"frames\":%lu,\"captureRecoveries\":%u,\"lastRecoveryMs\":%llu,\"endpoint\":\"%s\",\"host\":\"%s\",\"port\":%u,\"receiver\":\"%s\",\"tokenSet\":%s,\"piccapTargetCompatible\":%s,\"priority\":%d,\"uid\":%u,\"drmVerified\":false,\"mappingVerified\":false,\"outputColor\":[%u,%u,%u],\"error\":\"%s\",\"regions\":[",(capture||capture_source==SOURCE_PICCAP)?"true":"false",color_valid?"true":"false",frames,capture_recoveries,(unsigned long long)last_recovery_ms,endpoint,target.host,target.port,target.receiver,target.token[0]?"true":"false",target_piccap_local(&target)?"true":"false",PRIORITY,(unsigned)geteuid(),sent_rgb[0],sent_rgb[1],sent_rgb[2],safe);
int used=snprintf(json,sizeof(json),"{\"returnValue\":true,\"version\":\"0.8.1\",\"running\":%s,\"colorValid\":%s,\"frames\":%lu,\"captureRecoveries\":%u,\"lastRecoveryMs\":%llu,\"endpoint\":\"%s\",\"host\":\"%s\",\"port\":%u,\"receiver\":\"%s\",\"tokenSet\":%s,\"piccapTargetCompatible\":%s,\"priority\":%d,\"uid\":%u,\"drmVerified\":false,\"mappingVerified\":false,\"outputColor\":[%u,%u,%u],\"error\":\"%s\",\"regions\":[",(capture||capture_source==SOURCE_PICCAP)?"true":"false",color_valid?"true":"false",frames,capture_recoveries,(unsigned long long)last_recovery_ms,endpoint,target.host,target.port,target.receiver,target.token[0]?"true":"false",target_piccap_local(&target)?"true":"false",PRIORITY,(unsigned)geteuid(),sent_rgb[0],sent_rgb[1],sent_rgb[2],safe);
for(int i=0;i<16;i++)used+=snprintf(json+used,sizeof(json)-used,"%s[%u,%u,%u]",i?",":"",rgb[i][0],rgb[i][1],rgb[i][2]);
used+=snprintf(json+used,sizeof(json)-used,"],\"ledCount\":%u,\"spatialOutput\":%s,\"blackPercent\":[",led_count,led_count?"true":"false");
for(int i=0;i<16;i++)used+=snprintf(json+used,sizeof(json)-used,"%s%u",i?",":"",black_percent[i]);
@@ -246,8 +247,10 @@ static int tick(void *unused){
}
for(int c=0;c<3;c++)sent_rgb[c]=(unsigned char)(aggregate[c]/16);
unsigned char boosted_mean[3];boost_color(sent_rgb,boosted_mean);memcpy(sent_rgb,boosted_mean,3);
size_t capacity=256+(size_t)led_count*12;char *line=malloc(capacity);
bool image_output=strcmp(target.receiver,"HyperHDR")==0;
size_t capacity=256+(size_t)led_count*12;char *line=image_output?spatial_image_command(rgb,PRIORITY):malloc(capacity);
if(!line){set_error("Color buffer allocation failed");stop_capture();return 1;}
if(!image_output){
size_t written=(size_t)snprintf(line,capacity,"{\"command\":\"color\",\"priority\":%d,\"duration\":1500,\"origin\":\"HyperDRM\",\"color\":[",PRIORITY);
for(unsigned i=0;i<led_count;i++){
unsigned char interpolated[3],boosted[3];LedBlend blend=led_blends[i];
@@ -255,6 +258,7 @@ static int tick(void *unused){
written+=(size_t)snprintf(line+written,capacity-written,"%s%u,%u,%u",i?",":"",boosted[0],boosted[1],boosted[2]);
}
snprintf(line+written,capacity-written,"]}\n");
}
int failed=send_line(line);free(line);
if(failed){set_error("Receiver connection lost");stop_capture();return 1;}
char answer[4096];size_t used=0;bool complete=false;
+29
View File
@@ -0,0 +1,29 @@
#ifndef HYPERDRM_SPATIAL_IMAGE_H
#define HYPERDRM_SPATIAL_IMAGE_H
#include <stdlib.h>
#include <stdio.h>
#include "layout.h"
#include "interpolation.h"
#include "color_boost.h"
enum {SPATIAL_WIDTH=160,SPATIAL_HEIGHT=90,SPATIAL_PIXELS=SPATIAL_WIDTH*SPATIAL_HEIGHT};
/* Synthetic color plane from histogram samples, never captured video pixels.
* HyperHDR 22 treats JSON color as a solid color; image input retains space.
* The receiver samples this plane using its configured LED rectangles. */
static char *spatial_image_command(const unsigned char regions[16][3],unsigned priority){
static LedBlend blends[SPATIAL_PIXELS];static bool initialized;
if(!initialized){
for(unsigned y=0;y<SPATIAL_HEIGHT;y++)for(unsigned x=0;x<SPATIAL_WIDTH;x++)
blends[y*SPATIAL_WIDTH+x]=layout_blend((x+0.5)/SPATIAL_WIDTH,(y+0.5)/SPATIAL_HEIGHT);
initialized=true;
}
const char alphabet[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t capacity=SPATIAL_PIXELS*4+256;char *line=malloc(capacity);if(!line)return NULL;
size_t used=snprintf(line,capacity,"{\"command\":\"image\",\"priority\":%u,\"duration\":1500,\"origin\":\"HyperDRM\",\"imagewidth\":%u,\"imageheight\":%u,\"format\":\"rgb\",\"name\":\"Histogram colors\",\"imagedata\":\"",priority,SPATIAL_WIDTH,SPATIAL_HEIGHT);
for(unsigned i=0;i<SPATIAL_PIXELS;i++){
unsigned char rgb[3],color[3];LedBlend b=blends[i];blend_color(regions[b.a],regions[b.b],b.weight,rgb);boost_color(rgb,color);
line[used++]=alphabet[color[0]>>2];line[used++]=alphabet[((color[0]&3)<<4)|(color[1]>>4)];
line[used++]=alphabet[((color[1]&15)<<2)|(color[2]>>6)];line[used++]=alphabet[color[2]&63];
}
line[used++]='"';line[used++]='}';line[used++]='\n';line[used]=0;return line;
}
#endif
+17
View File
@@ -0,0 +1,17 @@
#include <assert.h>
#include <string.h>
#include "spatial_image.h"
int main(void){
(void)layout_from_json;
unsigned char regions[16][3];for(unsigned i=0;i<16;i++){regions[i][0]=11;regions[i][1]=22;regions[i][2]=33;}
char *command=spatial_image_command(regions,149);assert(command);
assert(strstr(command,"\"command\":\"image\"") && strstr(command,"\"priority\":149") && strstr(command,"\"format\":\"rgb\""));
const char *encoded=strstr(command,"\"imagedata\":\"")+13;
for(unsigned i=0;i<SPATIAL_PIXELS;i++)assert(!memcmp(encoded+i*4,"CxYh",4));
assert(!strcmp(encoded+SPATIAL_PIXELS*4,"\"}\n"));free(command);
for(unsigned i=0;i<16;i++){regions[i][0]=i*16;regions[i][1]=255-i*16;regions[i][2]=i*8;}
command=spatial_image_command(regions,149);encoded=strstr(command,"\"imagedata\":\"")+13;
assert(memcmp(encoded,encoded+(SPATIAL_WIDTH-1)*4,4));
assert(memcmp(encoded,encoded+(SPATIAL_PIXELS-SPATIAL_WIDTH)*4,4));free(command);
puts("PASS: image command, exact neutral RGB base64, spatial edge differences and cached geometry");return 0;
}