diff options
Diffstat (limited to 'helpers.c')
-rw-r--r-- | helpers.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/helpers.c b/helpers.c new file mode 100644 index 0000000..533769d --- /dev/null +++ b/helpers.c @@ -0,0 +1,407 @@ +/* +Copyright 2019 - Stettbacher Signal Processing AG + +Author: Patrick Roth + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or other +materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <string.h> +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <errno.h> +#include <tiffio.h> +#include <stdlib.h> + +#include "helpers.h" + + + +/** + * Save image as JPEG format + * + * @param filename filename as string + * @param format save image with this format + * @param color_pipe piped out image data + * @param compress 0 if no compression is used + * @return 0 on success, -1 on error + */ +static int save_jpeg(char *filename, enum frame_format_t format, struct color_pipe_t *__restrict__ color_pipe, int compress_img) { + printf("%s: saving JPEG image failed --> not implemented yet!!\n", __func__); + return -1; +} + + +/** + * Save image with TIFF format + * + * @param filename filename as string + * @param format save image with this format + * @param color_pipe piped out image data + * @param compress 0 if no compression is used + * @return 0 on success, -1 on error + */ +static int save_tiff(char *filename, enum frame_format_t format, struct color_pipe_t *__restrict__ color_pipe, int compress_img) { + + TIFF *tif; + int samples_per_pixel; + int i, u, height, width; + uint8_t *in_img8 = color_pipe->img_out; + uint16_t *in_img16 = color_pipe->img_out; + uint16_t *linebuf16; + int bitpersample; + + + if(color_pipe->is_color) { + samples_per_pixel = 3; + } + else { + samples_per_pixel = 1; + } + height = color_pipe->height; + width = color_pipe->width; + + if(color_pipe->bit_channel == 8) { + bitpersample = 8; + } + else if(color_pipe->bit_channel == 12) { + bitpersample = 16; + } + else { + printf("%s: %d bits per channel is not supported yet\n", __func__, color_pipe->bit_channel); + return -1; + } + + tif = TIFFOpen(filename,"w"); + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samples_per_pixel); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitpersample); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, width*samples_per_pixel); + TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)width); + TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)height); + TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, 0); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + if(compress_img) { + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW); + } + else { + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + } + TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); + + if(color_pipe->is_color) { + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + } + else { + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + } + + if(bitpersample == 8) { + // 8 bit per channel RGB image + for(i = 0; i < height; i++) { + TIFFWriteScanline(tif, (void*)&(in_img8[i*width*samples_per_pixel]), i, 0); + } + } + else if(bitpersample == 16) { + // 12 bit per channel RGB image (upscaling from 12 to 16 bit needed) + linebuf16 = malloc(width*samples_per_pixel*2); + if(linebuf16 == NULL) { + printf("%s: allocating memory failed: %s\n", __func__, strerror(errno)); + goto _end_savetiff; + } + + for(i = 0; i < height; i++) { + // upscale 12 to 16 bit + for(u = 0; u < width*samples_per_pixel; u++) { + linebuf16[u] = in_img16[i*width*samples_per_pixel+u] << 4; + } + TIFFWriteScanline(tif, (void*)linebuf16, i, 0); + } + free(linebuf16); + } + +_end_savetiff: + TIFFClose(tif); + return 0; +} + + +/** + * Save image frame with given file format. + * + * @param filename filename as string + * @param format save image with this format + * @param color_pipe piped out image data + * @param compress 0 if no compression is used + * @return 0 on success, -1 on error + */ +int save_imgframe(char *filename, enum frame_format_t format, struct color_pipe_t *color_pipe, int compress) { + + char filename_ext[256]; + + switch(format) { + case IMGFRAME_FORMAT_TIF: + sprintf(filename_ext, "%s.tif", filename); + save_tiff(filename_ext, format, color_pipe, compress); + break; + + case IMGFRAME_FORMAT_JPEG: + sprintf(filename_ext, "%s.jpeg", filename); + save_jpeg(filename_ext, format, color_pipe, compress); + break; + + default: + printf("%s: saving image with format %d not implemeted yet\n", __func__, format); + return -1; + } + return 0; +} + + +/** + * Tokenise (split) string + * + * This function uses strtok_r and therefore it's thread safe. + * + * @param s input string + * @param argv On return: array of string pointers containing the substrings + * @param argv_len string array length + * @param delim string delimiter used to tokenize input string ('\0' terminated) + * @return number of substrings splitted or -1 on error + */ + +int tokenise(char *s, char **argv, int argv_len, char *delim) { + + char *str; + int argc=0, i; + char *save_ptr; + + for(i = 0; i < argv_len; i++) { + argv[i] = NULL; + } + + str = strtok_r(s, delim, &save_ptr); + + while((str != NULL) && (argc < argv_len)) { + argv[argc++] = str; + str = strtok_r(NULL,delim,&save_ptr); + } + + if(argc > argv_len) { + return -1; + } + return argc; +} + + + +/** + * generic function to start thread + * This function is used to start a new thread. Normally all threads are scheduled with real time policy. If the + * priority is defined as @ref PRIO_TIME_SLICED a normal scheduling policy is used (no real time). + * + * @param thr On return: The ID of the new thread. + * @param func The start function. + * @param prio The thread priority. + * @param param The thread parameters. + * @return 0 on success otherwis -1. + */ +int generic_start_thread(pthread_t *thr, void*(*func)(void*), int prio, void *param) { + int r; + pthread_attr_t attr; + sigset_t bmask, oldmask; + struct sched_param sp; + + r = pthread_attr_init(&attr); // create attribute object + if(r) { + printf("%s: pthread_attr_init failed: %s\n", __func__, strerror(r)); + return -1; + } + + if(prio == PRIO_TIME_SLICED) { + r = pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + sp.sched_priority = 0; + } + else { + r = pthread_attr_setschedpolicy(&attr, SCHED_FIFO); // set realtime scheduling policy + sp.sched_priority = sched_get_priority_max(SCHED_FIFO) - prio; + } + if(r) { + printf("%s: pthread_attr_setschedpolicy failed: %s\n", __func__, strerror(r)); + goto _generic_start_thread_abort; + } + r = pthread_attr_setschedparam(&attr, &sp); + if(r) { + printf("%s: pthread_attr_setschedparam failed: %s\n", __func__, strerror(r)); + goto _generic_start_thread_abort; + } + r = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + if(r) { + printf("%s: pthread_attr_setinheritsched failed: %s\n", __func__, strerror(r)); + goto _generic_start_thread_abort; + } + + //temporarely block some signals and inherit the signal mask to the new thread + if(sigemptyset(&bmask)) { + printf("%s: sigemptyset failed\n", __func__); + goto _generic_start_thread_abort; + } + if(sigaddset(&bmask, SIGTERM)) { + printf("%s: sigaddset (SIGTERM) failed\n", __func__); + goto _generic_start_thread_abort; + } + if(sigaddset(&bmask, SIGINT)) { + printf("%s: sigaddset (SIGINT) failed\n", __func__); + goto _generic_start_thread_abort; + } + if(sigaddset(&bmask, SIGALRM)) { + printf("%s: sigaddset (SIGALRM) \n", __func__); + goto _generic_start_thread_abort; + } + if(sigaddset(&bmask, SIGCHLD)) { + printf("%s: sigaddset (SIGCHLD) failed\n", __func__); + goto _generic_start_thread_abort; + } + if(sigaddset(&bmask, SIGUSR1)) { + printf("%s: sigaddset (SIGUSR1) failed\n", __func__); + goto _generic_start_thread_abort; + } + if(sigaddset(&bmask, SIGUSR2)) { + printf("%s: sigaddset (SIGUSR2) failed\n", __func__); + goto _generic_start_thread_abort; + } + if(sigprocmask(SIG_BLOCK, &bmask, &oldmask)) { + printf("%s: sigprocmask failed: %s\n", __func__, strerror(errno)); + goto _generic_start_thread_abort; + } + + //create thread + r = pthread_create(thr, &attr, func, param); + if(r) { + printf("%s: pthread_create failed: %s\n", __func__, strerror(r)); + goto _generic_start_thread_abort; + } + + //restore old signalmask + if(sigprocmask (SIG_SETMASK, &oldmask, NULL)) { + printf("%s: sigprocmask failed: %s\n", __func__, strerror(errno)); + goto _generic_start_thread_abort; + } + + pthread_attr_destroy(&attr); //release attribute object + return 0; + +_generic_start_thread_abort: + pthread_attr_destroy(&attr); // release attribute object + return -1; +} + + +/** + * Execute a shell command. + * This function is thread safe. + * + * @param cmd shell command to be executed + * @param retstr On return: The return string from shell command execution. If NULL is defined nothing is returned. + * @param len maximum length of return string + * @return -1 if an error is detected + */ +int exec_shell_command(char *cmd, char *retstr, int len) { + FILE *substream; + char buf[512]; + int ret, len_tmp; + + if(retstr == NULL) { + ret = system(cmd); + } + else { + substream = popen(cmd, "r"); // start subprocess with streampipe connection + if (substream == NULL) { + printf("%s: popen failed: %s\n", __func__, strerror(errno)); + return -1; + } + + if(setvbuf(substream, NULL, _IOLBF, 0)) { // set line buffering for the stream + printf("%s: setvbuf failed: %s\n", __func__, strerror(errno)); + return -1; + } + + if (len > 0) { + retstr[0] = '\0'; + } + while(fgets(buf, sizeof(buf), substream) != NULL) { + len_tmp = strlen(buf); + if(len > 0) { + strncpy(retstr, buf, len); + retstr[len-1] = '\0'; + retstr += len_tmp; + len -= len_tmp; + } + } + ret = pclose(substream); + } + return ret; +} + + +/** + * Set the given process name to realtime scheduling with the definded priority. + * 0 means highest priority. + * + * NOTE + * Time critical scheduling policy SCHED_RR (real-time round-robin) is used! + * + * @param p_name process name (name given command 'ps') + * @param prio priority + * @return 0 on success, -1 on error + */ +int setRealtimeScheduling(char *p_name, int prio) { + + char shell_cmd[256]; + int pid; + char str[256]; + struct sched_param sp; + + // check string length of given process name + if(strlen(p_name) > (sizeof(shell_cmd)-30)) { + printf("%s: process name is too long\n", __func__); + return -1; + } + + sprintf(shell_cmd, "pidof %s", p_name); + if(exec_shell_command(shell_cmd, str, sizeof(str))) { + printf("%s: Setting realtime scheduling for '%s' failed: '%s'\n", __func__, p_name, str); + return -1; + } + + pid = atoi(str); + sp.sched_priority = sched_get_priority_max(SCHED_RR) - prio; + if(sched_setscheduler(pid, SCHED_RR, &sp)) { + printf("%s: sched_setscheduler failed: %s\n", __func__, strerror(errno)); + return -1; + } + return 0; +} |