add FLAC encoding support

This commit is contained in:
Steve Markgraf 2025-08-28 23:57:15 +02:00
parent c9b0837c92
commit d0984dac17
9 changed files with 243 additions and 40 deletions

View file

@ -88,6 +88,16 @@ else()
set(LIBUVC_INCLUDE_DIRS "" CACHE STRING "manual libuvc includepath") set(LIBUVC_INCLUDE_DIRS "" CACHE STRING "manual libuvc includepath")
endif() endif()
if(PKG_CONFIG_FOUND)
pkg_check_modules(LIBFLAC flac IMPORTED_TARGET)
if(LIBFLAC_LINK_LIBRARIES)
set(LIBFLAC_LIBRARIES "${LIBFLAC_LINK_LIBRARIES}")
endif()
else()
set(LIBFLAC_LIBRARIES "" CACHE STRING "manual FLAC path")
set(LIBFLAC_INCLUDE_DIRS "" CACHE STRING "manual FLAC includepath")
endif()
if(MSVC) if(MSVC)
set(THREADS_PTHREADS_LIBRARY "" CACHE STRING "manual pthread-win32 path") set(THREADS_PTHREADS_LIBRARY "" CACHE STRING "manual pthread-win32 path")
set(THREADS_PTHREADS_INCLUDE_DIR "" CACHE STRING "manual pthread-win32 includepath") set(THREADS_PTHREADS_INCLUDE_DIR "" CACHE STRING "manual pthread-win32 includepath")
@ -102,6 +112,9 @@ endif()
if(PKG_CONFIG_FOUND AND NOT LIBUVC_FOUND) if(PKG_CONFIG_FOUND AND NOT LIBUVC_FOUND)
message(FATAL_ERROR "LibUVC required to compile hsdaoh") message(FATAL_ERROR "LibUVC required to compile hsdaoh")
endif() endif()
if(PKG_CONFIG_FOUND AND NOT LIBFLAC_FOUND)
message(FATAL_ERROR "LibFLAC required to compile hsdaoh")
endif()
if(NOT THREADS_FOUND) if(NOT THREADS_FOUND)
message(FATAL_ERROR "pthreads(-win32) required to compile hsdaoh") message(FATAL_ERROR "pthreads(-win32) required to compile hsdaoh")
endif() endif()
@ -165,6 +178,14 @@ FOREACH(lib ${LIBUVC_LIBRARY_DIRS})
LIST(APPEND hsdaoh_PC_LIBS "-L${lib}") LIST(APPEND hsdaoh_PC_LIBS "-L${lib}")
ENDFOREACH(lib) ENDFOREACH(lib)
FOREACH(inc ${LIBFLAC_INCLUDEDIR})
LIST(APPEND hsdaoh_PC_CFLAGS "-I${inc}")
ENDFOREACH(inc)
FOREACH(lib ${LIBFLAC_LIBRARY_DIRS})
LIST(APPEND hsdaoh_PC_LIBS "-L${lib}")
ENDFOREACH(lib)
# use space-separation format for the pc file # use space-separation format for the pc file
STRING(REPLACE ";" " " hsdaoh_PC_CFLAGS "${hsdaoh_PC_CFLAGS}") STRING(REPLACE ";" " " hsdaoh_PC_CFLAGS "${hsdaoh_PC_CFLAGS}")
STRING(REPLACE ";" " " hsdaoh_PC_LIBS "${hsdaoh_PC_LIBS}") STRING(REPLACE ";" " " hsdaoh_PC_LIBS "${hsdaoh_PC_LIBS}")

View file

@ -42,7 +42,7 @@ There is one additional dependency to abstract the video input however (libuvc).
To install the build dependencies on a distribution based on Debian (e.g. Ubuntu), run the following command: To install the build dependencies on a distribution based on Debian (e.g. Ubuntu), run the following command:
sudo apt-get install build-essential cmake pkgconf libusb-1.0-0-dev libuvc-dev sudo apt-get install build-essential cmake pkgconf libusb-1.0-0-dev libuvc-dev libflac-dev
To build hsdaoh: To build hsdaoh:
@ -80,7 +80,7 @@ pacman -Suy
# Install the required dependencies: # Install the required dependencies:
pacman -S git zip mingw-w64-x86_64-libusb mingw-w64-x86_64-libwinpthread mingw-w64-x86_64-cc \ pacman -S git zip mingw-w64-x86_64-libusb mingw-w64-x86_64-libwinpthread mingw-w64-x86_64-cc \
mingw-w64-x86_64-gcc-libs mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc-libs mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-flac
``` ```
#### Build libuvc #### Build libuvc
@ -88,7 +88,7 @@ mingw-w64-x86_64-gcc-libs mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja
# Clone the repository: # Clone the repository:
git clone https://github.com/steve-m/libuvc.git git clone https://github.com/steve-m/libuvc.git
mkdir libuvc/build && cd libuvc/build mkdir libuvc/build && cd libuvc/build
cmake ../ -DCMAKE_INSTALL_PREFIX:PATH=/mingw64 cmake ../ -DCMAKE_POLICY_VERSION_MINIMUM=3.10 -DCMAKE_INSTALL_PREFIX:PATH=/mingw64
cmake --build . cmake --build .
cmake --install . cmake --install .
``` ```
@ -101,7 +101,7 @@ mkdir hsdaoh/build && cd hsdaoh/build
cmake ../ cmake ../
cmake --build . cmake --build .
# Gather all files required for release # Gather all files required for release
zip -j hsdaoh_win_release.zip src/*.exe src/*.dll /mingw64/bin/libusb-1.0.dll /mingw64/bin/libuvc.dll /mingw64/bin/libwinpthread-1.dll zip -j hsdaoh_win_release.zip src/*.exe src/*.dll /mingw64/bin/libusb-1.0.dll /mingw64/bin/libuvc.dll /mingw64/bin/libwinpthread-1.dll /mingw64/bin/libFLAC.dll /mingw64/bin/libogg-0.dll
``` ```
### Mac OS X ### Mac OS X

View file

@ -1,6 +1,7 @@
include(FindPkgConfig) include(FindPkgConfig)
pkg_check_modules(LIBUSB libusb-1.0 IMPORTED_TARGET) pkg_check_modules(LIBUSB libusb-1.0 IMPORTED_TARGET)
pkg_check_modules(LIBUVC libuvc IMPORTED_TARGET) pkg_check_modules(LIBUVC libuvc IMPORTED_TARGET)
pkg_check_modules(LIBFLAC flac IMPORTED_TARGET)
get_filename_component(HSDAOH_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) get_filename_component(HSDAOH_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)

View file

@ -41,6 +41,11 @@ typedef struct hsdaoh_data_info {
unsigned char *buf; unsigned char *buf;
size_t len; /* buffer length */ size_t len; /* buffer length */
uint16_t stream_id; uint16_t stream_id;
uint32_t srate;
uint8_t nchans;
uint8_t bits_per_samp;
bool is_signed;
bool is_float;
bool device_error; /* device error happened, terminate application */ bool device_error; /* device error happened, terminate application */
} hsdaoh_data_info_t; } hsdaoh_data_info_t;

View file

@ -12,6 +12,7 @@ struct llist {
size_t len; size_t len;
uint16_t sid; uint16_t sid;
uint16_t format; uint16_t format;
uint32_t srate;
struct llist *next; struct llist *next;
}; };

View file

@ -85,6 +85,7 @@ add_executable(hsdaoh_test hsdaoh_test.c)
set(INSTALL_TARGETS hsdaoh hsdaoh_static hsdaoh_file hsdaoh_tcp hsdaoh_test) set(INSTALL_TARGETS hsdaoh hsdaoh_static hsdaoh_file hsdaoh_tcp hsdaoh_test)
target_link_libraries(hsdaoh_file hsdaoh target_link_libraries(hsdaoh_file hsdaoh
${LIBFLAC_LIBRARIES}
# ${LIBUSB_LIBRARIES} # ${LIBUSB_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_THREAD_LIBS_INIT}
) )

View file

@ -53,6 +53,10 @@ static inline void hsdaoh_16bit_to_float(hsdaoh_dev_t *dev, hsdaoh_data_info_t *
data_info->buf = (uint8_t *)floats; data_info->buf = (uint8_t *)floats;
data_info->len = j * sizeof(float); data_info->len = j * sizeof(float);
data_info->bits_per_samp = 32;
data_info->nchans = 1;
data_info->is_signed = true;
data_info->is_float = true;
dev->cb(data_info); dev->cb(data_info);
free(floats); free(floats);
@ -81,6 +85,11 @@ void hsdaoh_unpack_pio_12bit(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info)
} else { } else {
data_info->buf = (uint8_t *)out; data_info->buf = (uint8_t *)out;
data_info->len = j * sizeof(uint16_t); data_info->len = j * sizeof(uint16_t);
data_info->bits_per_samp = 12;
data_info->nchans = 1;
data_info->is_signed = false;
data_info->is_float = false;
dev->cb(data_info); dev->cb(data_info);
} }
@ -112,11 +121,18 @@ void hsdaoh_unpack_pio_12bit_dual(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_in
out16_2[i] = out[i] & 0x0fff; out16_2[i] = out[i] & 0x0fff;
} }
data_info->bits_per_samp = 12;
data_info->nchans = 1;
if (dev->output_float) { if (dev->output_float) {
hsdaoh_16bit_to_float(dev, data_info, out16_1, i, 2047.5, true); hsdaoh_16bit_to_float(dev, data_info, out16_1, i, 2047.5, true);
} else { } else {
data_info->buf = (uint8_t *)out16_1; data_info->buf = (uint8_t *)out16_1;
data_info->len = i * sizeof(uint16_t); data_info->len = i * sizeof(uint16_t);
data_info->bits_per_samp = 12;
data_info->nchans = 1;
data_info->is_signed = false;
data_info->is_float = false;
dev->cb(data_info); dev->cb(data_info);
} }
@ -157,6 +173,10 @@ void hsdaoh_unpack_pio_8bit_iq(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info)
} else { } else {
data_info->buf = (uint8_t *)in; data_info->buf = (uint8_t *)in;
data_info->len = inlen; data_info->len = inlen;
data_info->bits_per_samp = 8;
data_info->nchans = 2;
data_info->is_signed = false;
data_info->is_float = false;
dev->cb(data_info); dev->cb(data_info);
} }
@ -202,6 +222,10 @@ void hsdaoh_unpack_pio_10bit_iq(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info
} else { } else {
data_info->buf = (uint8_t *)iq_samps; data_info->buf = (uint8_t *)iq_samps;
data_info->len = out_samps * sizeof(uint16_t); data_info->len = out_samps * sizeof(uint16_t);
data_info->bits_per_samp = 10;
data_info->nchans = 2;
data_info->is_signed = false;
data_info->is_float = false;
dev->cb(data_info); dev->cb(data_info);
} }
@ -215,14 +239,24 @@ void hsdaoh_unpack_pio_pcm1802_audio(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data
uint32_t *in = (uint32_t *)data_info->buf; uint32_t *in = (uint32_t *)data_info->buf;
size_t inlen = data_info->len / sizeof(uint32_t); size_t inlen = data_info->len / sizeof(uint32_t);
/* convert from S24LE to S32LE */ /* output as S24_3LE */
for (unsigned int i = 0; i < inlen; i++) uint8_t *out = malloc(inlen * 3);
in[i] <<= 8; int j = 0;
data_info->buf = (uint8_t *)in; for (unsigned int i = 0; i < inlen; i++) {
data_info->len = inlen * sizeof(uint32_t); out[j++] = in[i] & 0xff;
out[j++] = (in[i] >> 8) & 0xff;
out[j++] = (in[i] >> 16) & 0xff;
}
data_info->buf = (uint8_t *)out;
data_info->len = j;
data_info->bits_per_samp = 24;
data_info->nchans = 2;
data_info->is_signed = true;
data_info->is_float = false;
dev->cb(data_info); dev->cb(data_info);
free(out);
} }
void hsdaoh_unpack_fpga_12bit_dual(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info) void hsdaoh_unpack_fpga_12bit_dual(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info)
@ -250,6 +284,10 @@ void hsdaoh_unpack_fpga_12bit_dual(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_i
} else { } else {
data_info->buf = (uint8_t *)out16_1; data_info->buf = (uint8_t *)out16_1;
data_info->len = j * sizeof(uint16_t); data_info->len = j * sizeof(uint16_t);
data_info->bits_per_samp = 12;
data_info->nchans = 1;
data_info->is_signed = false;
data_info->is_float = false;
dev->cb(data_info); dev->cb(data_info);
} }

View file

@ -35,15 +35,23 @@
#define usleep(t) Sleep((t)/1000) #define usleep(t) Sleep((t)/1000)
#endif #endif
#include "FLAC/metadata.h"
#include "FLAC/stream_encoder.h"
#include "hsdaoh.h" #include "hsdaoh.h"
#define FD_NUMS 4 #define FD_NUMS 4
static int do_exit = 0; static bool do_exit = false;
static hsdaoh_dev_t *dev = NULL; static hsdaoh_dev_t *dev = NULL;
static uint32_t flac_level = 5;
static uint32_t flac_nthreads = 4;
typedef struct file_ctx { typedef struct file_ctx {
FILE *files[FD_NUMS]; FILE *files[FD_NUMS];
bool use_flac[FD_NUMS];
FLAC__StreamEncoder *encoder[FD_NUMS];
FLAC__StreamMetadata *seektable[FD_NUMS];
} file_ctx_t; } file_ctx_t;
void usage(void) void usage(void)
@ -52,9 +60,14 @@ void usage(void)
"hsdaoh_file, HDMI data acquisition tool\n\n" "hsdaoh_file, HDMI data acquisition tool\n\n"
"Usage:\n" "Usage:\n"
"\t[-d device_index (default: 0)]\n" "\t[-d device_index (default: 0)]\n"
"\t[-b maximum number of buffers (default: 16)]\n" "\t[-b maximum number of buffers (default: 96)]\n"
"\t[-0 to -3 filename of steam 0 to stream 3 (a '-' dumps samples to stdout)]\n" "\t[-l FLAC compression level (default: 5)]\n"
"\tfilename (of stream 0) (a '-' dumps samples to stdout)\n\n"); #ifdef FLAC__STREAM_ENCODER_SET_NUM_THREADS_OK
"\t[-t number of threads for FLAC encoding (default: 4)]\n"
#endif
"\t[-0 to -3 filename of stream 0 to stream 3 (a '-' dumps samples to stdout)]\n"
"\tfilename (of stream 0) (a '-' dumps samples to stdout)\n\n"
"\tFilenames with the extension .flac will enable FLAC encoding\n\n");
exit(1); exit(1);
} }
@ -64,7 +77,7 @@ sighandler(int signum)
{ {
if (CTRL_C_EVENT == signum) { if (CTRL_C_EVENT == signum) {
fprintf(stderr, "Signal caught, exiting!\n"); fprintf(stderr, "Signal caught, exiting!\n");
do_exit = 1; do_exit = true;
hsdaoh_stop_stream(dev); hsdaoh_stop_stream(dev);
return TRUE; return TRUE;
} }
@ -75,7 +88,7 @@ static void sighandler(int signum)
{ {
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
fprintf(stderr, "Signal caught, exiting!\n"); fprintf(stderr, "Signal caught, exiting!\n");
do_exit = 1; do_exit = true;
hsdaoh_stop_stream(dev); hsdaoh_stop_stream(dev);
} }
#endif #endif
@ -97,6 +110,102 @@ static void hsdaoh_callback(hsdaoh_data_info_t *data_info)
if (!file) if (!file)
return; return;
FLAC__StreamEncoder *encoder = f->encoder[data_info->stream_id];
/* allocate FLAC encoder if required */
if (f->use_flac[data_info->stream_id] && !encoder) {
FLAC__bool ret = true;
FLAC__StreamEncoderInitStatus init_status;
if ((encoder = FLAC__stream_encoder_new()) == NULL) {
fprintf(stderr, "ERROR: failed allocating FLAC encoder\n");
do_exit = true;
return;
}
ret &= FLAC__stream_encoder_set_verify(encoder, false);
ret &= FLAC__stream_encoder_set_compression_level(encoder, flac_level);
ret &= FLAC__stream_encoder_set_sample_rate(encoder,
data_info->srate > FLAC__MAX_SAMPLE_RATE ?
data_info->srate/1000 : data_info->srate);
ret &= FLAC__stream_encoder_set_channels(encoder, data_info->nchans);
ret &= FLAC__stream_encoder_set_bits_per_sample(encoder, data_info->bits_per_samp);
ret &= FLAC__stream_encoder_set_total_samples_estimate(encoder, 0);
ret &= FLAC__stream_encoder_set_streamable_subset(encoder, false);
#ifdef FLAC__STREAM_ENCODER_SET_NUM_THREADS_OK
if (FLAC__stream_encoder_set_num_threads(encoder, flac_nthreads) != FLAC__STREAM_ENCODER_SET_NUM_THREADS_OK)
ret = false;
#endif
if (!ret) {
fprintf(stderr, "ERROR: failed initializing FLAC encoder\n");
do_exit = true;
return;
}
init_status = FLAC__stream_encoder_init_FILE(encoder, f->files[data_info->stream_id], NULL, NULL);
if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
fprintf(stderr, "ERROR: failed initializing FLAC encoder: %s\n", FLAC__StreamEncoderInitStatusString[init_status]);
do_exit = true;
return;
}
f->encoder[data_info->stream_id] = encoder;
}
if (f->use_flac[data_info->stream_id]) {
/* write FLAC output */
FLAC__bool ok = false;
FLAC__int32 offset = data_info->is_signed ? 0 : 1 << (data_info->bits_per_samp - 1);
int bytes_per_samp = ((data_info->bits_per_samp - 1) / 8) + 1;
int nsamps = len / bytes_per_samp;
FLAC__int32 *out = malloc(nsamps * sizeof(FLAC__int32));
int i = 0;
if (bytes_per_samp == 1) {
/* data encoded in uint8 */
uint8_t *dat = (uint8_t *)data_info->buf;
for (; i < nsamps; i++)
out[i] = dat[i] - offset;
} else if (bytes_per_samp == 2) {
/* data encoded in uint16 */
uint16_t *dat = (uint16_t *)data_info->buf;
for (; i < nsamps; i++)
out[i] = dat[i] - offset;
} else if (bytes_per_samp == 3) {
/* data encoded in 3 * uint8 */
uint8_t *dat = (uint8_t *)data_info->buf;
int j = 0;
for (; i < nsamps; i++) {
/* convert S24_3LE and take care of sign extension */
FLAC__int32 samp = (FLAC__int32)((dat[j+2] << 24) | (dat[j+1] << 16) | (dat[j] << 8));
//samp /= 256;
samp >>= (32 - data_info->bits_per_samp);
j += 3;
out[i] = samp - offset;
}
} else {
/* data encoded in uint32 */
uint32_t *dat = (uint32_t *)data_info->buf;
for (; i < nsamps; i++)
out[i] = dat[i] - offset;
}
ok = FLAC__stream_encoder_process_interleaved(f->encoder[data_info->stream_id], out, nsamps / data_info->nchans);
free(out);
if (!ok && encoder) {
fprintf(stderr, "ERROR: () FLAC encoder could not process data: %s\n",
FLAC__StreamEncoderStateString[FLAC__stream_encoder_get_state(encoder)]);
hsdaoh_stop_stream(dev);
}
} else {
/* write raw file output */
while (nbytes < len) { while (nbytes < len) {
nbytes += fwrite(data_info->buf + nbytes, 1, len - nbytes, file); nbytes += fwrite(data_info->buf + nbytes, 1, len - nbytes, file);
@ -107,6 +216,7 @@ static void hsdaoh_callback(hsdaoh_data_info_t *data_info)
} }
} }
} }
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@ -116,13 +226,13 @@ int main(int argc, char **argv)
char *filenames[FD_NUMS] = { NULL, }; char *filenames[FD_NUMS] = { NULL, };
int n_read; int n_read;
int r, opt; int r, opt;
file_ctx_t f; file_ctx_t f = { 0 };
int dev_index = 0; int dev_index = 0;
unsigned int num_bufs = 0; unsigned int num_bufs = 0;
bool fname0_used = false; bool fname0_used = false;
bool have_file = false; bool have_file = false;
while ((opt = getopt(argc, argv, "0:1:2:3:d:b:")) != -1) { while ((opt = getopt(argc, argv, "0:1:2:3:d:b:l:t:")) != -1) {
switch (opt) { switch (opt) {
case 'd': case 'd':
dev_index = (uint32_t)atoi(optarg); dev_index = (uint32_t)atoi(optarg);
@ -130,6 +240,12 @@ int main(int argc, char **argv)
case 'b': case 'b':
num_bufs = (unsigned int)atoi(optarg); num_bufs = (unsigned int)atoi(optarg);
break; break;
case 'l':
flac_level = atoi(optarg);
break;
case 't':
flac_nthreads = atoi(optarg);
break;
case '0': case '0':
fname0_used = true; fname0_used = true;
have_file = true; have_file = true;
@ -184,6 +300,7 @@ int main(int argc, char **argv)
for (int i = 0; i < FD_NUMS; i++) { for (int i = 0; i < FD_NUMS; i++) {
f.files[i] = NULL; f.files[i] = NULL;
f.encoder[i] = NULL;
if (!filenames[i]) if (!filenames[i])
continue; continue;
@ -199,24 +316,36 @@ int main(int argc, char **argv)
fprintf(stderr, "Failed to open %s\n", filenames[i]); fprintf(stderr, "Failed to open %s\n", filenames[i]);
goto out; goto out;
} }
char *dot = strrchr(filenames[i], '.');
if (dot && !strcmp(dot, ".flac"))
f.use_flac[i] = true;
else
f.use_flac[i] = false;
} }
} }
r = hsdaoh_start_stream(dev, hsdaoh_callback, (void *)&f, num_bufs); r = hsdaoh_start_stream(dev, hsdaoh_callback, (void *)&f, num_bufs);
while (!do_exit) { while (!do_exit)
usleep(50000); usleep(50000);
}
if (do_exit)
fprintf(stderr, "\nUser cancel, exiting...\n"); fprintf(stderr, "\nUser cancel, exiting...\n");
else
fprintf(stderr, "\nLibrary error %d, exiting...\n", r);
hsdaoh_close(dev); hsdaoh_close(dev);
for (int i = 0; i < FD_NUMS; i++) { for (int i = 0; i < FD_NUMS; i++) {
if (f.files[i] && (f.files[i] != stdout)) if (!f.files[i])
continue;
if (f.use_flac[i] && f.encoder[i]) {
FLAC__bool ret = FLAC__stream_encoder_finish(f.encoder[i]);
if (!ret)
fprintf(stderr, "ERROR: FLAC encoder did not finish correctly: %s\n",
FLAC__StreamEncoderStateString[FLAC__stream_encoder_get_state(f.encoder[i])]);
FLAC__stream_encoder_delete(f.encoder[i]);
} else if (f.files[i] != stdout)
fclose(f.files[i]); fclose(f.files[i]);
} }

View file

@ -52,7 +52,7 @@
#include <format_convert.h> #include <format_convert.h>
#include <crc.h> #include <crc.h>
#define DEFAULT_BUFFERS 16 #define DEFAULT_BUFFERS 96
typedef struct hsdaoh_adapter { typedef struct hsdaoh_adapter {
uint16_t vid; uint16_t vid;
@ -531,8 +531,6 @@ int hsdaoh_close(hsdaoh_dev_t *dev)
return 0; return 0;
} }
// maybe rename to preferred output format
// and add real output format to data_info_t
int hsdaoh_set_output_format(hsdaoh_dev_t *dev, hsdaoh_output_format_t format) int hsdaoh_set_output_format(hsdaoh_dev_t *dev, hsdaoh_output_format_t format)
{ {
if (!dev) if (!dev)
@ -542,13 +540,14 @@ int hsdaoh_set_output_format(hsdaoh_dev_t *dev, hsdaoh_output_format_t format)
return 0; return 0;
} }
void hsdaoh_output(hsdaoh_dev_t *dev, uint16_t sid, int format, uint8_t *data, size_t len) void hsdaoh_output(hsdaoh_dev_t *dev, uint16_t sid, int format, uint32_t srate, uint8_t *data, size_t len)
{ {
hsdaoh_data_info_t data_info; hsdaoh_data_info_t data_info;
data_info.ctx = dev->cb_ctx; data_info.ctx = dev->cb_ctx;
data_info.stream_id = sid; data_info.stream_id = sid;
data_info.buf = data; data_info.buf = data;
data_info.len = len; data_info.len = len;
data_info.srate = srate;
switch (format) { switch (format) {
case PIO_8BIT_IQ: case PIO_8BIT_IQ:
@ -604,7 +603,7 @@ static void *hsdaoh_output_worker(void *arg)
pthread_mutex_unlock(&dev->ll_mutex); pthread_mutex_unlock(&dev->ll_mutex);
while (curelem != NULL) { while (curelem != NULL) {
hsdaoh_output(dev, curelem->sid, curelem->format, curelem->data, curelem->len); hsdaoh_output(dev, curelem->sid, curelem->format, curelem->srate, curelem->data, curelem->len);
prev = curelem; prev = curelem;
curelem = curelem->next; curelem = curelem->next;
@ -614,7 +613,7 @@ static void *hsdaoh_output_worker(void *arg)
} }
} }
void hsdaoh_enqueue_data(hsdaoh_dev_t *dev, uint16_t sid, int format, uint8_t *data, size_t len) void hsdaoh_enqueue_data(hsdaoh_dev_t *dev, uint16_t sid, int format, uint32_t srate, uint8_t *data, size_t len)
{ {
if (dev->async_status != HSDAOH_RUNNING) { if (dev->async_status != HSDAOH_RUNNING) {
free(data); free(data);
@ -626,6 +625,7 @@ void hsdaoh_enqueue_data(hsdaoh_dev_t *dev, uint16_t sid, int format, uint8_t *d
rpt->len = len; rpt->len = len;
rpt->sid = sid; rpt->sid = sid;
rpt->format = format; rpt->format = format;
rpt->srate = srate;
rpt->next = NULL; rpt->next = NULL;
pthread_mutex_lock(&dev->ll_mutex); pthread_mutex_lock(&dev->ll_mutex);
@ -721,6 +721,11 @@ void hsdaoh_process_frame(hsdaoh_dev_t *dev, uint8_t *data, int size)
uint16_t stream0_format = 0; uint16_t stream0_format = 0;
uint8_t *stream0_data = malloc(dev->width-1 * dev->height * 2); uint8_t *stream0_data = malloc(dev->width-1 * dev->height * 2);
if (!stream0_data) {
fprintf(stderr, "Out of memory, frame skipped!\n");
return;
}
for (unsigned int i = 0; i < dev->height; i++) { for (unsigned int i = 0; i < dev->height; i++) {
uint8_t *line_dat = data + (dev->width * sizeof(uint16_t) * i); uint8_t *line_dat = data + (dev->width * sizeof(uint16_t) * i);
@ -745,6 +750,7 @@ void hsdaoh_process_frame(hsdaoh_dev_t *dev, uint8_t *data, int size)
fprintf(stderr, "Invalid payload length: %d\n", payload_len); fprintf(stderr, "Invalid payload length: %d\n", payload_len);
/* discard frame */ /* discard frame */
free(stream0_data);
return; return;
} }
@ -770,14 +776,15 @@ void hsdaoh_process_frame(hsdaoh_dev_t *dev, uint8_t *data, int size)
stream0_format = format; stream0_format = format;
} else { } else {
uint8_t *out_data = malloc(out_len); uint8_t *out_data = malloc(out_len);
uint32_t srate = stream_id < DEFAULT_MAX_STREAMS ? meta.stream_info[stream_id].srate : 0;
memcpy(out_data, line_dat, out_len); memcpy(out_data, line_dat, out_len);
hsdaoh_enqueue_data(dev, stream_id, format, out_data, out_len); hsdaoh_enqueue_data(dev, stream_id, format, srate, out_data, out_len);
} }
} }
} }
if (dev->stream_synced && stream0_payload_bytes) if (dev->stream_synced && stream0_payload_bytes)
hsdaoh_enqueue_data(dev, 0, stream0_format, stream0_data, stream0_payload_bytes); hsdaoh_enqueue_data(dev, 0, stream0_format, meta.stream_info[0].srate, stream0_data, stream0_payload_bytes);
else else
free(stream0_data); free(stream0_data);
@ -788,7 +795,7 @@ void hsdaoh_process_frame(hsdaoh_dev_t *dev, uint8_t *data, int size)
dev->frames_since_error++; dev->frames_since_error++;
if (!dev->stream_synced && !frame_errors && (dev->in_order_cnt > 4)) { if (!dev->stream_synced && !frame_errors && (dev->in_order_cnt > 4)) {
fprintf(stderr, "Syncronized to HDMI input stream\n"); fprintf(stderr, "Synchronized to HDMI input stream\n");
dev->stream_synced = true; dev->stream_synced = true;
} }
} }
@ -839,7 +846,7 @@ int hsdaoh_start_stream(hsdaoh_dev_t *dev, hsdaoh_read_cb_t cb, void *ctx, unsig
// dev->output_float = true; // dev->output_float = true;
/* initialize with a threshold */ /* initialize with a threshold */
dev->highest_numq = 9; dev->highest_numq = DEFAULT_BUFFERS/2;
dev->llbuf_num = (buf_num == 0) ? DEFAULT_BUFFERS : buf_num; dev->llbuf_num = (buf_num == 0) ? DEFAULT_BUFFERS : buf_num;
pthread_mutex_init(&dev->ll_mutex, NULL); pthread_mutex_init(&dev->ll_mutex, NULL);