Captures the TV's audio and sends it out over several transports. The
primary one is HyperHDR: RTP/L16 to a host-side loopback device, since
HyperHDR has no network audio input of its own. A second route renders
the spectrum on the TV and sends FlatBuffers images to port 19400
instead, for setups where touching the host's sound config is not an
option.
native/ the service: capture backends (PulseAudio, ALSA, exec,
test tone, all dlopen-based), DSP, and one file per sink
frontend/ D-pad driven UI at a fixed 1920x1080
servicefiles/ native service manifest plus the boot script
host/ RTP receiver and the loopback installer for the HyperHDR
machine
tools/ build/package, asset generation, Homebrew Channel
manifest, on-TV probe
test/ host-side suites: FlatBuffers and RTP verified against
real decoders, the engine end to end, the page in jsdom
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
78 lines
2.2 KiB
C
78 lines
2.2 KiB
C
// Harness for verify_rtp.py: opens the real HyperHDR RTP sink, points it at a
|
|
// port on the loopback and writes a deterministic ramp through it. The Python
|
|
// side receives the datagrams and checks that what comes out of the wire is
|
|
// exactly what went in.
|
|
//
|
|
// rtp_send <port> <blocks> [frames-per-block] [sap]
|
|
|
|
#include "../native/src/common/audio.h"
|
|
#include "../native/src/common/json.h"
|
|
#include "../native/src/common/log.h"
|
|
#include "../native/src/dsp.h"
|
|
#include "../native/src/sinks/sink.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// Must match sample_at() in verify_rtp.py.
|
|
static int16_t sample_at(long index)
|
|
{
|
|
return (int16_t)((index * 251) % 65536 - 32768);
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
if (argc < 3) {
|
|
fprintf(stderr, "usage: %s <port> <blocks> [frames]\n", argv[0]);
|
|
return 2;
|
|
}
|
|
|
|
int port = atoi(argv[1]);
|
|
int blocks = atoi(argv[2]);
|
|
int frames = argc > 3 ? atoi(argv[3]) : AUDIO_BLOCK_FRAMES;
|
|
bool sap = argc > 4 && strcmp(argv[4], "sap") == 0;
|
|
|
|
log_set_level(LOG_ERROR);
|
|
|
|
char cfg_text[256];
|
|
snprintf(cfg_text, sizeof(cfg_text),
|
|
"{\"hyperhdr\":{\"host\":\"127.0.0.1\",\"port\":%d,"
|
|
"\"multicast\":false,\"sapAnnounce\":%s}}",
|
|
port, sap ? "true" : "false");
|
|
|
|
json_value_t* cfg = json_parse(cfg_text);
|
|
if (!cfg) {
|
|
fprintf(stderr, "bad config\n");
|
|
return 1;
|
|
}
|
|
|
|
audio_format_t fmt = { .rate = 48000, .channels = 2 };
|
|
char err[256] = { 0 };
|
|
sink_t* sink = sink_open("hyperhdr", cfg, &fmt, err, sizeof(err));
|
|
if (!sink) {
|
|
fprintf(stderr, "sink_open failed: %s\n", err);
|
|
json_free(cfg);
|
|
return 1;
|
|
}
|
|
|
|
int16_t* pcm = malloc((size_t)frames * fmt.channels * sizeof(int16_t));
|
|
dsp_levels_t levels;
|
|
memset(&levels, 0, sizeof(levels));
|
|
|
|
long index = 0;
|
|
for (int b = 0; b < blocks; b++) {
|
|
for (int i = 0; i < frames * fmt.channels; i++)
|
|
pcm[i] = sample_at(index++);
|
|
sink->write(sink, pcm, frames, &levels);
|
|
}
|
|
|
|
printf("%ld\n", index); // samples written, for the receiver to expect
|
|
fflush(stdout);
|
|
|
|
free(pcm);
|
|
sink_close(sink);
|
|
json_free(cfg);
|
|
return 0;
|
|
}
|