From df62a0e23627f65a2b9e1e0e1ecc7b018e75b935 Mon Sep 17 00:00:00 2001 From: Jaro Marval Date: Wed, 30 Sep 2026 10:30:13 -0500 Subject: [PATCH 1/5] Failing i6 JPEG snapshots on timeouts and errors without freeing garbage --- src/hal/star/i6_hal.c | 149 +++++++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 69 deletions(-) diff --git a/src/hal/star/i6_hal.c b/src/hal/star/i6_hal.c index 1c48fd0c..bc53fb8a 100644 --- a/src/hal/star/i6_hal.c +++ b/src/hal/star/i6_hal.c @@ -679,112 +679,123 @@ void i6_video_request_idr(char index) int i6_video_snapshot_grab(char index, char quality, hal_jpegdata *jpeg) { - int ret; + int ret, fd; + int count = 1; + i6_venc_jpg param; + i6_venc_stat stat; + i6_venc_strm strm; + i6_venc_pack packs[8]; + struct timeval timeout = { .tv_sec = 2, .tv_usec = 0 }; + fd_set readFds; + + memset(¶m, 0, sizeof(param)); + memset(&stat, 0, sizeof(stat)); + memset(&strm, 0, sizeof(strm)); if (ret = i6_channel_bind(index, 1)) { HAL_DANGER("i6_venc", "Binding the encoder channel " "%d failed with %#x!\n", index, ret); - goto abort; + return ret; } - i6_venc_jpg param; - memset(¶m, 0, sizeof(param)); if (ret = i6_venc.fnGetJpegParam(index, ¶m)) { HAL_DANGER("i6_venc", "Reading the JPEG settings " "%d failed with %#x!\n", index, ret); - goto abort; + goto unbind; } param.quality = quality; if (ret = i6_venc.fnSetJpegParam(index, ¶m)) { HAL_DANGER("i6_venc", "Writing the JPEG settings " "%d failed with %#x!\n", index, ret); - goto abort; + goto unbind; } - unsigned int count = 1; if (ret = i6_venc.fnStartReceivingEx(index, &count)) { HAL_DANGER("i6_venc", "Requesting one frame " "%d failed with %#x!\n", index, ret); - goto abort; + goto unbind; } - int fd = i6_venc.fnGetDescriptor(index); + fd = i6_venc.fnGetDescriptor(index); + if (fd < 0) { + HAL_DANGER("i6_venc", "Getting the encoder descriptor " + "%d failed with %#x!\n", index, fd); + ret = EXIT_FAILURE; + goto stop; + } - struct timeval timeout = { .tv_sec = 2, .tv_usec = 0 }; - fd_set readFds; FD_ZERO(&readFds); FD_SET(fd, &readFds); ret = select(fd + 1, &readFds, NULL, NULL, &timeout); - if (ret < 0) { - HAL_DANGER("i6_venc", "Select operation failed!\n"); - goto abort; - } else if (ret == 0) { - HAL_DANGER("i6_venc", "Capture stream timed out!\n"); - goto abort; + if (ret <= 0) { + HAL_DANGER("i6_venc", ret ? "Select operation failed!\n" : + "Capture stream timed out!\n"); + ret = EXIT_FAILURE; + goto free_fd; } - if (FD_ISSET(fd, &readFds)) { - i6_venc_stat stat; - if (ret = i6_venc.fnQuery(index, &stat)) { - HAL_DANGER("i6_venc", "Querying the encoder channel " - "%d failed with %#x!\n", index, ret); - goto abort; - } + if (ret = i6_venc.fnQuery(index, &stat)) { + HAL_DANGER("i6_venc", "Querying the encoder channel " + "%d failed with %#x!\n", index, ret); + goto free_fd; + } - if (!stat.curPacks) { - HAL_DANGER("i6_venc", "Current frame is empty, skipping it!\n"); - goto abort; - } + if (!stat.curPacks) { + HAL_DANGER("i6_venc", "Current frame is empty, skipping it!\n"); + ret = EXIT_FAILURE; + goto free_fd; + } - i6_venc_strm strm; - memset(&strm, 0, sizeof(strm)); - i6_venc_pack packs[8]; - if (stat.curPacks > 8) - strm.packet = (i6_venc_pack*)malloc(sizeof(i6_venc_pack) * stat.curPacks); - else - strm.packet = packs; - - if (!strm.packet) { - HAL_DANGER("i6_venc", "Memory allocation on channel %d failed!\n", index); - goto abort; - } - strm.count = stat.curPacks; - - if (ret = i6_venc.fnGetStream(index, &strm, stat.curPacks)) { - HAL_DANGER("i6_venc", "Getting the stream on " - "channel %d failed with %#x!\n", index, ret); - if (stat.curPacks > 8) free(strm.packet); - strm.packet = NULL; - goto abort; - } + if (stat.curPacks > 8) + strm.packet = malloc(sizeof(i6_venc_pack) * stat.curPacks); + else + strm.packet = packs; + if (!strm.packet) { + HAL_DANGER("i6_venc", "Memory allocation on channel %d failed!\n", index); + ret = EXIT_FAILURE; + goto free_fd; + } + strm.count = stat.curPacks; + + // On a failure the SDK leaves the line of the failed check in the sequence + if (ret = i6_venc.fnGetStream(index, &strm, 40)) { + HAL_DANGER("i6_venc", "Getting the stream on channel %d failed " + "with %#x (curPacks %u, leftPics %u, seq %#x)!\n", + index, ret, stat.curPacks, stat.leftPics, strm.sequence); + goto free_packs; + } - { - jpeg->jpegSize = 0; - for (unsigned int i = 0; i < strm.count; i++) { - i6_venc_pack *pack = &strm.packet[i]; - unsigned int packLen = pack->length - pack->offset; - unsigned char *packData = pack->data + pack->offset; - - unsigned int newLen = jpeg->jpegSize + packLen; - if (newLen > jpeg->length) { - jpeg->data = realloc(jpeg->data, newLen); - jpeg->length = newLen; - } - memcpy(jpeg->data + jpeg->jpegSize, packData, packLen); - jpeg->jpegSize += packLen; + jpeg->jpegSize = 0; + for (unsigned int i = 0; i < strm.count; i++) { + i6_venc_pack *pack = &strm.packet[i]; + unsigned int packLen = pack->length - pack->offset; + unsigned int newLen = jpeg->jpegSize + packLen; + + if (newLen > jpeg->length) { + unsigned char *grown = realloc(jpeg->data, newLen); + if (!grown) { + HAL_DANGER("i6_venc", "Growing the JPEG buffer to %u bytes failed!\n", newLen); + ret = EXIT_FAILURE; + break; } + jpeg->data = grown; + jpeg->length = newLen; } - -abort: - i6_venc.fnFreeStream(index, &strm); - if (stat.curPacks > 8) free(strm.packet); + memcpy(jpeg->data + jpeg->jpegSize, pack->data + pack->offset, packLen); + jpeg->jpegSize += packLen; } - i6_venc.fnFreeDescriptor(index); + i6_venc.fnFreeStream(index, &strm); +free_packs: + if (strm.packet != packs) + free(strm.packet); +free_fd: + i6_venc.fnFreeDescriptor(index); +stop: i6_venc.fnStopReceiving(index); - +unbind: i6_channel_unbind(index); return ret; From afde633e4be0b91cfb720eb7d72a70ce153448bd Mon Sep 17 00:00:00 2001 From: Jaro Marval Date: Wed, 30 Sep 2026 10:31:33 -0500 Subject: [PATCH 2/5] Mapping the SSC32x snapshot output ring before the first capture --- src/hal/star/i6_hal.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/hal/star/i6_hal.c b/src/hal/star/i6_hal.c index bc53fb8a..5198e939 100644 --- a/src/hal/star/i6_hal.c +++ b/src/hal/star/i6_hal.c @@ -625,6 +625,17 @@ int i6_video_create(char index, hal_vidconfig *config) (ret = i6_venc.fnStartReceiving(index))) return ret; + // The SSC32x MI_VENC maps the output ring in StartRecvPic only, not in + // StartRecvPicEx, so GetStream on a snapshot channel fails with + // ILLEGAL_PARAM unless it was started once; the mapping lasts until + // DestroyChn. The channel is not bound yet, nothing gets encoded + if (config->codec == HAL_VIDCODEC_JPG && series == 0xEF) { + if (ret = i6_venc.fnStartReceiving(index)) + return ret; + if (ret = i6_venc.fnStopReceiving(index)) + return ret; + } + i6_state[index].payload = config->codec; return EXIT_SUCCESS; From 26c800f09bb2607612777ddd486bd4371e9768bd Mon Sep 17 00:00:00 2001 From: Jaro Marval Date: Wed, 30 Sep 2026 10:32:50 -0500 Subject: [PATCH 3/5] Feeding the SSC32x JPEG encoder 4:2:0 when the height allows it --- src/hal/star/i6_hal.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hal/star/i6_hal.c b/src/hal/star/i6_hal.c index 5198e939..56fdd98f 100644 --- a/src/hal/star/i6_hal.c +++ b/src/hal/star/i6_hal.c @@ -177,6 +177,10 @@ int i6_channel_create(char index, short width, short height, char jpeg) port.flip = 0; port.compress = I6_COMPR_NONE; port.pixFmt = jpeg ? I6_PIXFMT_YUV422_YUYV : I6_PIXFMT_YUV420SP; + // The SSC32x JPE also takes 4:2:0 when the height is 16-aligned, + // a quarter less MMA than YUYV for the snapshot port + if (jpeg && series == 0xEF && !(height & 15)) + port.pixFmt = I6_PIXFMT_YUV420SP; return i6_vpe.fnSetPortConfig(_i6_vpe_chn, index, &port); } From fb7b7a7fe040298d9ed78bff9223000a1ac99e57 Mon Sep 17 00:00:00 2001 From: Jaro Marval Date: Wed, 30 Sep 2026 16:39:22 -0500 Subject: [PATCH 4/5] Disabling the i6 VPE port when binding it to the encoder fails --- src/hal/star/i6_hal.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/hal/star/i6_hal.c b/src/hal/star/i6_hal.c index 56fdd98f..c0683336 100644 --- a/src/hal/star/i6_hal.c +++ b/src/hal/star/i6_hal.c @@ -155,17 +155,21 @@ int i6_channel_bind(char index, char framerate) { unsigned int device; if (ret = i6_venc.fnGetChannelDeviceId(index, &device)) - return ret; + goto disable; i6_sys_bind source = { .module = I6_SYS_MOD_VPE, .device = _i6_vpe_dev, .channel = _i6_vpe_chn, .port = index }; i6_sys_bind dest = { .module = I6_SYS_MOD_VENC, .device = device, .channel = index, .port = _i6_venc_port }; if (ret = i6_sys.fnBindExt(&source, &dest, framerate, framerate, I6_SYS_LINK_FRAMEBASE, 0)) - return ret; + goto disable; } return EXIT_SUCCESS; + +disable: + i6_vpe.fnDisablePort(_i6_vpe_chn, index); + return ret; } int i6_channel_create(char index, short width, short height, char jpeg) From 15925d567a36ec6ce01e7c05b8f78c0e4ebb12a6 Mon Sep 17 00:00:00 2001 From: Jaro Marval Date: Wed, 30 Sep 2026 16:39:22 -0500 Subject: [PATCH 5/5] Resetting the JPEG buffer capacity along with its data after a failed grab --- src/jpeg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jpeg.c b/src/jpeg.c index babf71f9..13858794 100644 --- a/src/jpeg.c +++ b/src/jpeg.c @@ -137,6 +137,7 @@ int jpeg_get(short width, short height, char quality, char grayscale, if (ret && jpeg->data) { free(jpeg->data); jpeg->data = NULL; + jpeg->length = 0; } pthread_mutex_unlock(&jpeg_mutex);