| /* |
| * Copyright (c) 2022 Samsung Electronics Co., Ltd. |
| * All Rights Reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * - Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * - 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. |
| * |
| * - Neither the name of the copyright owner, nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * 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 OWNER 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. |
| */ |
| #ifndef _OAPV_APP_Y4M_H_ |
| #define _OAPV_APP_Y4M_H_ |
| |
| typedef struct y4m_params { |
| int w; |
| int h; |
| int fps_num; |
| int fps_den; |
| int color_format; |
| int bit_depth; |
| } y4m_params_t; |
| |
| static int y4m_test(FILE *fp) |
| { |
| |
| char buffer[9] = { 0 }; |
| |
| /*Peek to check if y4m header is present*/ |
| if(!fread(buffer, 1, 8, fp)) |
| return -1; |
| fseek(fp, 0, SEEK_SET); |
| buffer[8] = '\0'; |
| if(memcmp(buffer, "YUV4MPEG", 8)) { |
| return 0; |
| } |
| return 1; |
| } |
| |
| static int y4m_parse_tags(y4m_params_t *y4m, char *tags) |
| { |
| |
| char *p; |
| char *q; |
| char t_buff[20]; |
| int found_w = 0, found_h = 0, found_cf = 0; |
| int fps_n, fps_d, pix_ratio_n, pix_ratio_d; |
| |
| for(p = tags;; p = q) { |
| |
| /*Skip any leading spaces.*/ |
| while(*p == ' ') |
| p++; |
| |
| /*If that's all we have, stop.*/ |
| if(p[0] == '\0') |
| break; |
| |
| /*Find the end of this tag.*/ |
| for(q = p + 1; *q != '\0' && *q != ' '; q++) { |
| } |
| |
| /*Process the tag.*/ |
| switch(p[0]) { |
| case 'W': { |
| if(sscanf(p + 1, "%d", &y4m->w) != 1) |
| return OAPV_ERR; |
| found_w = 1; |
| break; |
| } |
| case 'H': { |
| if(sscanf(p + 1, "%d", &y4m->h) != 1) |
| return OAPV_ERR; |
| found_h = 1; |
| break; |
| } |
| case 'F': { |
| if(sscanf(p + 1, "%d:%d", &fps_n, &fps_d) != 2) |
| return OAPV_ERR; |
| y4m->fps_num = fps_n; |
| y4m->fps_den = fps_d; |
| break; |
| } |
| case 'I': { |
| // interlace = p[1]; |
| break; |
| } |
| case 'A': { |
| if(sscanf(p + 1, "%d:%d", &pix_ratio_n, &pix_ratio_d) != 2) |
| return OAPV_ERR; |
| break; |
| } |
| case 'C': { |
| if(q - p > 16) |
| return OAPV_ERR; |
| memcpy(t_buff, p + 1, q - p - 1); |
| t_buff[q - p - 1] = '\0'; |
| found_cf = 1; |
| break; |
| } |
| /*Ignore unknown tags.*/ |
| } |
| } |
| |
| if(!(found_w == 1 && found_h == 1)) { |
| logerr("Mandatory arugments are not found in y4m header"); |
| return OAPV_ERR; |
| } |
| /* Setting default colorspace to yuv420 and input_bd to 8 if header info. is NA */ |
| if(!found_cf) { |
| y4m->color_format = OAPV_CF_YCBCR420; |
| y4m->bit_depth = 8; |
| } |
| |
| if(strcmp(t_buff, "420jpeg") == 0 || strcmp(t_buff, "420") == 0 || |
| strcmp(t_buff, "420mpeg2") == 0 || strcmp(t_buff, "420paidv") == 0) { |
| y4m->color_format = OAPV_CF_YCBCR420; |
| y4m->bit_depth = 8; |
| } |
| else if(strcmp(t_buff, "422") == 0) { |
| y4m->color_format = OAPV_CF_YCBCR422; |
| y4m->bit_depth = 8; |
| } |
| else if(strcmp(t_buff, "444") == 0) { |
| y4m->color_format = OAPV_CF_YCBCR444; |
| y4m->bit_depth = 8; |
| } |
| else if(strcmp(t_buff, "420p10") == 0) { |
| y4m->color_format = OAPV_CF_YCBCR420; |
| y4m->bit_depth = 10; |
| } |
| else if(strcmp(t_buff, "422p10") == 0) { |
| y4m->color_format = OAPV_CF_YCBCR422; |
| y4m->bit_depth = 10; |
| } |
| else if(strcmp(t_buff, "444p10") == 0) { |
| y4m->color_format = OAPV_CF_YCBCR444; |
| y4m->bit_depth = 10; |
| } |
| else if(strcmp(t_buff, "mono") == 0) { |
| y4m->color_format = OAPV_CF_YCBCR400; |
| y4m->bit_depth = 8; |
| } |
| else { |
| y4m->color_format = OAPV_CF_UNKNOWN; |
| y4m->bit_depth = -1; |
| } |
| return OAPV_OK; |
| } |
| |
| int y4m_header_parser(FILE *ip_y4m, y4m_params_t *y4m) |
| { |
| const int head_size = 128; |
| char buffer[128]; |
| int ret; |
| int i; |
| |
| memset(buffer, 0, sizeof(char) * head_size); |
| |
| /*Read until newline, or 128 cols, whichever happens first.*/ |
| for(i = 0; i < (head_size - 1); i++) { |
| |
| if(!fread(buffer + i, 1, 1, ip_y4m)) |
| return -1; |
| if(buffer[i] == '\n') |
| break; |
| } |
| /*We skipped too much header data.*/ |
| if(i == (head_size - 1)) { |
| logerr("Error parsing header; not a YUV2MPEG2 file?\n"); |
| return -1; |
| } |
| buffer[i] = '\0'; |
| if(memcmp(buffer, "YUV4MPEG", 8)) { |
| logerr("Incomplete magic for YUV4MPEG file.\n"); |
| return -1; |
| } |
| if(buffer[8] != '2') { |
| logerr("Incorrect YUV input file version; YUV4MPEG2 required.\n"); |
| } |
| ret = y4m_parse_tags(y4m, buffer + 5); |
| if(ret < 0) { |
| logerr("Error parsing YUV4MPEG2 header.\n"); |
| return ret; |
| } |
| return 0; |
| } |
| |
| static void y4m_update_param(args_parser_t *args, y4m_params_t *y4m) |
| { |
| args->set_int(args, "width", y4m->w); |
| args->set_int(args, "height", y4m->h); |
| char tmp_fps[256]; |
| sprintf(tmp_fps, "%d/%d", y4m->fps_num, y4m->fps_den); |
| args->set_str(args, "fps", tmp_fps); |
| args->set_int(args, "input-depth", y4m->bit_depth); |
| } |
| |
| static int write_y4m_header(char *fname, oapv_imgb_t *imgb) |
| { |
| int color_format = OAPV_CS_GET_FORMAT(imgb->cs); |
| int bit_depth = OAPV_CS_GET_BIT_DEPTH(imgb->cs); |
| int len = 80; |
| int buff_len = 0; |
| char buf[80] = { |
| '\0', |
| }; |
| char c_buf[16] = { |
| '\0', |
| }; |
| FILE *fp; |
| |
| if(color_format == OAPV_CF_YCBCR420) { |
| if(bit_depth == 8) |
| strcpy(c_buf, "420mpeg2"); |
| else if(bit_depth == 10) |
| strcpy(c_buf, "420p10"); |
| } |
| else if(color_format == OAPV_CF_YCBCR422) { |
| if(bit_depth == 8) |
| strcpy(c_buf, "422"); |
| else if(bit_depth == 10) |
| strcpy(c_buf, "422p10"); |
| } |
| else if(color_format == OAPV_CF_YCBCR444) { |
| if(bit_depth == 8) |
| strcpy(c_buf, "444"); |
| else if(bit_depth == 10) |
| strcpy(c_buf, "444p10"); |
| } |
| else if(color_format == OAPV_CF_YCBCR400) { |
| if(bit_depth == 8) |
| strcpy(c_buf, "mono"); |
| } |
| |
| if(strlen(c_buf) == 0) { |
| logerr("Color format is not suuported by y4m"); |
| return -1; |
| } |
| |
| /*setting fps to 30 by default as there is no fps related parameter */ |
| buff_len = snprintf(buf, len, "YUV4MPEG2 W%d H%d F%d:%d Ip C%s\n", |
| imgb->w[0], imgb->h[0], 30, 1, c_buf); |
| |
| fp = fopen(fname, "ab"); |
| if(fp == NULL) { |
| logerr("cannot open file = %s\n", fname); |
| return -1; |
| } |
| if(buff_len != fwrite(buf, 1, buff_len, fp)) { |
| fclose(fp); |
| return -1; |
| } |
| fclose(fp); |
| return 0; |
| } |
| /* Frame level header or separator */ |
| static int write_y4m_frame_header(char *fname) |
| { |
| FILE *fp; |
| fp = fopen(fname, "ab"); |
| if(fp == NULL) { |
| logerr("cannot open file = %s\n", fname); |
| return -1; |
| } |
| if(6 != fwrite("FRAME\n", 1, 6, fp)) { |
| fclose(fp); |
| return -1; |
| } |
| fclose(fp); |
| return 0; |
| } |
| |
| // check whether file name is y4m type or not |
| // return |
| // - positive value : file name has y4m format name |
| // - zero : YUV format name |
| // - nogative value : unknown format name |
| static int check_file_name_type(char * fname) |
| { |
| char fext[16]; |
| if(strlen(fname) < 5) { /* at least x.yuv or x.y4m */ |
| return -1; |
| } |
| strncpy(fext, fname + strlen(fname) - 3, sizeof(fext) - 1); |
| fext[0] = toupper(fext[0]); |
| fext[1] = toupper(fext[1]); |
| fext[2] = toupper(fext[2]); |
| |
| if(strcmp(fext, "YUV") == 0) { |
| return 0; |
| } |
| else if(strcmp(fext, "Y4M") == 0) { |
| return 1; |
| } |
| else { |
| return -1; |
| } |
| return -1; // false |
| } |
| |
| #endif /* _OAPV_APP_Y4M_H_ */ |