diff --git a/veejay-current/veejay-client/src/fastintfns.h b/veejay-current/veejay-client/src/fastintfns.h new file mode 100644 index 00000000..41c611c9 --- /dev/null +++ b/veejay-current/veejay-client/src/fastintfns.h @@ -0,0 +1,18 @@ +/* fast int primitives. min,max,rnddiv2 + * + * WARNING: Assumes 2's complement arithmetic. + */ +static inline int intmax( register int x, register int y ) +{ + return x < y ? y : x; +} + +static inline int intmin( register int x, register int y ) +{ + return x < y ? x : y; +} + +static inline int rnddiv2( int x ) +{ + return (x+(x>0))>>1; +} diff --git a/veejay-current/veejay-client/src/format_codes.h b/veejay-current/veejay-client/src/format_codes.h new file mode 100644 index 00000000..2c958f04 --- /dev/null +++ b/veejay-current/veejay-client/src/format_codes.h @@ -0,0 +1,46 @@ +/* + $Id: format_codes.h,v 1.10 2005/12/09 23:07:56 wackston2 Exp $ + + Copyright (C) 2001 Andrew Stevens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __FORMAT_CODES_H__ +#define __FORMAT_CODES_H__ + +#define MPEG_FORMAT_MPEG1 0 +#define MPEG_FORMAT_VCD 1 +#define MPEG_FORMAT_VCD_NSR 2 +#define MPEG_FORMAT_MPEG2 3 +#define MPEG_FORMAT_SVCD 4 +#define MPEG_FORMAT_SVCD_NSR 5 +#define MPEG_FORMAT_VCD_STILL 6 +#define MPEG_FORMAT_SVCD_STILL 7 +#define MPEG_FORMAT_DVD_NAV 8 +#define MPEG_FORMAT_DVD 9 +#define MPEG_FORMAT_ATSC480i 10 +#define MPEG_FORMAT_ATSC480p 11 +#define MPEG_FORMAT_ATSC720p 12 +#define MPEG_FORMAT_ATSC1080i 13 + +#define MPEG_FORMAT_FIRST 0 +#define MPEG_FORMAT_LAST MPEG_FORMAT_ATSC1080i + +#define MPEG_STILLS_FORMAT(x) ((x)==MPEG_FORMAT_VCD_STILL||(x)==MPEG_FORMAT_SVCD_STILL) +#define MPEG_ATSC_FORMAT(x) ((x)>=MPEG_FORMAT_ATSC480i && (x)<=MPEG_FORMAT_ATSC1080i) +#define MPEG_HDTV_FORMAT(x) MPEG_ATSC_FORMAT(x) +#define MPEG_SDTV_FORMAT(x) (!MPEG_HDTV_FORMAT(x)) +#endif /* __FORMAT_CODES_H__ */ diff --git a/veejay-current/veejay-client/src/mjpeg_logging.c b/veejay-current/veejay-client/src/mjpeg_logging.c new file mode 100644 index 00000000..3d43fd8d --- /dev/null +++ b/veejay-current/veejay-client/src/mjpeg_logging.c @@ -0,0 +1,230 @@ +/* + $Id: mjpeg_logging.c,v 1.16 2007/04/01 18:06:06 sms00 Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include "mjpeg_logging.h" + +#ifdef HAVE___PROGNAME +extern const char *__progname; +#endif + +/* + * Put these here and NOT in the exported header file mjpeg_logging.h + * + * since ALL program use of these should use the API below (mjpeg_warn, + * mjpeg_error,etc) these symbols do not need to be exported and possibly + * conflict with syslog.h +*/ + +#define LOG_DEBUG 1 +#define LOG_INFO 2 +#define LOG_WARN 3 +#define LOG_ERROR 4 + +static log_level_t mjpeg_log_verbosity = 0; +static char *default_handler_id = NULL; + +static int default_mjpeg_log_filter( log_level_t level ) +{ + int verb_from_env; + if( mjpeg_log_verbosity == 0 ) + { + char *mjpeg_verb_env = getenv("MJPEG_VERBOSITY"); + if( mjpeg_verb_env != NULL ) + { + verb_from_env = LOG_WARN-atoi(mjpeg_verb_env); + if( verb_from_env >= LOG_DEBUG && verb_from_env <= LOG_ERROR ) + mjpeg_log_verbosity = (log_level_t)verb_from_env; + } + } + return (level < LOG_WARN && level < mjpeg_log_verbosity); +} + +static mjpeg_log_filter_t _filter = default_mjpeg_log_filter; + +static void +default_mjpeg_log_handler(log_level_t level, const char message[]) +{ + const char *ids; + + if( (*_filter)( level ) ) + return; + if (default_handler_id != NULL) { + ids = default_handler_id; + } else { +#ifdef HAVE___PROGNAME + ids = __progname; +#else + ids = "???"; +#endif + } + switch(level) { + case LOG_ERROR: + fprintf(stderr, "**ERROR: [%s] %s\n", ids, message); + break; + case LOG_DEBUG: + fprintf(stderr, "--DEBUG: [%s] %s\n", ids, message); + break; + case LOG_WARN: + fprintf(stderr, "++ WARN: [%s] %s\n", ids, message); + break; + case LOG_INFO: + fprintf(stderr, " INFO: [%s] %s\n", ids, message); + break; + default: + assert(0); + } +} + +static mjpeg_log_handler_t _handler = default_mjpeg_log_handler; + + +mjpeg_log_handler_t +mjpeg_log_set_handler(mjpeg_log_handler_t new_handler) +{ + mjpeg_log_handler_t old_handler = _handler; + + _handler = new_handler; + return old_handler; +} + +/*************** + * Set default log handlers degree of verboseity. + * 0 = quiet, 1 = info, 2 = debug + *************/ + +int +mjpeg_default_handler_verbosity(int verbosity) +{ + int prev_verb = mjpeg_log_verbosity; + mjpeg_log_verbosity = (log_level_t)(LOG_WARN - verbosity); + return prev_verb; +} + +/* + * Set identifier string used by default handler + */ +int +mjpeg_default_handler_identifier(const char *new_id) +{ + const char *s; + if (new_id == NULL) { + if (default_handler_id != NULL) + free(default_handler_id); + default_handler_id = NULL; + return 0; + } + /* find basename of new_id (remove any directory prefix) */ + if ((s = strrchr(new_id, '/')) == NULL) + s = new_id; + else + s = s + 1; + default_handler_id = strdup(s); + return 0; +} + +static void +mjpeg_logv(log_level_t level, const char format[], va_list args) +{ + char buf[1024] = { 0, }; + + /* TODO: Original had a re-entrancy error trap to assist bug + finding. To make this work with multi-threaded applications a + lock is needed hence delete. + */ + + vsnprintf(buf, sizeof(buf)-1, format, args); + _handler(level, buf); +} + +void +mjpeg_log(log_level_t level, const char format[], ...) +{ + va_list args; + va_start (args, format); + mjpeg_logv(level, format, args); + va_end (args); +} + +void +mjpeg_debug(const char format[], ...) +{ + va_list args; + va_start (args, format); + mjpeg_logv(LOG_DEBUG, format, args); + va_end (args); +} + +void +mjpeg_info(const char format[], ...) +{ + va_list args; + va_start (args, format); + mjpeg_logv(LOG_INFO, format, args); + va_end (args); +} + +void +mjpeg_warn(const char format[], ...) +{ + va_list args; + va_start (args, format); + mjpeg_logv(LOG_WARN, format, args); + va_end (args); +} + +void +mjpeg_error(const char format[], ...) +{ + va_list args; + va_start (args, format); + mjpeg_logv(LOG_ERROR, format, args); + va_end (args); +} + +void +mjpeg_error_exit1(const char format[], ...) +{ + va_list args; + va_start( args, format ); + mjpeg_logv( LOG_ERROR, format, args); + va_end(args); + exit(EXIT_FAILURE); +} + +log_level_t +mjpeg_loglev_t(const char *level) +{ + if (strcasecmp("debug", level) == 0) return(LOG_DEBUG); + else if (strcasecmp("info", level) == 0) return(LOG_INFO); + else if (strcasecmp("warn", level) == 0) return(LOG_WARN); + else if (strcasecmp("error", level) == 0) return(LOG_ERROR); + return(0); +} diff --git a/veejay-current/veejay-client/src/mjpeg_logging.h b/veejay-current/veejay-client/src/mjpeg_logging.h new file mode 100644 index 00000000..9918af48 --- /dev/null +++ b/veejay-current/veejay-client/src/mjpeg_logging.h @@ -0,0 +1,77 @@ +/* + $Id: mjpeg_logging.h,v 1.11 2007/04/01 18:06:06 sms00 Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __MJPEG_LOGGING_H__ +#define __MJPEG_LOGGING_H__ + +#include + +/* to avoid changing all the places log_level_t is used */ +typedef int log_level_t; + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +#define GNUC_PRINTF( format_idx, arg_idx ) \ + __attribute__((format (printf, format_idx, arg_idx))) +#else /* !__GNUC__ */ +#define GNUC_PRINTF( format_idx, arg_idx ) +#endif /* !__GNUC__ */ + +#ifdef __cplusplus +extern "C" { +#endif +void +mjpeg_log(log_level_t level, const char format[], ...) GNUC_PRINTF(2, 3); + +typedef int(*mjpeg_log_filter_t)(log_level_t level); + +typedef void(*mjpeg_log_handler_t)(log_level_t level, const char message[]); + +mjpeg_log_handler_t +mjpeg_log_set_handler(mjpeg_log_handler_t new_handler); + +int +mjpeg_default_handler_identifier(const char *new_id); + +int +mjpeg_default_handler_verbosity(int verbosity); + +void +mjpeg_debug(const char format[], ...) GNUC_PRINTF(1,2); + +void +mjpeg_info(const char format[], ...) GNUC_PRINTF(1,2); + +void +mjpeg_warn(const char format[], ...) GNUC_PRINTF(1,2); + +void +mjpeg_error(const char format[], ...) GNUC_PRINTF(1,2); + +void +mjpeg_error_exit1(const char format[], ...) GNUC_PRINTF(1,2); + +log_level_t +mjpeg_loglev_t(const char *str); + +#ifdef __cplusplus +} +#endif + +#endif /* __MJPEG_LOGGING_H__ */ diff --git a/veejay-current/veejay-client/src/mjpeg_types.h b/veejay-current/veejay-client/src/mjpeg_types.h new file mode 100644 index 00000000..88b461c1 --- /dev/null +++ b/veejay-current/veejay-client/src/mjpeg_types.h @@ -0,0 +1,28 @@ +/* + $Id: mjpeg_types.h,v 1.16 2006/05/18 18:19:05 sms00 Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __MJPEG_TYPES_H__ +#define __MJPEG_TYPES_H__ + +#include /* FreeBSD, others - ssize_t */ +#include +#include + +#endif /* __MJPEG_TYPES_H__ */ diff --git a/veejay-current/veejay-client/src/mpegconsts.c b/veejay-current/veejay-client/src/mpegconsts.c new file mode 100644 index 00000000..43072156 --- /dev/null +++ b/veejay-current/veejay-client/src/mpegconsts.c @@ -0,0 +1,512 @@ + +/* + * mpegconsts.c: Video format constants for MPEG and utilities for display + * and conversion to format used for yuv4mpeg + * + * Copyright (C) 2001 Andrew Stevens + * Copyright (C) 2001 Matthew Marjanovic + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include "mpegconsts.h" +#include "yuv4mpeg.h" +#include "yuv4mpeg_intern.h" +#include "format_codes.h" + +static y4m_ratio_t +mpeg_framerates[] = { + Y4M_FPS_UNKNOWN, + Y4M_FPS_NTSC_FILM, + Y4M_FPS_FILM, + Y4M_FPS_PAL, + Y4M_FPS_NTSC, + Y4M_FPS_30, + Y4M_FPS_PAL_FIELD, + Y4M_FPS_NTSC_FIELD, + Y4M_FPS_60 +}; + + +#define MPEG_NUM_RATES (sizeof(mpeg_framerates)/sizeof(mpeg_framerates[0])) +static const mpeg_framerate_code_t mpeg_num_framerates = MPEG_NUM_RATES; + +static const char * +framerate_definitions[MPEG_NUM_RATES] = +{ + "illegal", + "24000.0/1001.0 (NTSC 3:2 pulldown converted FILM)", + "24.0 (NATIVE FILM)", + "25.0 (PAL/SECAM VIDEO / converted FILM)", + "30000.0/1001.0 (NTSC VIDEO)", + "30.0", + "50.0 (PAL FIELD RATE)", + "60000.0/1001.0 (NTSC FIELD RATE)", + "60.0" +}; + + +static const char *mpeg1_aspect_ratio_definitions[] = +{ + "illegal", + "1:1 (square pixels)", + "1:0.6735", + "1:0.7031 (16:9 Anamorphic PAL/SECAM for 720x578/352x288 images)", + "1:0.7615", + "1:0.8055", + "1:0.8437 (16:9 Anamorphic NTSC for 720x480/352x240 images)", + "1:0.8935", + "1:0.9375 (4:3 PAL/SECAM for 720x578/352x288 images)", + "1:0.9815", + "1:1.0255", + "1:1:0695", + "1:1.1250 (4:3 NTSC for 720x480/352x240 images)", + "1:1.1575", + "1:1.2015" +}; + +static const y4m_ratio_t mpeg1_aspect_ratios[] = +{ + Y4M_SAR_UNKNOWN, + Y4M_SAR_MPEG1_1, + Y4M_SAR_MPEG1_2, + Y4M_SAR_MPEG1_3, /* Anamorphic 16:9 PAL */ + Y4M_SAR_MPEG1_4, + Y4M_SAR_MPEG1_5, + Y4M_SAR_MPEG1_6, /* Anamorphic 16:9 NTSC */ + Y4M_SAR_MPEG1_7, + Y4M_SAR_MPEG1_8, /* PAL/SECAM 4:3 */ + Y4M_SAR_MPEG1_9, + Y4M_SAR_MPEG1_10, + Y4M_SAR_MPEG1_11, + Y4M_SAR_MPEG1_12, /* NTSC 4:3 */ + Y4M_SAR_MPEG1_13, + Y4M_SAR_MPEG1_14, +}; + +static const char *mpeg2_aspect_ratio_definitions[] = +{ + "illegal", + "1:1 pixels", + "4:3 display", + "16:9 display", + "2.21:1 display" +}; + + +static const y4m_ratio_t mpeg2_aspect_ratios[] = +{ + Y4M_DAR_UNKNOWN, + Y4M_DAR_MPEG2_1, + Y4M_DAR_MPEG2_2, + Y4M_DAR_MPEG2_3, + Y4M_DAR_MPEG2_4 +}; + +static const char **aspect_ratio_definitions[2] = +{ + mpeg1_aspect_ratio_definitions, + mpeg2_aspect_ratio_definitions +}; + +static const y4m_ratio_t *mpeg_aspect_ratios[2] = +{ + mpeg1_aspect_ratios, + mpeg2_aspect_ratios +}; + +static const mpeg_aspect_code_t mpeg_num_aspect_ratios[2] = +{ + sizeof(mpeg1_aspect_ratios)/sizeof(mpeg1_aspect_ratios[0]), + sizeof(mpeg2_aspect_ratios)/sizeof(mpeg2_aspect_ratios[0]) +}; + +static const char *mjpegtools_format_code_definitions[MPEG_FORMAT_LAST+1] = +{ + "Generic MPEG1", + "Standard VCD", + "Stretched VCD", + "Generic MPEG2", + "Standard SVCD", + "Stretched SVCD", + "VCD Still", + "SVCD Still", + "DVD with dummy navigation packets", + "Standard DVD", + "ATSC 480i", + "ATSC 480p", + "ATSC 720p", + "ATSC 1080i" +}; + +/* + * Is code a valid MPEG framerate code? + */ + +int +mpeg_valid_framerate_code( mpeg_framerate_code_t code ) +{ + return ((code > 0) && (code < mpeg_num_framerates)) ? 1 : 0; +} + + +/* + * Convert MPEG frame-rate code to corresponding frame-rate + */ + +y4m_ratio_t +mpeg_framerate( mpeg_framerate_code_t code ) +{ + if ((code > 0) && (code < mpeg_num_framerates)) + return mpeg_framerates[code]; + else + return y4m_fps_UNKNOWN; +} + +/* + * Look-up MPEG frame rate code for a (exact) frame rate. + */ + + +mpeg_framerate_code_t +mpeg_framerate_code( y4m_ratio_t framerate ) +{ + mpeg_framerate_code_t i; + + y4m_ratio_reduce(&framerate); + /* start at '1', because 0 is unknown/illegal */ + for (i = 1; i < mpeg_num_framerates; ++i) { + if (Y4M_RATIO_EQL(framerate, mpeg_framerates[i])) + return i; + } + return 0; +} + + +/* small enough to distinguish 1/1000 from 1/1001 */ +#define MPEG_FPS_TOLERANCE 0.0001 + +y4m_ratio_t +mpeg_conform_framerate( double fps ) +{ + mpeg_framerate_code_t i; + y4m_ratio_t result; + + /* try to match it to a standard frame rate */ + /* (start at '1', because 0 is unknown/illegal) */ + for (i = 1; i < mpeg_num_framerates; i++) + { + double deviation = 1.0 - (Y4M_RATIO_DBL(mpeg_framerates[i]) / fps); + if ( (deviation > -MPEG_FPS_TOLERANCE) && + (deviation < +MPEG_FPS_TOLERANCE) ) + return mpeg_framerates[i]; + } + /* no luck? just turn it into a ratio (8 decimal place accuracy) */ + result.n = (int)((fps * 100000000.0) + 0.5); + result.d = 100000000; + y4m_ratio_reduce(&result); + return result; +} + + + +/* + * Is code a valid MPEG aspect-ratio code? + */ + +int +mpeg_valid_aspect_code( int version, mpeg_framerate_code_t c ) +{ + if ((version == 1) || (version == 2)) + return ((c > 0) && (c < mpeg_num_aspect_ratios[version-1])) ? 1 : 0; + else + return 0; +} + + +/* + * Convert MPEG aspect-ratio code to corresponding aspect-ratio + */ + +y4m_ratio_t +mpeg_aspect_ratio( int mpeg_version, mpeg_aspect_code_t code ) +{ + y4m_ratio_t ratio; + if ((mpeg_version >= 1) && (mpeg_version <= 2) && + (code > 0) && (code < mpeg_num_aspect_ratios[mpeg_version-1])) + { + ratio = mpeg_aspect_ratios[mpeg_version-1][code]; + y4m_ratio_reduce(&ratio); + return ratio; + } + else + return y4m_sar_UNKNOWN; +} + + + +/* + * Look-up corresponding MPEG aspect ratio code given an exact aspect ratio. + * + * WARNING: The semantics of aspect ratio coding *changed* between + * MPEG1 and MPEG2. In MPEG1 it is the *pixel* aspect ratio. In + * MPEG2 it is the (far more sensible) aspect ratio of the eventual + * display. + * + */ + +mpeg_aspect_code_t +mpeg_frame_aspect_code( int mpeg_version, y4m_ratio_t aspect_ratio ) +{ + mpeg_aspect_code_t i; + y4m_ratio_t red_ratio = aspect_ratio; + y4m_ratio_reduce( &red_ratio ); + if( mpeg_version < 1 || mpeg_version > 2 ) + return 0; + /* (start at '1', because 0 is unknown/illegal) */ + for( i = 1; i < mpeg_num_aspect_ratios[mpeg_version-1]; ++i ) + { + y4m_ratio_t red_entry = mpeg_aspect_ratios[mpeg_version-1][i]; + y4m_ratio_reduce( &red_entry ); + if( Y4M_RATIO_EQL( red_entry, red_ratio) ) + return i; + } + + return 0; + +} + + + +/* + * Guess the correct MPEG aspect ratio code, + * given the true sample aspect ratio and frame size of a video stream + * (and the MPEG version, 1 or 2). + * + * Returns 0 if it has no good guess. + * + */ + + +/* this is big enough to accommodate the difference between 720 and 704 */ +#define GUESS_ASPECT_TOLERANCE 0.03 + +mpeg_aspect_code_t +mpeg_guess_mpeg_aspect_code(int mpeg_version, y4m_ratio_t sampleaspect, + int frame_width, int frame_height) +{ + if (Y4M_RATIO_EQL(sampleaspect, y4m_sar_UNKNOWN)) + { + return 0; + } + switch (mpeg_version) { + case 1: + if (Y4M_RATIO_EQL(sampleaspect, y4m_sar_SQUARE)) + { + return 1; + } + else if (Y4M_RATIO_EQL(sampleaspect, y4m_sar_NTSC_CCIR601)) + { + return 12; + } + else if (Y4M_RATIO_EQL(sampleaspect, y4m_sar_NTSC_16_9)) + { + return 6; + } + else if (Y4M_RATIO_EQL(sampleaspect, y4m_sar_PAL_CCIR601)) + { + return 8; + } + else if (Y4M_RATIO_EQL(sampleaspect, y4m_sar_PAL_16_9)) + { + return 3; + } + return 0; + break; + case 2: + if (Y4M_RATIO_EQL(sampleaspect, y4m_sar_SQUARE)) + { + return 1; /* '1' means square *pixels* in MPEG-2; go figure. */ + } + else + { + int i; + double true_far; /* true frame aspect ratio */ + true_far = + (double)(sampleaspect.n * frame_width) / + (double)(sampleaspect.d * frame_height); + /* start at '2'... */ + for (i = 2; i < (int)(mpeg_num_aspect_ratios[mpeg_version-1]); i++) + { + double ratio = + true_far / Y4M_RATIO_DBL(mpeg_aspect_ratios[mpeg_version-1][i]); + if ( (ratio > (1.0 - GUESS_ASPECT_TOLERANCE)) && + (ratio < (1.0 + GUESS_ASPECT_TOLERANCE)) ) + return i; + } + return 0; + } + break; + default: + return 0; + break; + } +} + + + + +/* + * Guess the true sample aspect ratio of a video stream, + * given the MPEG aspect ratio code and the actual frame size + * (and the MPEG version, 1 or 2). + * + * Returns y4m_sar_UNKNOWN if it has no good guess. + * + */ +y4m_ratio_t +mpeg_guess_sample_aspect_ratio(int mpeg_version, + mpeg_aspect_code_t code, + int frame_width, int frame_height) +{ + switch (mpeg_version) + { + case 1: + /* MPEG-1 codes turn into SAR's, just not quite the right ones. + For the common/known values, we provide the ratio used in practice, + otherwise say we don't know.*/ + switch (code) + { + case 1: return y4m_sar_SQUARE; break; + case 3: return y4m_sar_PAL_16_9; break; + case 6: return y4m_sar_NTSC_16_9; break; + case 8: return y4m_sar_PAL_CCIR601; break; + case 12: return y4m_sar_NTSC_CCIR601; break; + default: + return y4m_sar_UNKNOWN; break; + } + break; + case 2: + /* MPEG-2 codes turn into Display Aspect Ratios, though not exactly the + DAR's used in practice. For common/standard frame sizes, we provide + the original SAR; otherwise, we say we don't know. */ + if (code == 1) + { + return y4m_sar_SQUARE; /* '1' means square *pixels* in MPEG-2 */ + } + else if ((code >= 2) && (code <= 4)) + { + return y4m_guess_sar(frame_width, frame_height, + mpeg2_aspect_ratios[code]); + } + else + { + return y4m_sar_UNKNOWN; + } + break; + default: + return y4m_sar_UNKNOWN; + break; + } +} + + + + + +/* + * Look-up MPEG explanatory definition string for frame rate code + * + */ + + +const char * +mpeg_framerate_code_definition( mpeg_framerate_code_t code ) +{ + if( code == 0 || code >= mpeg_num_framerates ) + return "UNDEFINED: illegal/reserved frame-rate ratio code"; + + return framerate_definitions[code]; +} + +/* + * Look-up MPEG explanatory definition string aspect ratio code for an + * aspect ratio code + * + */ + +const char * +mpeg_aspect_code_definition( int mpeg_version, mpeg_aspect_code_t code ) +{ + if( mpeg_version < 1 || mpeg_version > 2 ) + return "UNDEFINED: illegal MPEG version"; + + if( code < 1 || code >= mpeg_num_aspect_ratios[mpeg_version-1] ) + return "UNDEFINED: illegal aspect ratio code"; + + return aspect_ratio_definitions[mpeg_version-1][code]; +} + + +/* + * Look-up explanatory definition of interlace field order code + * + */ + +const char * +mpeg_interlace_code_definition( int yuv4m_interlace_code ) +{ + const char *def; + switch( yuv4m_interlace_code ) + { + case Y4M_UNKNOWN : + def = "unknown"; + break; + case Y4M_ILACE_NONE : + def = "none/progressive"; + break; + case Y4M_ILACE_TOP_FIRST : + def = "top-field-first"; + break; + case Y4M_ILACE_BOTTOM_FIRST : + def = "bottom-field-first"; + break; + default : + def = "UNDEFINED: illegal video interlacing type-code!"; + break; + } + return def; +} + +/* + * Look-up explanatory definition of mjepgtools preset format code + * + */ +const char *mpeg_format_code_defintion( int format_code ) +{ + if(format_code >= MPEG_FORMAT_FIRST && format_code <= MPEG_FORMAT_LAST ) + return mjpegtools_format_code_definitions[format_code]; + else + return "UNDEFINED: illegal format code!"; +}; + +/* + * Local variables: + * c-file-style: "stroustrup" + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/veejay-current/veejay-client/src/mpegconsts.h b/veejay-current/veejay-client/src/mpegconsts.h new file mode 100644 index 00000000..3e9543d1 --- /dev/null +++ b/veejay-current/veejay-client/src/mpegconsts.h @@ -0,0 +1,170 @@ + +/* + * mpegconsts.c: Video format constants for MPEG and utilities for display + * and conversion to format used for yuv4mpeg + * + * Copyright (C) 2001 Andrew Stevens + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MPEGCONSTS_H__ +#define __MPEGCONSTS_H__ + +#include + +typedef unsigned int mpeg_framerate_code_t; +typedef unsigned int mpeg_aspect_code_t; + +#ifdef __cplusplus +#define START_CPP_WRAP extern "C" { +#define END_CPP_WRAP }; +#else +#define START_CPP_WRAP +#define END_CPP_WRAP +#endif + +START_CPP_WRAP + + +/* + * Convert MPEG frame-rate code to corresponding frame-rate + * y4m_fps_UNKNOWN = { 0, 0 } = Undefined/resrerved code. + */ + +y4m_ratio_t +mpeg_framerate( mpeg_framerate_code_t code ); + + +/* + * Is code a valid MPEG frame-rate code? + * Return 1 if true; 0 otherwise. + */ + +int +mpeg_valid_framerate_code( mpeg_framerate_code_t code ); + + +/* + * Look-up MPEG frame rate code for a (exact) frame rate. + * 0 = No MPEG code defined for frame-rate + */ + +mpeg_framerate_code_t +mpeg_framerate_code( y4m_ratio_t framerate ); + + +/* + * Convert floating-point framerate to an exact ratio. + * Uses a standard MPEG rate, if it finds one within MPEG_FPS_TOLERANCE + * (see mpegconsts.c), otherwise uses "fps:1000000" as the ratio. + */ + +y4m_ratio_t +mpeg_conform_framerate( double fps ); + +/* + * Convert MPEG aspect ratio code to corresponding aspect ratio + * + * WARNING: The semantics of aspect ratio coding *changed* between + * MPEG1 and MPEG2. In MPEG1 it is the *pixel* aspect ratio. In + * MPEG2 it is the (far more sensible) aspect ratio of the eventual + * display. + * + */ + +y4m_ratio_t +mpeg_aspect_ratio( int mpeg_version, mpeg_aspect_code_t code ); + + +/* + * Is code a valid MPEG(1,2) aspect-ratio code? + * Return 1 if true; 0 otherwise. + */ + +int +mpeg_valid_aspect_code( int mpeg_version, mpeg_aspect_code_t code ); + + +/* + * Look-up MPEG aspect ratio code for an aspect ratio - tolerance + * is Y4M_ASPECT_MULT used by YUV4MPEG (see yuv4mpeg_intern.h) + * + * WARNING: The semantics of aspect ratio coding *changed* between + * MPEG1 and MPEG2. In MPEG1 it is the *pixel* aspect ratio. In + * MPEG2 it is the (far more sensible) aspect ratio of the eventual + * display. + * + */ + +mpeg_aspect_code_t +mpeg_frame_aspect_code( int mpeg_version, y4m_ratio_t aspect_ratio ); + +/* + * Look-up MPEG explanatory definition string aspect ratio code for an + * aspect ratio code + * + */ + +const char * +mpeg_aspect_code_definition( int mpeg_version, mpeg_aspect_code_t code ); + +/* + * Look-up MPEG explanatory definition string aspect ratio code for an + * frame rate code + * + */ + +const char * +mpeg_framerate_code_definition( mpeg_framerate_code_t code ); + +const char * +mpeg_interlace_code_definition( int yuv4m_interlace_code ); + + +/* + * Guess the correct MPEG aspect ratio code, + * given the true sample aspect ratio and frame size of a video stream + * (and the MPEG version, 1 or 2). + * + * Returns 0 if it has no good answer. + * + */ +mpeg_aspect_code_t +mpeg_guess_mpeg_aspect_code(int mpeg_version, y4m_ratio_t sampleaspect, + int frame_width, int frame_height); + +/* + * Guess the true sample aspect ratio of a video stream, + * given the MPEG aspect ratio code and the actual frame size + * (and the MPEG version, 1 or 2). + * + * Returns y4m_sar_UNKNOWN if it has no good answer. + * + */ +y4m_ratio_t +mpeg_guess_sample_aspect_ratio(int mpeg_version, + mpeg_aspect_code_t code, + int frame_width, int frame_height); + +/* + * Look-up MJPEGTOOLS MPEG preset format code explanatory definition string a + * + */ +const char *mpeg_format_code_defintion( int format_code ); + +END_CPP_WRAP + +#endif /* __MPEGCONSTS_H__ */ diff --git a/veejay-current/veejay-client/src/mpegtimecode.c b/veejay-current/veejay-client/src/mpegtimecode.c new file mode 100644 index 00000000..09986739 --- /dev/null +++ b/veejay-current/veejay-client/src/mpegtimecode.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2001 Kawamata/Hitoshi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include "mpegtimecode.h" + +/************************************************************** + * // NTSC DROP FRAME TIMECODE / 29.97fps (SMTPE) + * // hh:mm:ss:ff + * // hh: 0.. + * // mm: 0..59 + * // ss: 0..59 + * // ff: 0..29 # ss != 0 || mm % 10 == 0 + * // 2..29 # ss == 0 && mm % 10 != 0 + * // + * // 00:00:00:00 00:00:00:01 00:00:00:02 ... 00:00:00:29 + * // 00:00:01:00 00:00:01:01 00:00:01:02 ... 00:00:01:29 + * // : + * // 00:00:59:00 00:00:59:01 00:00:59:02 ... 00:00:59:29 + * // 00:01:00:02 ... 00:01:00:29 + * // 00:01:01:00 00:01:01:01 00:01:01:02 ... 00:01:00:29 + * // : + * // 00:01:59:00 00:01:59:01 00:01:59:02 ... 00:01:59:29 + * // 00:02:00:02 ... 00:02:00:29 + * // 00:02:01:00 00:02:01:01 00:02:01:02 ... 00:02:00:29 + * // : + * // : + * // 00:09:59:00 00:09:59:01 00:09:59:02 ... 00:09:59:29 + * // 00:10:00:00 00:10:00:01 00:10:00:02 ... 00:10:00:29 + * // 00:10:01:00 00:10:01:01 00:10:01:02 ... 00:10:01:29 + * // : + * // 00:10:59:00 00:10:59:01 00:10:59:02 ... 00:10:59:29 + * // 00:11:00:02 ... 00:11:00:29 + * // 00:11:01:00 00:11:01:01 00:11:01:02 ... 00:11:00:29 + * // : + * // : + * // DROP FRAME / 59.94fps (no any standard) + * // DROP FRAME / 23.976fps (no any standard) + ***************************************************************/ + +int dropframetimecode = -1; + +/* mpeg_timecode() return -tc->f on first frame in the minute, tc->f on other. */ +int +mpeg_timecode(MPEG_timecode_t *tc, int f, int fpscode, double fps) +{ + static const int ifpss[] = { 0, 24, 24, 25, 30, 30, 50, 60, 60, }; + int h, m, s; + + if (dropframetimecode < 0) { + char *env = getenv("MJPEG_DROP_FRAME_TIME_CODE"); + dropframetimecode = (env && *env != '0' && *env != 'n' && *env != 'N'); + } + if (dropframetimecode && + 0 < fpscode && fpscode + 1 < sizeof ifpss / sizeof ifpss[0] && + ifpss[fpscode] == ifpss[fpscode + 1]) { + int topinmin = 0, k = (30*4) / ifpss[fpscode]; + f *= k; /* frame# when 119.88fps */ + h = (f / ((10*60*30-18)*4)); /* # of 10min. */ + f %= ((10*60*30-18)*4); /* frame# in 10min. */ + f -= (2*4); /* frame# in 10min. - (2*4) */ + m = (f / ((60*30-2)*4)); /* min. in 10min. */ + topinmin = ((f - k) / ((60*30-2)*4) < m); + m += (h % 6 * 10); /* min. */ + h /= 6; /* hour */ + f %= ((60*30-2)*4); /* frame# in min. - (2*4)*/ + f += (2*4); /* frame# in min. */ + s = f / (30*4); /* sec. */ + f %= (30*4); /* frame# in sec. */ + f /= k; /* frame# in sec. on original fps */ + tc->f = f; + if (topinmin) + f = -f; + } else { + int ifps = ((0 < fpscode && fpscode < sizeof ifpss / sizeof ifpss[0])? + ifpss[fpscode]: (int)(fps + .5)); + s = f / ifps; + f %= ifps; + m = s / 60; + s %= 60; + h = m / 60; + m %= 60; + tc->f = f; + } + tc->s = s; + tc->m = m; + tc->h = h; + return f; +} diff --git a/veejay-current/veejay-client/src/mpegtimecode.h b/veejay-current/veejay-client/src/mpegtimecode.h new file mode 100644 index 00000000..e702a9b7 --- /dev/null +++ b/veejay-current/veejay-client/src/mpegtimecode.h @@ -0,0 +1,39 @@ +/* -*- mode:C -*- */ +/* + * Copyright (C) 2001 Kawamata/Hitoshi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MPEGTIMECODE_H__ +#define __MPEGTIMECODE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char h, m, s, f; +} MPEG_timecode_t; + +extern int dropframetimecode; +extern int mpeg_timecode(MPEG_timecode_t *tc, int f, int fpscode, double fps); +/* mpeg_timecode() return -tc->f on first frame in the minute, tc->f on other. */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/veejay-current/veejay-client/src/utils.c b/veejay-current/veejay-client/src/utils.c index f09e7c02..ed2fcf7a 100644 --- a/veejay-current/veejay-client/src/utils.c +++ b/veejay-current/veejay-client/src/utils.c @@ -18,10 +18,10 @@ */ #include #include -#include -#include -#include -#include +#include "yuv4mpeg_intern.h" +#include "yuv4mpeg.h" +#include "mpegconsts.h" +#include "mpegtimecode.h" #include #include diff --git a/veejay-current/veejay-client/src/videodev_mjpeg.h b/veejay-current/veejay-client/src/videodev_mjpeg.h new file mode 100644 index 00000000..68fd79c5 --- /dev/null +++ b/veejay-current/veejay-client/src/videodev_mjpeg.h @@ -0,0 +1,118 @@ +/* These are the MJPEG API extensions for the Video4Linux API, + first introduced by the Iomega Buz driver by Rainer Johanni + +*/ + +/* This is identical with the mgavideo internal params struct, + please tell me if you change this struct here ! top-field-first */ + + int APPn; /* Number of APP segment to be written, must be 0..15 */ + int APP_len; /* Length of data in JPEG APPn segment */ + char APP_data[60]; /* Data in the JPEG APPn segment. */ + + int COM_len; /* Length of data in JPEG COM segment */ + char COM_data[60]; /* Data in JPEG COM segment */ + + unsigned long jpeg_markers; /* Which markers should go into the JPEG output. + Unless you exactly know what you do, leave them untouched. + Inluding less markers will make the resulting code + smaller, but there will be fewer aplications + which can read it. + The presence of the APP and COM marker is + influenced by APP0_len and COM_len ONLY! */ +#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */ +#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */ +#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */ +#define JPEG_MARKER_COM (1<<6) /* Comment segment */ +#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */ + + int VFIFO_FB; /* Flag for enabling Video Fifo Feedback. + If this flag is turned on and JPEG decompressing + is going to the screen, the decompress process + is stopped every time the Video Fifo is full. + This enables a smooth decompress to the screen + but the video output signal will get scrambled */ + + /* Misc */ + + char reserved[312]; /* Makes 512 bytes for this structure */ +}; + +struct mjpeg_requestbuffers +{ + unsigned long count; /* Number of buffers for MJPEG grabbing */ + unsigned long size; /* Size PER BUFFER in bytes */ +}; + +struct mjpeg_sync +{ + unsigned long frame; /* Frame (0 - n) for double buffer */ + unsigned long length; /* number of code bytes in buffer (capture only) */ + unsigned long seq; /* frame sequence number */ + struct timeval timestamp; /* timestamp */ +}; + +struct mjpeg_status +{ + int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */ + int signal; /* Returned: 1 if valid video signal detected */ + int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int color; /* Returned: 1 if color signal detected */ +}; + +/* +Private IOCTL to set up for displaying MJPEG +*/ +#define MJPIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct mjpeg_params) +#define MJPIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct mjpeg_params) +#define MJPIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct mjpeg_requestbuffers) +#define MJPIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int) +#define MJPIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int) +#define MJPIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct mjpeg_sync) +#define MJPIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct mjpeg_status) diff --git a/veejay-current/veejay-client/src/vj-api.c b/veejay-current/veejay-client/src/vj-api.c index fadb8807..2c78aad3 100644 --- a/veejay-current/veejay-client/src/vj-api.c +++ b/veejay-current/veejay-client/src/vj-api.c @@ -43,8 +43,8 @@ #include #include #include -#include -#include +#include "mpegconsts.h" +#include "mpegtimecode.h" #include #include #include diff --git a/veejay-current/veejay-client/src/yuv4mpeg.c b/veejay-current/veejay-client/src/yuv4mpeg.c new file mode 100644 index 00000000..a7775801 --- /dev/null +++ b/veejay-current/veejay-client/src/yuv4mpeg.c @@ -0,0 +1,1414 @@ +/* + * yuv4mpeg.c: Functions for reading and writing "new" YUV4MPEG streams + * + * Copyright (C) 2001 Matthew J. Marjanovic + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include + +#include +#include +#include +#include +#define INTERNAL_Y4M_LIBCODE_STUFF_QPX +#include "yuv4mpeg.h" +#include "yuv4mpeg_intern.h" +#include "mjpeg_logging.h" + + +static int _y4mparam_allow_unknown_tags = 1; /* default is forgiveness */ +static int _y4mparam_feature_level = 0; /* default is ol YUV4MPEG2 */ + +static void *(*_y4m_alloc)(size_t bytes) = malloc; +static void (*_y4m_free)(void *ptr) = free; + + +int y4m_allow_unknown_tags(int yn) +{ + int old = _y4mparam_allow_unknown_tags; + if (yn >= 0) + _y4mparam_allow_unknown_tags = (yn) ? 1 : 0; + return old; +} + +int y4m_accept_extensions(int level) +{ + int old = _y4mparam_feature_level; + if (level >= 0) + _y4mparam_feature_level = level; + return old; +} + + +/************************************************************************* + * + * Convenience functions for fd read/write + * + * - guaranteed to transfer entire payload (or fail) + * - returns: + * 0 on complete success + * +(# of remaining bytes) on eof (for y4m_read) + * -(# of rem. bytes) on error (and ERRNO should be set) + * + *************************************************************************/ + +ssize_t y4m_read(int fd, void *buf, size_t len) +{ + ssize_t n; + uint8_t *ptr = (uint8_t *)buf; + + while (len > 0) { + n = read(fd, ptr, len); + if (n <= 0) { + /* return amount left to read */ + if (n == 0) + return len; /* n == 0 --> eof */ + else + return -len; /* n < 0 --> error */ + } + ptr += n; + len -= n; + } + return 0; +} + +ssize_t y4m_write(int fd, const void *buf, size_t len) +{ + ssize_t n; + const uint8_t *ptr = (const uint8_t *)buf; + + while (len > 0) { + n = write(fd, ptr, len); + if (n <= 0) return -len; /* return amount left to write */ + ptr += n; + len -= n; + } + return 0; +} + +/* read len bytes from fd into buf */ +ssize_t y4m_read_cb(y4m_cb_reader_t * fd, void *buf, size_t len) + { + return fd->read(fd->data, buf, len); + } + +/* write len bytes from fd into buf */ +ssize_t y4m_write_cb(y4m_cb_writer_t * fd, const void *buf, size_t len) + { + return fd->write(fd->data, buf, len); + } + +/* Functions to use the callback interface from plain filedescriptors */ + +/* read len bytes from fd into buf */ +ssize_t y4m_read_fd(void * data, void *buf, size_t len) + { + int * f = (int*)data; + return y4m_read(*f, buf, len); + } + +/* write len bytes from fd into buf */ +ssize_t y4m_write_fd(void * data, const void *buf, size_t len) + { + int * f = (int*)data; + return y4m_write(*f, buf, len); + } + +static void set_cb_reader_from_fd(y4m_cb_reader_t * ret, int * fd) + { + ret->read = y4m_read_fd; + ret->data = fd; + } + +static void set_cb_writer_from_fd(y4m_cb_writer_t * ret, int * fd) + { + ret->write = y4m_write_fd; + ret->data = fd; + } + + +/************************************************************************* + * + * "Extra tags" handling + * + *************************************************************************/ + +static char *y4m_new_xtag(void) +{ + return _y4m_alloc(Y4M_MAX_XTAG_SIZE * sizeof(char)); +} + +void y4m_init_xtag_list(y4m_xtag_list_t *xtags) +{ + int i; + xtags->count = 0; + for (i = 0; i < Y4M_MAX_XTAGS; i++) { + xtags->tags[i] = NULL; + } +} + +void y4m_fini_xtag_list(y4m_xtag_list_t *xtags) +{ + int i; + for (i = 0; i < Y4M_MAX_XTAGS; i++) { + if (xtags->tags[i] != NULL) { + _y4m_free(xtags->tags[i]); + xtags->tags[i] = NULL; + } + } + xtags->count = 0; +} + +void y4m_copy_xtag_list(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src) +{ + int i; + for (i = 0; i < src->count; i++) { + if (dest->tags[i] == NULL) + dest->tags[i] = y4m_new_xtag(); + strncpy(dest->tags[i], src->tags[i], Y4M_MAX_XTAG_SIZE); + } + dest->count = src->count; +} + +static int y4m_snprint_xtags(char *s, int maxn, const y4m_xtag_list_t *xtags) +{ + int i, room; + + for (i = 0, room = maxn - 1; i < xtags->count; i++) { + int n = snprintf(s, room + 1, " %s", xtags->tags[i]); + if ((n < 0) || (n > room)) return Y4M_ERR_HEADER; + s += n; + room -= n; + } + s[0] = '\n'; /* finish off header with newline */ + s[1] = '\0'; /* ...and end-of-string */ + return Y4M_OK; +} + +int y4m_xtag_count(const y4m_xtag_list_t *xtags) +{ + return xtags->count; +} + +const char *y4m_xtag_get(const y4m_xtag_list_t *xtags, int n) +{ + if (n >= xtags->count) + return NULL; + else + return xtags->tags[n]; +} + +int y4m_xtag_add(y4m_xtag_list_t *xtags, const char *tag) +{ + if (xtags->count >= Y4M_MAX_XTAGS) return Y4M_ERR_XXTAGS; + if (xtags->tags[xtags->count] == NULL) + xtags->tags[xtags->count] = y4m_new_xtag(); + strncpy(xtags->tags[xtags->count], tag, Y4M_MAX_XTAG_SIZE); + (xtags->count)++; + return Y4M_OK; +} + +int y4m_xtag_remove(y4m_xtag_list_t *xtags, int n) +{ + int i; + char *q; + + if ((n < 0) || (n >= xtags->count)) return Y4M_ERR_RANGE; + q = xtags->tags[n]; + for (i = n; i < (xtags->count - 1); i++) + xtags->tags[i] = xtags->tags[i+1]; + xtags->tags[i] = q; + (xtags->count)--; + return Y4M_OK; +} + +int y4m_xtag_clearlist(y4m_xtag_list_t *xtags) +{ + xtags->count = 0; + return Y4M_OK; +} + +int y4m_xtag_addlist(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src) +{ + int i, j; + + if ((dest->count + src->count) > Y4M_MAX_XTAGS) return Y4M_ERR_XXTAGS; + for (i = dest->count, j = 0; + j < src->count; + i++, j++) { + if (dest->tags[i] == NULL) + dest->tags[i] = y4m_new_xtag(); + strncpy(dest->tags[i], src->tags[i], Y4M_MAX_XTAG_SIZE); + } + dest->count += src->count; + return Y4M_OK; +} + + +/************************************************************************* + * + * Creators/destructors for y4m_*_info_t structures + * + *************************************************************************/ + +void y4m_init_stream_info(y4m_stream_info_t *info) +{ + if (info == NULL) return; + /* init substructures */ + y4m_init_xtag_list(&(info->x_tags)); + /* set defaults */ + y4m_clear_stream_info(info); +} + +void y4m_clear_stream_info(y4m_stream_info_t *info) +{ + if (info == NULL) return; + /* clear/initialize info */ + info->width = Y4M_UNKNOWN; + info->height = Y4M_UNKNOWN; + info->interlace = Y4M_UNKNOWN; + info->framerate = y4m_fps_UNKNOWN; + info->sampleaspect = y4m_sar_UNKNOWN; + if (_y4mparam_feature_level < 1) { + info->chroma = Y4M_CHROMA_420JPEG; + } else { + info->chroma = Y4M_UNKNOWN; + } + y4m_xtag_clearlist(&(info->x_tags)); +} + +void y4m_copy_stream_info(y4m_stream_info_t *dest, + const y4m_stream_info_t *src) +{ + if ((dest == NULL) || (src == NULL)) return; + /* copy info */ + dest->width = src->width; + dest->height = src->height; + dest->interlace = src->interlace; + dest->framerate = src->framerate; + dest->sampleaspect = src->sampleaspect; + dest->chroma = src->chroma; + y4m_copy_xtag_list(&(dest->x_tags), &(src->x_tags)); +} + +// returns 0 if equal, nonzero if different +static int y4m_compare_stream_info(const y4m_stream_info_t *s1,const y4m_stream_info_t *s2) +{ + int i,j; + + if( s1->width != s2->width || + s1->height != s2->height || + s1->interlace != s2->interlace || + s1->framerate.n != s2->framerate.n || + s1->framerate.d != s2->framerate.d || + s1->sampleaspect.n != s2->sampleaspect.n || + s1->sampleaspect.d != s2->sampleaspect.d || + s1->chroma != s2->chroma || + s1->x_tags.count != s2->x_tags.count ) + return 1; + + // the tags may not be in the same order + for( i=0; ix_tags.count; i++ ) { + for( j=0; jx_tags.count; j++ ) + if( !strncmp(s1->x_tags.tags[i],s2->x_tags.tags[j],Y4M_MAX_XTAG_SIZE) ) + goto next; + return 1; + next:; + } + return 0; +} + +void y4m_fini_stream_info(y4m_stream_info_t *info) +{ + if (info == NULL) return; + y4m_fini_xtag_list(&(info->x_tags)); +} + +void y4m_si_set_width(y4m_stream_info_t *si, int width) +{ + si->width = width; +} + +int y4m_si_get_width(const y4m_stream_info_t *si) +{ return si->width; } + +void y4m_si_set_height(y4m_stream_info_t *si, int height) +{ + si->height = height; +} + +int y4m_si_get_height(const y4m_stream_info_t *si) +{ return si->height; } + +void y4m_si_set_interlace(y4m_stream_info_t *si, int interlace) +{ si->interlace = interlace; } + +int y4m_si_get_interlace(const y4m_stream_info_t *si) +{ return si->interlace; } + +void y4m_si_set_framerate(y4m_stream_info_t *si, y4m_ratio_t framerate) +{ si->framerate = framerate; } + +y4m_ratio_t y4m_si_get_framerate(const y4m_stream_info_t *si) +{ return si->framerate; } + +void y4m_si_set_sampleaspect(y4m_stream_info_t *si, y4m_ratio_t sar) +{ si->sampleaspect = sar; } + +y4m_ratio_t y4m_si_get_sampleaspect(const y4m_stream_info_t *si) +{ return si->sampleaspect; } + +void y4m_si_set_chroma(y4m_stream_info_t *si, int chroma_mode) +{ si->chroma = chroma_mode; } + +int y4m_si_get_chroma(const y4m_stream_info_t *si) +{ return si->chroma; } + + +int y4m_si_get_plane_count(const y4m_stream_info_t *si) +{ + switch (si->chroma) { + case Y4M_CHROMA_420JPEG: + case Y4M_CHROMA_420MPEG2: + case Y4M_CHROMA_420PALDV: + case Y4M_CHROMA_444: + case Y4M_CHROMA_422: + case Y4M_CHROMA_411: + return 3; + case Y4M_CHROMA_MONO: + return 1; + case Y4M_CHROMA_444ALPHA: + return 4; + default: + return Y4M_UNKNOWN; + } +} + +int y4m_si_get_plane_width(const y4m_stream_info_t *si, int plane) +{ + switch (plane) { + case 0: + return (si->width); + case 1: + case 2: + switch (si->chroma) { + case Y4M_CHROMA_420JPEG: + case Y4M_CHROMA_420MPEG2: + case Y4M_CHROMA_420PALDV: + return (si->width) / 2; + case Y4M_CHROMA_444: + case Y4M_CHROMA_444ALPHA: + return (si->width); + case Y4M_CHROMA_422: + return (si->width) / 2; + case Y4M_CHROMA_411: + return (si->width) / 4; + default: + return Y4M_UNKNOWN; + } + case 3: + switch (si->chroma) { + case Y4M_CHROMA_444ALPHA: + return (si->width); + default: + return Y4M_UNKNOWN; + } + default: + return Y4M_UNKNOWN; + } +} + +int y4m_si_get_plane_height(const y4m_stream_info_t *si, int plane) +{ + switch (plane) { + case 0: + return (si->height); + case 1: + case 2: + switch (si->chroma) { + case Y4M_CHROMA_420JPEG: + case Y4M_CHROMA_420MPEG2: + case Y4M_CHROMA_420PALDV: + return (si->height) / 2; + case Y4M_CHROMA_444: + case Y4M_CHROMA_444ALPHA: + case Y4M_CHROMA_422: + case Y4M_CHROMA_411: + return (si->height); + default: + return Y4M_UNKNOWN; + } + case 3: + switch (si->chroma) { + case Y4M_CHROMA_444ALPHA: + return (si->height); + default: + return Y4M_UNKNOWN; + } + default: + return Y4M_UNKNOWN; + } +} + +int y4m_si_get_plane_length(const y4m_stream_info_t *si, int plane) +{ + int w = y4m_si_get_plane_width(si, plane); + int h = y4m_si_get_plane_height(si, plane); + if ((w != Y4M_UNKNOWN) && (h != Y4M_UNKNOWN)) + return (w * h); + else + return Y4M_UNKNOWN; +} + +int y4m_si_get_framelength(const y4m_stream_info_t *si) +{ + int total = 0; + int planes = y4m_si_get_plane_count(si); + int p; + for (p = 0; p < planes; p++) { + int plen = y4m_si_get_plane_length(si, p); + if (plen == Y4M_UNKNOWN) return Y4M_UNKNOWN; + total += plen; + } + return total; +} + +y4m_xtag_list_t *y4m_si_xtags(y4m_stream_info_t *si) +{ return &(si->x_tags); } + + +void y4m_init_frame_info(y4m_frame_info_t *info) +{ + if (info == NULL) return; + /* init substructures */ + y4m_init_xtag_list(&(info->x_tags)); + /* set defaults */ + y4m_clear_frame_info(info); +} + +void y4m_clear_frame_info(y4m_frame_info_t *info) +{ + if (info == NULL) return; + /* clear/initialize info */ + info->spatial = Y4M_UNKNOWN; + info->temporal = Y4M_UNKNOWN; + info->presentation = Y4M_UNKNOWN; + y4m_xtag_clearlist(&(info->x_tags)); +} + +void y4m_copy_frame_info(y4m_frame_info_t *dest, const y4m_frame_info_t *src) +{ + if ((dest == NULL) || (src == NULL)) return; + /* copy info */ + dest->spatial = src->spatial; + dest->temporal = src->temporal; + dest->presentation = src->presentation; + y4m_copy_xtag_list(&(dest->x_tags), &(src->x_tags)); +} + +void y4m_fini_frame_info(y4m_frame_info_t *info) +{ + if (info == NULL) return; + y4m_fini_xtag_list(&(info->x_tags)); +} + +void y4m_fi_set_presentation(y4m_frame_info_t *fi, int pres) +{ fi->presentation = pres; } + +int y4m_fi_get_presentation(const y4m_frame_info_t *fi) +{ return fi->presentation; } + +void y4m_fi_set_temporal(y4m_frame_info_t *fi, int sampling) +{ fi->temporal = sampling; } + +int y4m_fi_get_temporal(const y4m_frame_info_t *fi) +{ return fi->temporal; } + +void y4m_fi_set_spatial(y4m_frame_info_t *fi, int sampling) +{ fi->spatial = sampling; } + +int y4m_fi_get_spatial(const y4m_frame_info_t *fi) +{ return fi->spatial; } + +y4m_xtag_list_t *y4m_fi_xtags(y4m_frame_info_t *fi) +{ return &(fi->x_tags); } + + +/************************************************************************* + * + * Tag parsing + * + *************************************************************************/ + +int y4m_parse_stream_tags(char *s, y4m_stream_info_t *i) +{ + char *token, *value; + char tag; + int err; + + /* parse fields */ + for (token = strtok(s, Y4M_DELIM); + token != NULL; + token = strtok(NULL, Y4M_DELIM)) { + if (token[0] == '\0') continue; /* skip empty strings */ + tag = token[0]; + value = token + 1; + switch (tag) { + case 'W': /* width */ + i->width = atoi(value); + if (i->width <= 0) return Y4M_ERR_RANGE; + break; + case 'H': /* height */ + i->height = atoi(value); + if (i->height <= 0) return Y4M_ERR_RANGE; + break; + case 'F': /* frame rate (fps) */ + if ((err = y4m_parse_ratio(&(i->framerate), value)) != Y4M_OK) + return err; + if (i->framerate.n < 0) return Y4M_ERR_RANGE; + break; + case 'I': /* interlacing */ + switch (value[0]) { + case 'p': i->interlace = Y4M_ILACE_NONE; break; + case 't': i->interlace = Y4M_ILACE_TOP_FIRST; break; + case 'b': i->interlace = Y4M_ILACE_BOTTOM_FIRST; break; + case 'm': i->interlace = Y4M_ILACE_MIXED; break; + case '?': + default: + i->interlace = Y4M_UNKNOWN; break; + } + break; + case 'A': /* sample (pixel) aspect ratio */ + if ((err = y4m_parse_ratio(&(i->sampleaspect), value)) != Y4M_OK) + return err; + if (i->sampleaspect.n < 0) return Y4M_ERR_RANGE; + break; + case 'C': + i->chroma = y4m_chroma_parse_keyword(value); + if (i->chroma == Y4M_UNKNOWN) + return Y4M_ERR_HEADER; + break; + case 'X': /* 'X' meta-tag */ + if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err; + break; + default: + /* possible error on unknown options */ + if (_y4mparam_allow_unknown_tags) { + /* unknown tags ok: store in xtag list and warn... */ + if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err; + mjpeg_warn("Unknown stream tag encountered: '%s'", token); + } else { + /* unknown tags are *not* ok */ + return Y4M_ERR_BADTAG; + } + break; + } + } + + /* Without 'C' tag or any other chroma spec, default to 420jpeg */ + if (i->chroma == Y4M_UNKNOWN) + i->chroma = Y4M_CHROMA_420JPEG; + + /* Error checking... */ + /* - Width and Height are required. */ + if ((i->width == Y4M_UNKNOWN) || (i->height == Y4M_UNKNOWN)) + return Y4M_ERR_HEADER; + /* - Non-420 chroma and mixed interlace require level >= 1 */ + if (_y4mparam_feature_level < 1) { + if ((i->chroma != Y4M_CHROMA_420JPEG) && + (i->chroma != Y4M_CHROMA_420MPEG2) && + (i->chroma != Y4M_CHROMA_420PALDV)) + return Y4M_ERR_FEATURE; + if (i->interlace == Y4M_ILACE_MIXED) + return Y4M_ERR_FEATURE; + } + + /* ta da! done. */ + return Y4M_OK; +} + + + +static int y4m_parse_frame_tags(char *s, const y4m_stream_info_t *si, + y4m_frame_info_t *fi) +{ + char *token, *value; + char tag; + int err; + + /* parse fields */ + for (token = strtok(s, Y4M_DELIM); + token != NULL; + token = strtok(NULL, Y4M_DELIM)) { + if (token[0] == '\0') continue; /* skip empty strings */ + tag = token[0]; + value = token + 1; + switch (tag) { + case 'I': + /* frame 'I' tag requires feature level >= 1 */ + if (_y4mparam_feature_level < 1) return Y4M_ERR_FEATURE; + if (si->interlace != Y4M_ILACE_MIXED) return Y4M_ERR_BADTAG; + switch (value[0]) { + case 't': fi->presentation = Y4M_PRESENT_TOP_FIRST; break; + case 'T': fi->presentation = Y4M_PRESENT_TOP_FIRST_RPT; break; + case 'b': fi->presentation = Y4M_PRESENT_BOTTOM_FIRST; break; + case 'B': fi->presentation = Y4M_PRESENT_BOTTOM_FIRST_RPT; break; + case '1': fi->presentation = Y4M_PRESENT_PROG_SINGLE; break; + case '2': fi->presentation = Y4M_PRESENT_PROG_DOUBLE; break; + case '3': fi->presentation = Y4M_PRESENT_PROG_TRIPLE; break; + default: + return Y4M_ERR_BADTAG; + } + switch (value[1]) { + case 'p': fi->temporal = Y4M_SAMPLING_PROGRESSIVE; break; + case 'i': fi->temporal = Y4M_SAMPLING_INTERLACED; break; + default: + return Y4M_ERR_BADTAG; + } + switch (value[2]) { + case 'p': fi->spatial = Y4M_SAMPLING_PROGRESSIVE; break; + case 'i': fi->spatial = Y4M_SAMPLING_INTERLACED; break; + case '?': fi->spatial = Y4M_UNKNOWN; break; + default: + return Y4M_ERR_BADTAG; + } + break; + case 'X': /* 'X' meta-tag */ + if ((err = y4m_xtag_add(&(fi->x_tags), token)) != Y4M_OK) return err; + break; + default: + /* possible error on unknown options */ + if (_y4mparam_allow_unknown_tags) { + /* unknown tags ok: store in xtag list and warn... */ + if ((err = y4m_xtag_add(&(fi->x_tags), token)) != Y4M_OK) return err; + mjpeg_warn("Unknown frame tag encountered: '%s'", token); + } else { + /* unknown tags are *not* ok */ + return Y4M_ERR_BADTAG; + } + break; + } + } + /* error-checking and/or non-mixed defaults */ + switch (si->interlace) { + case Y4M_ILACE_MIXED: + /* T and P are required if stream "Im" */ + if ((fi->presentation == Y4M_UNKNOWN) || (fi->temporal == Y4M_UNKNOWN)) + return Y4M_ERR_HEADER; + /* and S is required if stream is also 4:2:0 */ + if ( ((si->chroma == Y4M_CHROMA_420JPEG) || + (si->chroma == Y4M_CHROMA_420MPEG2) || + (si->chroma == Y4M_CHROMA_420PALDV)) && + (fi->spatial == Y4M_UNKNOWN) ) + return Y4M_ERR_HEADER; + break; + case Y4M_ILACE_NONE: + /* stream "Ip" --> equivalent to frame "I1pp" */ + fi->spatial = Y4M_SAMPLING_PROGRESSIVE; + fi->temporal = Y4M_SAMPLING_PROGRESSIVE; + fi->presentation = Y4M_PRESENT_PROG_SINGLE; + break; + case Y4M_ILACE_TOP_FIRST: + /* stream "It" --> equivalent to frame "Itii" */ + fi->spatial = Y4M_SAMPLING_INTERLACED; + fi->temporal = Y4M_SAMPLING_INTERLACED; + fi->presentation = Y4M_PRESENT_TOP_FIRST; + break; + case Y4M_ILACE_BOTTOM_FIRST: + /* stream "Ib" --> equivalent to frame "Ibii" */ + fi->spatial = Y4M_SAMPLING_INTERLACED; + fi->temporal = Y4M_SAMPLING_INTERLACED; + fi->presentation = Y4M_PRESENT_BOTTOM_FIRST; + break; + default: + /* stream unknown: then, whatever */ + break; + } + /* ta da! done. */ + return Y4M_OK; +} + + + + + +/************************************************************************* + * + * Read/Write stream header + * + *************************************************************************/ + + +static int y4m_read_stream_header_line_cb(y4m_cb_reader_t * fd, y4m_stream_info_t *i,char *line,int n) +{ + int err; + + /* start with a clean slate */ + y4m_clear_stream_info(i); + /* read the header line */ + for (; n < Y4M_LINE_MAX; n++) { + if (y4m_read_cb(fd, line+n, 1)) + return Y4M_ERR_SYSTEM; + if (line[n] == '\n') { + line[n] = '\0'; /* Replace linefeed by end of string */ + break; + } + } + /* look for keyword in header */ + if (strncmp(line, Y4M_MAGIC, strlen(Y4M_MAGIC))) + return Y4M_ERR_MAGIC; + if (n >= Y4M_LINE_MAX) + return Y4M_ERR_HEADER; + if ((err = y4m_parse_stream_tags(line + strlen(Y4M_MAGIC), i)) != Y4M_OK) + return err; + + return Y4M_OK; +} + +static int y4m_reread_stream_header_line_cb(y4m_cb_reader_t *fd,const y4m_stream_info_t *si,char *line,int n) +{ + y4m_stream_info_t i; + int err=y4m_read_stream_header_line_cb(fd,&i,line,n); + if( err==Y4M_OK && y4m_compare_stream_info(si,&i) ) + err=Y4M_ERR_HEADER; + y4m_fini_stream_info(&i); + return err; +} + +int y4m_read_stream_header_cb(y4m_cb_reader_t *fd, y4m_stream_info_t *i) +{ + char line[Y4M_LINE_MAX]; + + return y4m_read_stream_header_line_cb(fd,i,line,0); +} + +int y4m_read_stream_header(int fd, y4m_stream_info_t *i) +{ + y4m_cb_reader_t r; + set_cb_reader_from_fd(&r, &fd); + return y4m_read_stream_header_cb(&r, i); +} + +int y4m_write_stream_header_cb(y4m_cb_writer_t * fd, const y4m_stream_info_t *i) +{ + char s[Y4M_LINE_MAX+1]; + int n; + int err; + y4m_ratio_t rate = i->framerate; + y4m_ratio_t aspect = i->sampleaspect; + const char *chroma_keyword = y4m_chroma_keyword(i->chroma); + + if ((i->chroma == Y4M_UNKNOWN) || (chroma_keyword == NULL)) + return Y4M_ERR_HEADER; + if (_y4mparam_feature_level < 1) { + if ((i->chroma != Y4M_CHROMA_420JPEG) && + (i->chroma != Y4M_CHROMA_420MPEG2) && + (i->chroma != Y4M_CHROMA_420PALDV)) + return Y4M_ERR_FEATURE; + if (i->interlace == Y4M_ILACE_MIXED) + return Y4M_ERR_FEATURE; + } + y4m_ratio_reduce(&rate); + y4m_ratio_reduce(&aspect); + n = snprintf(s, sizeof(s), "%s W%d H%d F%d:%d I%s A%d:%d C%s", + Y4M_MAGIC, + i->width, + i->height, + rate.n, rate.d, + (i->interlace == Y4M_ILACE_NONE) ? "p" : + (i->interlace == Y4M_ILACE_TOP_FIRST) ? "t" : + (i->interlace == Y4M_ILACE_BOTTOM_FIRST) ? "b" : + (i->interlace == Y4M_ILACE_MIXED) ? "m" : "?", + aspect.n, aspect.d, + chroma_keyword + ); + if ((n < 0) || (n > Y4M_LINE_MAX)) return Y4M_ERR_HEADER; + if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, &(i->x_tags))) + != Y4M_OK) + return err; + /* non-zero on error */ + return (y4m_write_cb(fd, s, strlen(s)) ? Y4M_ERR_SYSTEM : Y4M_OK); +} + +int y4m_write_stream_header(int fd, const y4m_stream_info_t *i) +{ + y4m_cb_writer_t w; + set_cb_writer_from_fd(&w, &fd); + return y4m_write_stream_header_cb(&w, i); +} + + + + +/************************************************************************* + * + * Read/Write frame header + * + *************************************************************************/ + +int y4m_read_frame_header_cb(y4m_cb_reader_t * fd, + const y4m_stream_info_t *si, + y4m_frame_info_t *fi) +{ + char line[Y4M_LINE_MAX]; + char *p; + int n; + ssize_t remain; + + again: + /* start with a clean slate */ + y4m_clear_frame_info(fi); + /* This is more clever than read_stream_header... + Try to read "FRAME\n" all at once, and don't try to parse + if nothing else is there... + */ + remain = y4m_read_cb(fd, line, sizeof(Y4M_FRAME_MAGIC)-1+1); /* -'\0', +'\n' */ + if (remain < 0) return Y4M_ERR_SYSTEM; + if (remain > 0) { + /* A clean EOF should end exactly at a frame-boundary */ + if (remain == sizeof(Y4M_FRAME_MAGIC)) + return Y4M_ERR_EOF; + else + return Y4M_ERR_BADEOF; + } + if (strncmp(line, Y4M_FRAME_MAGIC, sizeof(Y4M_FRAME_MAGIC)-1)) { + int err=y4m_reread_stream_header_line_cb(fd,si,line,sizeof(Y4M_FRAME_MAGIC)-1+1); + if( err!=Y4M_OK ) + return err; + goto again; + } + if (line[sizeof(Y4M_FRAME_MAGIC)-1] == '\n') + return Y4M_OK; /* done -- no tags: that was the end-of-line. */ + + if (line[sizeof(Y4M_FRAME_MAGIC)-1] != Y4M_DELIM[0]) { + return Y4M_ERR_MAGIC; /* wasn't a space -- what was it? */ + } + + /* proceed to get the tags... (overwrite the magic) */ + for (n = 0, p = line; n < Y4M_LINE_MAX; n++, p++) { + if (y4m_read_cb(fd, p, 1)) + return Y4M_ERR_SYSTEM; + if (*p == '\n') { + *p = '\0'; /* Replace linefeed by end of string */ + break; + } + } + if (n >= Y4M_LINE_MAX) return Y4M_ERR_HEADER; + /* non-zero on error */ + return y4m_parse_frame_tags(line, si, fi); +} + +int y4m_read_frame_header(int fd, + const y4m_stream_info_t *si, + y4m_frame_info_t *fi) + { + y4m_cb_reader_t r; + set_cb_reader_from_fd(&r, &fd); + return y4m_read_frame_header_cb(&r, si, fi); + } + + +int y4m_write_frame_header_cb(y4m_cb_writer_t * fd, + const y4m_stream_info_t *si, + const y4m_frame_info_t *fi) +{ + char s[Y4M_LINE_MAX+1]; + int n, err; + + if (si->interlace == Y4M_ILACE_MIXED) { + if (_y4mparam_feature_level < 1) return Y4M_ERR_FEATURE; + n = snprintf(s, sizeof(s), "%s I%c%c%c", Y4M_FRAME_MAGIC, + (fi->presentation == Y4M_PRESENT_TOP_FIRST) ? 't' : + (fi->presentation == Y4M_PRESENT_TOP_FIRST_RPT) ? 'T' : + (fi->presentation == Y4M_PRESENT_BOTTOM_FIRST) ? 'b' : + (fi->presentation == Y4M_PRESENT_BOTTOM_FIRST_RPT) ? 'B' : + (fi->presentation == Y4M_PRESENT_PROG_SINGLE) ? '1' : + (fi->presentation == Y4M_PRESENT_PROG_DOUBLE) ? '2' : + (fi->presentation == Y4M_PRESENT_PROG_TRIPLE) ? '3' : + '?', + (fi->temporal == Y4M_SAMPLING_PROGRESSIVE) ? 'p' : + (fi->temporal == Y4M_SAMPLING_INTERLACED) ? 'i' : + '?', + (fi->spatial == Y4M_SAMPLING_PROGRESSIVE) ? 'p' : + (fi->spatial == Y4M_SAMPLING_INTERLACED) ? 'i' : + '?' + ); + } else { + n = snprintf(s, sizeof(s), "%s", Y4M_FRAME_MAGIC); + } + + if ((n < 0) || (n > Y4M_LINE_MAX)) return Y4M_ERR_HEADER; + if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, &(fi->x_tags))) + != Y4M_OK) + return err; + /* non-zero on error */ + return (y4m_write_cb(fd, s, strlen(s)) ? Y4M_ERR_SYSTEM : Y4M_OK); +} + +int y4m_write_frame_header(int fd, + const y4m_stream_info_t *si, + const y4m_frame_info_t *fi) +{ + y4m_cb_writer_t w; + set_cb_writer_from_fd(&w, &fd); + return y4m_write_frame_header_cb(&w, si, fi); +} + +/************************************************************************* + * + * Read/Write entire frame + * + *************************************************************************/ + +int y4m_read_frame_data_cb(y4m_cb_reader_t * fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, uint8_t * const *frame) +{ + int planes = y4m_si_get_plane_count(si); + int p; + + /* Read each plane */ + for (p = 0; p < planes; p++) { + int w = y4m_si_get_plane_width(si, p); + int h = y4m_si_get_plane_height(si, p); + if (y4m_read_cb(fd, frame[p], w*h)) return Y4M_ERR_SYSTEM; + } + return Y4M_OK; +} + +int y4m_read_frame_data(int fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, uint8_t * const *frame) +{ + y4m_cb_reader_t r; + set_cb_reader_from_fd(&r, &fd); + return y4m_read_frame_data_cb(&r, si, fi, frame); +} + +int y4m_read_frame_cb(y4m_cb_reader_t * fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, uint8_t * const *frame) +{ + int err; + + /* Read frame header */ + if ((err = y4m_read_frame_header_cb(fd, si, fi)) != Y4M_OK) return err; + /* Read date */ + return y4m_read_frame_data_cb(fd, si, fi, frame); +} + +int y4m_read_frame(int fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, uint8_t * const *frame) +{ + y4m_cb_reader_t r; + set_cb_reader_from_fd(&r, &fd); + return y4m_read_frame_cb(&r, si, fi, frame); +} + + + +int y4m_write_frame_cb(y4m_cb_writer_t * fd, const y4m_stream_info_t *si, + const y4m_frame_info_t *fi, uint8_t * const *frame) +{ + int planes = y4m_si_get_plane_count(si); + int err, p; + + /* Write frame header */ + if ((err = y4m_write_frame_header_cb(fd, si, fi)) != Y4M_OK) return err; + /* Write each plane */ + for (p = 0; p < planes; p++) { + int w = y4m_si_get_plane_width(si, p); + int h = y4m_si_get_plane_height(si, p); + if (y4m_write_cb(fd, frame[p], w*h)) return Y4M_ERR_SYSTEM; + } + return Y4M_OK; +} + +int y4m_write_frame(int fd, const y4m_stream_info_t *si, + const y4m_frame_info_t *fi, uint8_t * const *frame) +{ + y4m_cb_writer_t w; + set_cb_writer_from_fd(&w, &fd); + return y4m_write_frame_cb(&w, si, fi, frame); +} + +/************************************************************************* + * + * Read/Write entire frame, (de)interleaved (to)from two separate fields + * + *************************************************************************/ + + +int y4m_read_fields_data_cb(y4m_cb_reader_t * fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field) +{ + int p; + int planes = y4m_si_get_plane_count(si); + const int maxrbuf=32*1024; + uint8_t *rbuf=_y4m_alloc(maxrbuf); + int rbufpos=0,rbuflen=0; + + /* Read each plane */ + for (p = 0; p < planes; p++) { + uint8_t *dsttop = upper_field[p]; + uint8_t *dstbot = lower_field[p]; + int height = y4m_si_get_plane_height(si, p); + int width = y4m_si_get_plane_width(si, p); + int y; + /* alternately read one line into each field */ + for (y = 0; y < height; y += 2) { + if( width*2 >= maxrbuf ) { + if (y4m_read_cb(fd, dsttop, width)) goto y4merr; + if (y4m_read_cb(fd, dstbot, width)) goto y4merr; + } else { + if( rbufpos==rbuflen ) { + rbuflen=(height-y)*width; + if( rbuflen>maxrbuf ) + rbuflen=maxrbuf-maxrbuf%(2*width); + if( y4m_read_cb(fd,rbuf,rbuflen) ) + goto y4merr; + rbufpos=0; + } + + memcpy(dsttop,rbuf+rbufpos,width); rbufpos+=width; + memcpy(dstbot,rbuf+rbufpos,width); rbufpos+=width; + } + dsttop+=width; + dstbot+=width; + } + } + _y4m_free(rbuf); + return Y4M_OK; + + y4merr: + _y4m_free(rbuf); + return Y4M_ERR_SYSTEM; +} + +int y4m_read_fields_data(int fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field) +{ + y4m_cb_reader_t r; + set_cb_reader_from_fd(&r, &fd); + return y4m_read_fields_data_cb(&r, si, fi, upper_field, lower_field); +} + + +int y4m_read_fields_cb(y4m_cb_reader_t * fd, const y4m_stream_info_t *si, y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field) +{ + int err; + /* Read frame header */ + if ((err = y4m_read_frame_header_cb(fd, si, fi)) != Y4M_OK) return err; + /* Read data */ + return y4m_read_fields_data_cb(fd, si, fi, upper_field, lower_field); +} + +int y4m_read_fields(int fd, const y4m_stream_info_t *si, y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field) +{ + y4m_cb_reader_t r; + set_cb_reader_from_fd(&r, &fd); + return y4m_read_fields_cb(&r, si, fi, upper_field, lower_field); +} + +int y4m_write_fields_cb(y4m_cb_writer_t * fd, const y4m_stream_info_t *si, + const y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field) +{ + int p, err; + int planes = y4m_si_get_plane_count(si); + int numwbuf=0; + const int maxwbuf=32*1024; + uint8_t *wbuf; + + /* Write frame header */ + if ((err = y4m_write_frame_header_cb(fd, si, fi)) != Y4M_OK) return err; + /* Write each plane */ + wbuf=_y4m_alloc(maxwbuf); + for (p = 0; p < planes; p++) { + uint8_t *srctop = upper_field[p]; + uint8_t *srcbot = lower_field[p]; + int height = y4m_si_get_plane_height(si, p); + int width = y4m_si_get_plane_width(si, p); + int y; + /* alternately write one line from each field */ + for (y = 0; y < height; y += 2) { + if( width*2 >= maxwbuf ) { + if (y4m_write_cb(fd, srctop, width)) goto y4merr; + if (y4m_write_cb(fd, srcbot, width)) goto y4merr; + } else { + if (numwbuf + 2 * width > maxwbuf) { + if(y4m_write_cb(fd, wbuf, numwbuf)) goto y4merr; + numwbuf=0; + } + + memcpy(wbuf+numwbuf,srctop,width); numwbuf += width; + memcpy(wbuf+numwbuf,srcbot,width); numwbuf += width; + } + srctop += width; + srcbot += width; + } + } + if( numwbuf ) + if( y4m_write_cb(fd, wbuf, numwbuf) ) + goto y4merr; + _y4m_free(wbuf); + return Y4M_OK; + + y4merr: + _y4m_free(wbuf); + return Y4M_ERR_SYSTEM; +} + +int y4m_write_fields(int fd, const y4m_stream_info_t *si, + const y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field) +{ + y4m_cb_writer_t w; + set_cb_writer_from_fd(&w, &fd); + return y4m_write_fields_cb(&w, si, fi, upper_field, lower_field); +} + +/************************************************************************* + * + * Handy logging of stream info + * + *************************************************************************/ + +void y4m_log_stream_info(log_level_t level, const char *prefix, + const y4m_stream_info_t *i) +{ + char s[256]; + + snprintf(s, sizeof(s), " frame size: "); + if (i->width == Y4M_UNKNOWN) + snprintf(s+strlen(s), sizeof(s)-strlen(s), "(?)x"); + else + snprintf(s+strlen(s), sizeof(s)-strlen(s), "%dx", i->width); + if (i->height == Y4M_UNKNOWN) + snprintf(s+strlen(s), sizeof(s)-strlen(s), "(?) pixels "); + else + snprintf(s+strlen(s), sizeof(s)-strlen(s), "%d pixels ", i->height); + { + int framelength = y4m_si_get_framelength(i); + if (framelength == Y4M_UNKNOWN) + snprintf(s+strlen(s), sizeof(s)-strlen(s), "(? bytes)"); + else + snprintf(s+strlen(s), sizeof(s)-strlen(s), "(%d bytes)", framelength); + mjpeg_log(level, "%s%s", prefix, s); + } + { + const char *desc = y4m_chroma_description(i->chroma); + if (desc == NULL) desc = "unknown!"; + mjpeg_log(level, "%s chroma: %s", prefix, desc); + } + if ((i->framerate.n == 0) && (i->framerate.d == 0)) + mjpeg_log(level, "%s frame rate: ??? fps", prefix); + else + mjpeg_log(level, "%s frame rate: %d/%d fps (~%f)", prefix, + i->framerate.n, i->framerate.d, + (double) i->framerate.n / (double) i->framerate.d); + mjpeg_log(level, "%s interlace: %s", prefix, + (i->interlace == Y4M_ILACE_NONE) ? "none/progressive" : + (i->interlace == Y4M_ILACE_TOP_FIRST) ? "top-field-first" : + (i->interlace == Y4M_ILACE_BOTTOM_FIRST) ? "bottom-field-first" : + (i->interlace == Y4M_ILACE_MIXED) ? "mixed-mode" : + "anyone's guess"); + if ((i->sampleaspect.n == 0) && (i->sampleaspect.d == 0)) + mjpeg_log(level, "%ssample aspect ratio: ?:?", prefix); + else + mjpeg_log(level, "%ssample aspect ratio: %d:%d", prefix, + i->sampleaspect.n, i->sampleaspect.d); +} + + +/************************************************************************* + * + * Convert error code to string + * + *************************************************************************/ + +const char *y4m_strerr(int err) +{ + switch (err) { + case Y4M_OK: return "no error"; + case Y4M_ERR_RANGE: return "parameter out of range"; + case Y4M_ERR_SYSTEM: return "system error (failed read/write)"; + case Y4M_ERR_HEADER: return "bad stream or frame header"; + case Y4M_ERR_BADTAG: return "unknown header tag"; + case Y4M_ERR_MAGIC: return "bad header magic"; + case Y4M_ERR_XXTAGS: return "too many xtags"; + case Y4M_ERR_EOF: return "end-of-file"; + case Y4M_ERR_BADEOF: return "stream ended unexpectedly (EOF)"; + case Y4M_ERR_FEATURE: return "stream requires unsupported features"; + default: + return "unknown error code"; + } +} + + +/************************************************************************* + * + * Chroma subsampling stuff + * + *************************************************************************/ + +y4m_ratio_t y4m_chroma_ss_x_ratio(int chroma_mode) +{ + y4m_ratio_t r; + switch (chroma_mode) { + case Y4M_CHROMA_444ALPHA: + case Y4M_CHROMA_444: + case Y4M_CHROMA_MONO: + r.n = 1; r.d = 1; break; + case Y4M_CHROMA_420JPEG: + case Y4M_CHROMA_420MPEG2: + case Y4M_CHROMA_420PALDV: + case Y4M_CHROMA_422: + r.n = 1; r.d = 2; break; + case Y4M_CHROMA_411: + r.n = 1; r.d = 4; break; + default: + r.n = 0; r.d = 0; + } + return r; +} + +y4m_ratio_t y4m_chroma_ss_y_ratio(int chroma_mode) +{ + y4m_ratio_t r; + switch (chroma_mode) { + case Y4M_CHROMA_444ALPHA: + case Y4M_CHROMA_444: + case Y4M_CHROMA_MONO: + case Y4M_CHROMA_422: + case Y4M_CHROMA_411: + r.n = 1; r.d = 1; break; + case Y4M_CHROMA_420JPEG: + case Y4M_CHROMA_420MPEG2: + case Y4M_CHROMA_420PALDV: + r.n = 1; r.d = 2; break; + default: + r.n = 0; r.d = 0; + } + return r; +} + + +#if 0 /* unfinished work here */ +y4m_ratio_t y4m_chroma_ss_x_offset(int chroma_mode, int field, int plane) +{ + y4m_ratio_t r; + switch (chroma_mode) { + case Y4M_CHROMA_444ALPHA: + case Y4M_CHROMA_444: + case Y4M_CHROMA_MONO: + case Y4M_CHROMA_422: + case Y4M_CHROMA_411: + r.n = 0; r.d = 1; break; + case Y4M_CHROMA_420JPEG: + case Y4M_CHROMA_420MPEG2: + case Y4M_CHROMA_420PALDV: + r.n = 1; r.d = 2; break; + default: + r.n = 0; r.d = 0; + } + return r; +} + +y4m_ratio_t y4m_chroma_ss_y_offset(int chroma_mode, int field, int plane); +{ + y4m_ratio_t r; + switch (chroma_mode) { + case Y4M_CHROMA_444ALPHA: + case Y4M_CHROMA_444: + case Y4M_CHROMA_MONO: + case Y4M_CHROMA_422: + case Y4M_CHROMA_411: + r.n = 0; r.d = 1; break; + case Y4M_CHROMA_420JPEG: + case Y4M_CHROMA_420MPEG2: + case Y4M_CHROMA_420PALDV: + r.n = 1; r.d = 2; break; + default: + r.n = 0; r.d = 0; + } + return r; +} +#endif + +int y4m_chroma_parse_keyword(const char *s) +{ + if (!strcasecmp("420jpeg", s)) + return Y4M_CHROMA_420JPEG; + else if (!strcasecmp("420mpeg2", s)) + return Y4M_CHROMA_420MPEG2; + else if (!strcasecmp("420paldv", s)) + return Y4M_CHROMA_420PALDV; + else if (!strcasecmp("444", s)) + return Y4M_CHROMA_444; + else if (!strcasecmp("422", s)) + return Y4M_CHROMA_422; + else if (!strcasecmp("411", s)) + return Y4M_CHROMA_411; + else if (!strcasecmp("mono", s)) + return Y4M_CHROMA_MONO; + else if (!strcasecmp("444alpha", s)) + return Y4M_CHROMA_444ALPHA; + else + return Y4M_UNKNOWN; +} + +const char *y4m_chroma_keyword(int chroma_mode) +{ + switch (chroma_mode) { + case Y4M_CHROMA_420JPEG: return "420jpeg"; + case Y4M_CHROMA_420MPEG2: return "420mpeg2"; + case Y4M_CHROMA_420PALDV: return "420paldv"; + case Y4M_CHROMA_444: return "444"; + case Y4M_CHROMA_422: return "422"; + case Y4M_CHROMA_411: return "411"; + case Y4M_CHROMA_MONO: return "mono"; + case Y4M_CHROMA_444ALPHA: return "444alpha"; + default: + return NULL; + } +} + +const char *y4m_chroma_description(int chroma_mode) +{ + switch (chroma_mode) { + case Y4M_CHROMA_420JPEG: return "4:2:0 JPEG/MPEG-1 (interstitial)"; + case Y4M_CHROMA_420MPEG2: return "4:2:0 MPEG-2 (horiz. cositing)"; + case Y4M_CHROMA_420PALDV: return "4:2:0 PAL-DV (altern. siting)"; + case Y4M_CHROMA_444: return "4:4:4 (no subsampling)"; + case Y4M_CHROMA_422: return "4:2:2 (horiz. cositing)"; + case Y4M_CHROMA_411: return "4:1:1 (horiz. cositing)"; + case Y4M_CHROMA_MONO: return "luma plane only"; + case Y4M_CHROMA_444ALPHA: return "4:4:4 with alpha channel"; + default: + return NULL; + } +} diff --git a/veejay-current/veejay-client/src/yuv4mpeg.h b/veejay-current/veejay-client/src/yuv4mpeg.h new file mode 100644 index 00000000..79a41f64 --- /dev/null +++ b/veejay-current/veejay-client/src/yuv4mpeg.h @@ -0,0 +1,757 @@ +/* + * yuv4mpeg.h: Functions for reading and writing "new" YUV4MPEG2 streams. + * + * Stream format is described at the end of this file. + * + * + * Copyright (C) 2004 Matthew J. Marjanovic + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __YUV4MPEG_H__ +#define __YUV4MPEG_H__ + +#include +#include +#ifndef _WIN32 +#include +#endif +#include + + +/************************************************************************ + * error codes returned by y4m_* functions + ************************************************************************/ +#define Y4M_OK 0 +#define Y4M_ERR_RANGE 1 /* argument or tag value out of range */ +#define Y4M_ERR_SYSTEM 2 /* failed system call, check errno */ +#define Y4M_ERR_HEADER 3 /* illegal/malformed header */ +#define Y4M_ERR_BADTAG 4 /* illegal tag character */ +#define Y4M_ERR_MAGIC 5 /* bad header magic */ +#define Y4M_ERR_EOF 6 /* end-of-file (clean) */ +#define Y4M_ERR_XXTAGS 7 /* too many xtags */ +#define Y4M_ERR_BADEOF 8 /* unexpected end-of-file */ +#define Y4M_ERR_FEATURE 9 /* stream requires features beyond allowed level */ + + +/* generic 'unknown' value for integer parameters (e.g. interlace, height) */ +#define Y4M_UNKNOWN -1 + +/************************************************************************ + * values for the "interlace" parameter [y4m_*_interlace()] + ************************************************************************/ +#define Y4M_ILACE_NONE 0 /* non-interlaced, progressive frame */ +#define Y4M_ILACE_TOP_FIRST 1 /* interlaced, top-field first */ +#define Y4M_ILACE_BOTTOM_FIRST 2 /* interlaced, bottom-field first */ +#define Y4M_ILACE_MIXED 3 /* mixed, "refer to frame header" */ + +/************************************************************************ + * values for the "chroma" parameter [y4m_*_chroma()] + ************************************************************************/ +#define Y4M_CHROMA_420JPEG 0 /* 4:2:0, H/V centered, for JPEG/MPEG-1 */ +#define Y4M_CHROMA_420MPEG2 1 /* 4:2:0, H cosited, for MPEG-2 */ +#define Y4M_CHROMA_420PALDV 2 /* 4:2:0, alternating Cb/Cr, for PAL-DV */ +#define Y4M_CHROMA_444 3 /* 4:4:4, no subsampling, phew. */ +#define Y4M_CHROMA_422 4 /* 4:2:2, H cosited */ +#define Y4M_CHROMA_411 5 /* 4:1:1, H cosited */ +#define Y4M_CHROMA_MONO 6 /* luma plane only */ +#define Y4M_CHROMA_444ALPHA 7 /* 4:4:4 with an alpha channel */ + +/************************************************************************ + * values for sampling parameters [y4m_*_spatial(), y4m_*_temporal()] + ************************************************************************/ +#define Y4M_SAMPLING_PROGRESSIVE 0 +#define Y4M_SAMPLING_INTERLACED 1 + +/************************************************************************ + * values for "presentation" parameter [y4m_*_presentation()] + ************************************************************************/ +#define Y4M_PRESENT_TOP_FIRST 0 /* top-field-first */ +#define Y4M_PRESENT_TOP_FIRST_RPT 1 /* top-first, repeat top */ +#define Y4M_PRESENT_BOTTOM_FIRST 2 /* bottom-field-first */ +#define Y4M_PRESENT_BOTTOM_FIRST_RPT 3 /* bottom-first, repeat bottom */ +#define Y4M_PRESENT_PROG_SINGLE 4 /* single progressive frame */ +#define Y4M_PRESENT_PROG_DOUBLE 5 /* progressive frame, repeat once */ +#define Y4M_PRESENT_PROG_TRIPLE 6 /* progressive frame, repeat twice */ + +#define Y4M_MAX_NUM_PLANES 4 + +/************************************************************************ + * 'ratio' datatype, for rational numbers + * (see 'ratio' functions down below) + ************************************************************************/ +typedef struct _y4m_ratio { + int n; /* numerator */ + int d; /* denominator */ +} y4m_ratio_t; + + +/************************************************************************ + * useful standard framerates (as ratios) + ************************************************************************/ +extern const y4m_ratio_t y4m_fps_UNKNOWN; +extern const y4m_ratio_t y4m_fps_NTSC_FILM; /* 24000/1001 film (in NTSC) */ +extern const y4m_ratio_t y4m_fps_FILM; /* 24fps film */ +extern const y4m_ratio_t y4m_fps_PAL; /* 25fps PAL */ +extern const y4m_ratio_t y4m_fps_NTSC; /* 30000/1001 NTSC */ +extern const y4m_ratio_t y4m_fps_30; /* 30fps */ +extern const y4m_ratio_t y4m_fps_PAL_FIELD; /* 50fps PAL field rate */ +extern const y4m_ratio_t y4m_fps_NTSC_FIELD; /* 60000/1001 NTSC field rate */ +extern const y4m_ratio_t y4m_fps_60; /* 60fps */ + +/************************************************************************ + * useful standard sample (pixel) aspect ratios (W:H) + ************************************************************************/ +extern const y4m_ratio_t y4m_sar_UNKNOWN; +extern const y4m_ratio_t y4m_sar_SQUARE; /* square pixels */ +extern const y4m_ratio_t y4m_sar_NTSC_CCIR601; /* 525-line (NTSC) Rec.601 */ +extern const y4m_ratio_t y4m_sar_NTSC_16_9; /* 16:9 NTSC/Rec.601 */ +extern const y4m_ratio_t y4m_sar_NTSC_SVCD_4_3; /* NTSC SVCD 4:3 */ +extern const y4m_ratio_t y4m_sar_NTSC_SVCD_16_9;/* NTSC SVCD 16:9 */ +extern const y4m_ratio_t y4m_sar_PAL_CCIR601; /* 625-line (PAL) Rec.601 */ +extern const y4m_ratio_t y4m_sar_PAL_16_9; /* 16:9 PAL/Rec.601 */ +extern const y4m_ratio_t y4m_sar_PAL_SVCD_4_3; /* PAL SVCD 4:3 */ +extern const y4m_ratio_t y4m_sar_PAL_SVCD_16_9; /* PAL SVCD 16:9 */ +extern const y4m_ratio_t y4m_sar_SQR_ANA16_9; /* anamorphic 16:9 sampled */ + /* from 4:3 with square pixels */ + +/************************************************************************ + * useful standard display aspect ratios (W:H) + ************************************************************************/ +extern const y4m_ratio_t y4m_dar_UNKNOWN; +extern const y4m_ratio_t y4m_dar_4_3; /* standard TV */ +extern const y4m_ratio_t y4m_dar_16_9; /* widescreen TV */ +extern const y4m_ratio_t y4m_dar_221_100; /* word-to-your-mother TV */ + + +#define Y4M_MAX_XTAGS 32 /* maximum number of xtags in list */ +#define Y4M_MAX_XTAG_SIZE 32 /* max length of an xtag (including 'X') */ + +typedef struct _y4m_xtag_list y4m_xtag_list_t; +typedef struct _y4m_stream_info y4m_stream_info_t; +typedef struct _y4m_frame_info y4m_frame_info_t; + + +#ifdef __cplusplus +#define BEGIN_CDECLS extern "C" { +#define END_CDECLS } +#else +#define BEGIN_CDECLS +#define END_CDECLS +#endif + +BEGIN_CDECLS + +/************************************************************************ + * 'ratio' functions + ************************************************************************/ + +/* 'normalize' a ratio (remove common factors) */ +void y4m_ratio_reduce(y4m_ratio_t *r); + +/* parse "nnn:ddd" into a ratio (returns Y4M_OK or Y4M_ERR_RANGE) */ +int y4m_parse_ratio(y4m_ratio_t *r, const char *s); + +/* quick test of two ratios for equality (i.e. identical components) */ +#define Y4M_RATIO_EQL(a,b) ( ((a).n == (b).n) && ((a).d == (b).d) ) + +/* quick conversion of a ratio to a double (no divide-by-zero check!) */ +#define Y4M_RATIO_DBL(r) ((double)(r).n / (double)(r).d) + +/************************************************************************* + * + * Guess the true SAR (sample aspect ratio) from a list of commonly + * encountered values, given the "suggested" display aspect ratio (DAR), + * and the true frame width and height. + * + * Returns y4m_sar_UNKNOWN if no match is found. + * + *************************************************************************/ +y4m_ratio_t y4m_guess_sar(int width, int height, y4m_ratio_t dar); + + +/************************************************************************* + * + * Chroma Subsampling Mode information + * + * x_ratio, y_ratio - subsampling of chroma planes + * x_offset, y_offset - offset of chroma sample grid, + * relative to luma (0,0) sample + * + *************************************************************************/ + +y4m_ratio_t y4m_chroma_ss_x_ratio(int chroma_mode); +y4m_ratio_t y4m_chroma_ss_y_ratio(int chroma_mode); +#if 0 +y4m_ratio_t y4m_chroma_ss_x_offset(int chroma_mode, int field, int plane); +y4m_ratio_t y4m_chroma_ss_y_offset(int chroma_mode, int field, int plane); +#endif + +/* Given a string containing a (case-insensitive) chroma-tag keyword, + return appropriate chroma mode (or Y4M_UNKNOWN) */ +int y4m_chroma_parse_keyword(const char *s); + +/* Given a Y4M_CHROMA_* mode, return appropriate chroma-tag keyword, + or NULL if there is none. */ +const char *y4m_chroma_keyword(int chroma_mode); + +/* Given a Y4M_CHROMA_* mode, return appropriate chroma mode description, + or NULL if there is none. */ +const char *y4m_chroma_description(int chroma_mode); + + + +/************************************************************************ + * 'xtag' functions + * + * o Before using an xtag_list (but after the structure/memory has been + * allocated), you must initialize it via y4m_init_xtag_list(). + * o After using an xtag_list (but before the structure is released), + * call y4m_fini_xtag_list() to free internal memory. + * + ************************************************************************/ + +/* initialize an xtag_list structure */ +void y4m_init_xtag_list(y4m_xtag_list_t *xtags); + +/* finalize an xtag_list structure */ +void y4m_fini_xtag_list(y4m_xtag_list_t *xtags); + +/* make one xtag_list into a copy of another */ +void y4m_copy_xtag_list(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src); + +/* return number of tags in an xtag_list */ +int y4m_xtag_count(const y4m_xtag_list_t *xtags); + +/* access n'th tag in an xtag_list */ +const char *y4m_xtag_get(const y4m_xtag_list_t *xtags, int n); + +/* append a new tag to an xtag_list + returns: Y4M_OK - success + Y4M_ERR_XXTAGS - list is already full */ +int y4m_xtag_add(y4m_xtag_list_t *xtags, const char *tag); + +/* remove a tag from an xtag_list + returns: Y4M_OK - success + Y4M_ERR_RANGE - n is out of range */ +int y4m_xtag_remove(y4m_xtag_list_t *xtags, int n); + +/* remove all tags from an xtag_list + returns: Y4M_OK - success */ +int y4m_xtag_clearlist(y4m_xtag_list_t *xtags); + +/* append copies of tags from src list to dest list + returns: Y4M_OK - success + Y4M_ERR_XXTAGS - operation would overfill dest list */ +int y4m_xtag_addlist(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src); + + + +/************************************************************************ + * '*_info' functions + * + * o Before using a *_info structure (but after the structure/memory has + * been allocated), you must initialize it via y4m_init_*_info(). + * o After using a *_info structure (but before the structure is released), + * call y4m_fini_*_info() to free internal memory. + * o Use the 'set' and 'get' accessors to modify or access the fields in + * the structures; don't touch the structure directly. (Ok, so there + * is no really convenient C syntax to prevent you from doing this, + * but we are all responsible programmers here, so just don't do it!) + * + ************************************************************************/ + +/* initialize a stream_info structure */ +void y4m_init_stream_info(y4m_stream_info_t *i); + +/* finalize a stream_info structure */ +void y4m_fini_stream_info(y4m_stream_info_t *i); + +/* reset stream_info back to default/unknown values */ +void y4m_clear_stream_info(y4m_stream_info_t *info); + +/* make one stream_info into a copy of another */ +void y4m_copy_stream_info(y4m_stream_info_t *dest, + const y4m_stream_info_t *src); + +/* access or set stream_info fields */ +/* level 0 */ +int y4m_si_get_width(const y4m_stream_info_t *si); +int y4m_si_get_height(const y4m_stream_info_t *si); +int y4m_si_get_interlace(const y4m_stream_info_t *si); +y4m_ratio_t y4m_si_get_framerate(const y4m_stream_info_t *si); +y4m_ratio_t y4m_si_get_sampleaspect(const y4m_stream_info_t *si); +void y4m_si_set_width(y4m_stream_info_t *si, int width); +void y4m_si_set_height(y4m_stream_info_t *si, int height); +void y4m_si_set_interlace(y4m_stream_info_t *si, int interlace); +void y4m_si_set_framerate(y4m_stream_info_t *si, y4m_ratio_t framerate); +void y4m_si_set_sampleaspect(y4m_stream_info_t *si, y4m_ratio_t sar); +/* level 1 */ +void y4m_si_set_chroma(y4m_stream_info_t *si, int chroma_mode); +int y4m_si_get_chroma(const y4m_stream_info_t *si); + +/* derived quantities (no setter) */ +/* level 0 */ +int y4m_si_get_framelength(const y4m_stream_info_t *si); +/* level 1 */ +int y4m_si_get_plane_count(const y4m_stream_info_t *si); +int y4m_si_get_plane_width(const y4m_stream_info_t *si, int plane); +int y4m_si_get_plane_height(const y4m_stream_info_t *si, int plane); +int y4m_si_get_plane_length(const y4m_stream_info_t *si, int plane); + + +/* access stream_info xtag_list */ +y4m_xtag_list_t *y4m_si_xtags(y4m_stream_info_t *si); + + +/* initialize a frame_info structure */ +void y4m_init_frame_info(y4m_frame_info_t *i); + +/* finalize a frame_info structure */ +void y4m_fini_frame_info(y4m_frame_info_t *i); + +/* reset frame_info back to default/unknown values */ +void y4m_clear_frame_info(y4m_frame_info_t *info); + +/* make one frame_info into a copy of another */ +void y4m_copy_frame_info(y4m_frame_info_t *dest, + const y4m_frame_info_t *src); + + +/* access or set frame_info fields (level 1) */ +int y4m_fi_get_presentation(const y4m_frame_info_t *fi); +int y4m_fi_get_temporal(const y4m_frame_info_t *fi); +int y4m_fi_get_spatial(const y4m_frame_info_t *fi); + +void y4m_fi_set_presentation(y4m_frame_info_t *fi, int pres); +void y4m_fi_set_temporal(y4m_frame_info_t *fi, int sampling); +void y4m_fi_set_spatial(y4m_frame_info_t *fi, int sampling); + + +/* access frame_info xtag_list */ +y4m_xtag_list_t *y4m_fi_xtags(y4m_frame_info_t *fi); + + +/************************************************************************ + * blocking read and write functions + * + * o guaranteed to transfer entire payload (or fail) + * o return values: + * 0 (zero) complete success + * -(# of remaining bytes) error (and errno left set) + * +(# of remaining bytes) EOF (for y4m_read only) + * + ************************************************************************/ + +/* read len bytes from fd into buf */ +ssize_t y4m_read(int fd, void *buf, size_t len); + +/* write len bytes from fd into buf */ +ssize_t y4m_write(int fd, const void *buf, size_t len); + +/************************************************************************ + * callback based read and write + * + * The structures y4m_cb_reader_t and y4m_cb_writer_t must be + * set up by the caller before and of the *_read_*_cb() or + * *_write_*_cb() functions are called. Te return values of + * the read() and write() members have the same meaning as for + * y4m_read() and y4m_write() + * + ************************************************************************/ + +typedef struct y4m_cb_reader_s + { + void * data; + ssize_t (*read)(void * data, void *buf, size_t len); + } y4m_cb_reader_t; + +typedef struct y4m_cb_writer_s + { + void * data; + ssize_t (*write)(void * data, const void *buf, size_t len); + } y4m_cb_writer_t; + +/* read len bytes from fd into buf */ +ssize_t y4m_read_cb(y4m_cb_reader_t * fd, void *buf, size_t len); + +/* write len bytes from fd into buf */ +ssize_t y4m_write_cb(y4m_cb_writer_t * fd, const void *buf, size_t len); + + +/************************************************************************ + * stream header processing functions + * + * o return values: + * Y4M_OK - success + * Y4M_ERR_* - error (see y4m_strerr() for descriptions) + * + ************************************************************************/ + +/* parse a string of stream header tags */ +int y4m_parse_stream_tags(char *s, y4m_stream_info_t *i); + +/* read a stream header from file descriptor fd + (the current contents of stream_info are erased first) */ +int y4m_read_stream_header(int fd, y4m_stream_info_t *i); + +/* read a stream header with a callback reader + (the current contents of stream_info are erased first) */ +int y4m_read_stream_header_cb(y4m_cb_reader_t * fd, y4m_stream_info_t *i); + +/* write a stream header to file descriptor fd */ +int y4m_write_stream_header(int fd, const y4m_stream_info_t *i); + +/* write a stream header with a callback writer */ +int y4m_write_stream_header_cb(y4m_cb_writer_t * fd, const y4m_stream_info_t *i); + + +/************************************************************************ + * frame processing functions + * + * o return values: + * Y4M_OK - success + * Y4M_ERR_* - error (see y4m_strerr() for descriptions) + * + ************************************************************************/ + +/* write a frame header to file descriptor fd */ +int y4m_write_frame_header(int fd, + const y4m_stream_info_t *si, + const y4m_frame_info_t *fi); + +/* write a frame header with a callback writer fd */ +int y4m_write_frame_header_cb(y4m_cb_writer_t * fd, + const y4m_stream_info_t *si, + const y4m_frame_info_t *fi); + +/* write a complete frame (header + data) to file descriptor fd + o planes[] points to 1-4 buffers, one each for image plane */ +int y4m_write_frame(int fd, const y4m_stream_info_t *si, + const y4m_frame_info_t *fi, uint8_t * const *planes); + +/* write a complete frame (header + data) with a callback writer fd + o planes[] points to 1-4 buffers, one each for image plane */ +int y4m_write_frame_cb(y4m_cb_writer_t * fd, const y4m_stream_info_t *si, + const y4m_frame_info_t *fi, uint8_t * const *planes); + +/* write a complete frame (header + data), to file descriptor fd + but interleave fields from two separate buffers + o upper_field[] same as planes[] above, but for upper field only + o lower_field[] same as planes[] above, but for lower field only +*/ +int y4m_write_fields(int fd, const y4m_stream_info_t *si, + const y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field); + +/* write a complete frame (header + data), with a callback writer fd + but interleave fields from two separate buffers + o upper_field[] same as planes[] above, but for upper field only + o lower_field[] same as planes[] above, but for lower field only +*/ +int y4m_write_fields_cb(y4m_cb_writer_t * fd, const y4m_stream_info_t *si, + const y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field); + +/* read a frame header from file descriptor fd + (the current contents of frame_info are erased first) */ +int y4m_read_frame_header(int fd, + const y4m_stream_info_t *si, + y4m_frame_info_t *fi); + +/* read a frame header with callback reader fd + (the current contents of frame_info are erased first) */ +int y4m_read_frame_header_cb(y4m_cb_reader_t * fd, + const y4m_stream_info_t *si, + y4m_frame_info_t *fi); + +/* read frame data from file descriptor fd + [to be called after y4m_read_frame_header()] + o planes[] points to 1-4 buffers, one each for image plane */ +int y4m_read_frame_data(int fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, uint8_t * const *planes); + +/* read frame data with callback reader fd + [to be called after y4m_read_frame_header_cb()] + o planes[] points to 1-4 buffers, one each for image plane */ +int y4m_read_frame_data_cb(y4m_cb_reader_t * fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, uint8_t * const *planes); + +/* read frame data from file descriptor fd, + but de-interleave fields into two separate buffers + [to be called after y4m_read_frame_header()] + o upper_field[] same as planes[] above, but for upper field only + o lower_field[] same as planes[] above, but for lower field only +*/ +int y4m_read_fields_data(int fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field); + +/* read frame data with callback reader fd, + but de-interleave fields into two separate buffers + [to be called after y4m_read_frame_header_cb()] + o upper_field[] same as planes[] above, but for upper field only + o lower_field[] same as planes[] above, but for lower field only +*/ +int y4m_read_fields_data_cb(y4m_cb_reader_t * fd, + const y4m_stream_info_t *si, + y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field); + +/* read a complete frame (header + data) from file descriptor fd, + o planes[] points to 1-4 buffers, one each for image plane */ +int y4m_read_frame(int fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, uint8_t * const *planes); + +/* read a complete frame (header + data) from callback reader fd, + o planes[] points to 1-4 buffers, one each for image plane */ +int y4m_read_frame_cb(y4m_cb_reader_t * fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, uint8_t * const *planes); + +/* read a complete frame (header + data) from file descriptor fd, + but de-interleave fields into two separate buffers + o upper_field[] same as planes[] above, but for upper field only + o lower_field[] same as planes[] above, but for lower field only +*/ +int y4m_read_fields(int fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field); + +/* read a complete frame (header + data) from callback_reader fd, + but de-interleave fields into two separate buffers + o upper_field[] same as planes[] above, but for upper field only + o lower_field[] same as planes[] above, but for lower field only +*/ +int y4m_read_fields_cb(y4m_cb_reader_t * fd, const y4m_stream_info_t *si, + y4m_frame_info_t *fi, + uint8_t * const *upper_field, + uint8_t * const *lower_field); + +/************************************************************************ + * miscellaneous functions + ************************************************************************/ + +/* convenient dump of stream header info via mjpeg_log facility + * - each logged/printed line is prefixed by 'prefix' + */ +void y4m_log_stream_info(log_level_t level, const char *prefix, + const y4m_stream_info_t *i); + +/* convert a Y4M_ERR_* error code into mildly explanatory string */ +const char *y4m_strerr(int err); + +/* set 'allow_unknown_tag' flag for library... + o yn = 0 : unknown header tags will produce a parsing error + o yn = 1 : unknown header tags/values will produce a warning, but + are otherwise passed along via the xtags list + o yn = -1: don't change, just return current setting + + return value: previous setting of flag +*/ +int y4m_allow_unknown_tags(int yn); + + +/* set level of "accepted extensions" for the library... + o level = 0: default - conform to original YUV4MPEG2 spec; yield errors + when reading or writing a stream which exceeds it. + o level = 1: allow reading/writing streams which contain non-420jpeg + chroma and/or mixed-mode interlacing + o level = -1: don't change, just return current setting + + return value: previous setting of level + */ +int y4m_accept_extensions(int level); + + +END_CDECLS + + +/************************************************************************ + ************************************************************************ + + Description of the (new!, forever?) YUV4MPEG2 stream format: + + STREAM consists of + o one '\n' terminated STREAM-HEADER + o unlimited number of FRAMEs + + FRAME consists of + o one '\n' terminated FRAME-HEADER + o "length" octets of planar YCrCb 4:2:0 image data + (if frame is interlaced, then the two fields are interleaved) + + + STREAM-HEADER consists of + o string "YUV4MPEG2" + o unlimited number TAGGED-FIELDs, each preceded by ' ' separator + o '\n' line terminator + + FRAME-HEADER consists of + o string "FRAME" + o unlimited number of TAGGED-FIELDs, each preceded by ' ' separator + o '\n' line terminator + + + TAGGED-FIELD consists of + o single ascii character tag + o VALUE (which does not contain whitespace) + + VALUE consists of + o integer (base 10 ascii representation) + or o RATIO + or o single ascii character + or o non-whitespace ascii string + + RATIO consists of + o numerator (integer) + o ':' (a colon) + o denominator (integer) + + + The currently supported tags for the STREAM-HEADER: + W - [integer] frame width, pixels, should be > 0 + H - [integer] frame height, pixels, should be > 0 + C - [string] chroma-subsampling/data format + 420jpeg (default) + 420mpeg2 + 420paldv + 411 + 422 + 444 - non-subsampled Y'CbCr + 444alpha - Y'CbCr with alpha channel (with Y' black/white point) + mono - Y' plane only + I - [char] interlacing: p - progressive (none) + t - top-field-first + b - bottom-field-first + m - mixed -- see 'I' tag in frame header + ? - unknown + F - [ratio] frame-rate, 0:0 == unknown + A - [ratio] sample (pixel) aspect ratio, 0:0 == unknown + X - [character string] 'metadata' (unparsed, but passed around) + + The currently supported tags for the FRAME-HEADER: + Ixyz - framing/sampling (required if-and-only-if stream is "Im") + x: t - top-field-first + T - top-field-first and repeat + b - bottom-field-first + B - bottom-field-first and repeat + 1 - single progressive frame + 2 - double progressive frame (repeat) + 3 - triple progressive frame (repeat twice) + + y: p - progressive: fields sampled at same time + i - interlaced: fields sampled at different times + + z: p - progressive: subsampling over whole frame + i - interlaced: each field subsampled independently + ? - unknown (allowed only for non-4:2:0 subsampling) + + X - character string 'metadata' (unparsed, but passed around) + + ************************************************************************ + ************************************************************************/ + + +/* + + THAT'S ALL FOLKS! + + Thank you for reading the source code. We hope you have thoroughly + enjoyed the experience. + +*/ + + + + + +#ifdef INTERNAL_Y4M_LIBCODE_STUFF_QPX +#define Y4MPRIVATIZE(identifier) identifier +#else +#define Y4MPRIVATIZE(identifier) PRIVATE##identifier +#endif + +/* + * Actual structure definitions of structures which you shouldn't touch. + * + */ + +/************************************************************************ + * 'xtag_list' --- list of unparsed and/or meta/X header tags + * + * Do not touch this structure directly! + * + * Use the y4m_xtag_*() functions (see below). + * You must initialize/finalize this structure before/after use. + ************************************************************************/ +struct _y4m_xtag_list { + int Y4MPRIVATIZE(count); + char *Y4MPRIVATIZE(tags)[Y4M_MAX_XTAGS]; +}; + + +/************************************************************************ + * 'stream_info' --- stream header information + * + * Do not touch this structure directly! + * + * Use the y4m_si_*() functions (see below). + * You must initialize/finalize this structure before/after use. + ************************************************************************/ +struct _y4m_stream_info { + /* values from header/setters */ + int Y4MPRIVATIZE(width); + int Y4MPRIVATIZE(height); + int Y4MPRIVATIZE(interlace); /* see Y4M_ILACE_* definitions */ + y4m_ratio_t Y4MPRIVATIZE(framerate); /* see Y4M_FPS_* definitions */ + y4m_ratio_t Y4MPRIVATIZE(sampleaspect); /* see Y4M_SAR_* definitions */ + int Y4MPRIVATIZE(chroma); /* see Y4M_CHROMA_* definitions */ + + /* mystical X tags */ + y4m_xtag_list_t Y4MPRIVATIZE(x_tags); +}; + + +/************************************************************************ + * 'frame_info' --- frame header information + * + * Do not touch this structure directly! + * + * Use the y4m_fi_*() functions (see below). + * You must initialize/finalize this structure before/after use. + ************************************************************************/ + +struct _y4m_frame_info { + int Y4MPRIVATIZE(spatial); /* see Y4M_SAMPLING_* definitions */ + int Y4MPRIVATIZE(temporal); /* see Y4M_SAMPLING_* definitions */ + int Y4MPRIVATIZE(presentation); /* see Y4M_PRESENT_* definitions */ + /* mystical X tags */ + y4m_xtag_list_t Y4MPRIVATIZE(x_tags); +}; + + +#undef Y4MPRIVATIZE + + +#endif /* __YUV4MPEG_H__ */ + + diff --git a/veejay-current/veejay-client/src/yuv4mpeg_intern.h b/veejay-current/veejay-client/src/yuv4mpeg_intern.h new file mode 100644 index 00000000..3ada1cba --- /dev/null +++ b/veejay-current/veejay-client/src/yuv4mpeg_intern.h @@ -0,0 +1,85 @@ +/* + * yuv4mpeg_intern.h: Internal constants for "new" YUV4MPEG streams + * + * Copyright (C) 2001 Andrew Stevens + * Copyright (C) 2001 Matthew J. Marjanovic + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __YUV4MPEG_INTERN_H__ +#define __YUV4MPEG_INTERN_H__ + + +#define Y4M_MAGIC "YUV4MPEG2" +#define Y4M_FRAME_MAGIC "FRAME" + +#define Y4M_DELIM " " /* single-character(space) separating tagged fields */ + +#define Y4M_LINE_MAX 256 /* max number of characters in a header line + (including the '\n', but not the '\0') */ + + +/* standard framerate ratios */ +#define Y4M_FPS_UNKNOWN { 0, 0 } +#define Y4M_FPS_NTSC_FILM { 24000, 1001 } +#define Y4M_FPS_FILM { 24, 1 } +#define Y4M_FPS_PAL { 25, 1 } +#define Y4M_FPS_NTSC { 30000, 1001 } +#define Y4M_FPS_30 { 30, 1 } +#define Y4M_FPS_PAL_FIELD { 50, 1 } +#define Y4M_FPS_NTSC_FIELD { 60000, 1001 } +#define Y4M_FPS_60 { 60, 1 } + +/* standard sample/pixel aspect ratios */ +#define Y4M_SAR_UNKNOWN { 0, 0 } +#define Y4M_SAR_SQUARE { 1, 1 } +#define Y4M_SAR_SQR_ANA_16_9 { 4, 3 } +#define Y4M_SAR_NTSC_CCIR601 { 10, 11 } +#define Y4M_SAR_NTSC_16_9 { 40, 33 } +#define Y4M_SAR_NTSC_SVCD_4_3 { 15, 11 } +#define Y4M_SAR_NTSC_SVCD_16_9 { 20, 11 } +#define Y4M_SAR_PAL_CCIR601 { 59, 54 } +#define Y4M_SAR_PAL_16_9 { 118, 81 } +#define Y4M_SAR_PAL_SVCD_4_3 { 59, 36 } +#define Y4M_SAR_PAL_SVCD_16_9 { 59, 27 } + +#define Y4M_SAR_MPEG1_1 Y4M_SAR_SQUARE +#define Y4M_SAR_MPEG1_2 { 10000, 6735 } +#define Y4M_SAR_MPEG1_3 { 10000, 7031 } /* Anamorphic 16:9 PAL */ +#define Y4M_SAR_MPEG1_4 { 10000, 7615 } +#define Y4M_SAR_MPEG1_5 { 10000, 8055 } +#define Y4M_SAR_MPEG1_6 { 10000, 8437 } /* Anamorphic 16:9 NTSC */ +#define Y4M_SAR_MPEG1_7 { 10000, 8935 } +#define Y4M_SAR_MPEG1_8 { 10000, 9375 } /* PAL/SECAM 4:3 */ +#define Y4M_SAR_MPEG1_9 { 10000, 9815 } +#define Y4M_SAR_MPEG1_10 { 10000, 10255 } +#define Y4M_SAR_MPEG1_11 { 10000, 10695 } +#define Y4M_SAR_MPEG1_12 { 10000, 11250 } /* NTSC 4:3 */ +#define Y4M_SAR_MPEG1_13 { 10000, 11575 } +#define Y4M_SAR_MPEG1_14 { 10000, 12015 } + +#define Y4M_DAR_UNKNOWN { 0, 0 } +#define Y4M_DAR_4_3 { 4, 3 } +#define Y4M_DAR_16_9 { 16, 9 } +#define Y4M_DAR_221_100 { 221, 100 } + +#define Y4M_DAR_MPEG2_1 { 1, 1 } +#define Y4M_DAR_MPEG2_2 { 4, 3 } +#define Y4M_DAR_MPEG2_3 { 16, 9 } +#define Y4M_DAR_MPEG2_4 { 221, 100 } + +#endif /* __YUV4MPEG_INTERN_H__ */ + diff --git a/veejay-current/veejay-client/src/yuv4mpeg_ratio.c b/veejay-current/veejay-client/src/yuv4mpeg_ratio.c new file mode 100644 index 00000000..7b50578c --- /dev/null +++ b/veejay-current/veejay-client/src/yuv4mpeg_ratio.c @@ -0,0 +1,157 @@ +/* + * yuv4mpeg_ratio.c: Functions for dealing with y4m_ratio_t datatype. + * + * Copyright (C) 2001 Matthew J. Marjanovic + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include + +#include +#include "yuv4mpeg.h" +#include "yuv4mpeg_intern.h" + + +/* useful list of standard framerates */ +const y4m_ratio_t y4m_fps_UNKNOWN = Y4M_FPS_UNKNOWN; +const y4m_ratio_t y4m_fps_NTSC_FILM = Y4M_FPS_NTSC_FILM; +const y4m_ratio_t y4m_fps_FILM = Y4M_FPS_FILM; +const y4m_ratio_t y4m_fps_PAL = Y4M_FPS_PAL; +const y4m_ratio_t y4m_fps_NTSC = Y4M_FPS_NTSC; +const y4m_ratio_t y4m_fps_30 = Y4M_FPS_30; +const y4m_ratio_t y4m_fps_PAL_FIELD = Y4M_FPS_PAL_FIELD; +const y4m_ratio_t y4m_fps_NTSC_FIELD = Y4M_FPS_NTSC_FIELD; +const y4m_ratio_t y4m_fps_60 = Y4M_FPS_60; + +/* useful list of standard sample aspect ratios */ +const y4m_ratio_t y4m_sar_UNKNOWN = Y4M_SAR_UNKNOWN; +const y4m_ratio_t y4m_sar_SQUARE = Y4M_SAR_SQUARE; +const y4m_ratio_t y4m_sar_SQR_ANA_16_9 = Y4M_SAR_SQR_ANA_16_9; +const y4m_ratio_t y4m_sar_NTSC_CCIR601 = Y4M_SAR_NTSC_CCIR601; +const y4m_ratio_t y4m_sar_NTSC_16_9 = Y4M_SAR_NTSC_16_9; +const y4m_ratio_t y4m_sar_NTSC_SVCD_4_3 = Y4M_SAR_NTSC_SVCD_4_3; +const y4m_ratio_t y4m_sar_NTSC_SVCD_16_9 = Y4M_SAR_NTSC_SVCD_16_9; +const y4m_ratio_t y4m_sar_PAL_CCIR601 = Y4M_SAR_PAL_CCIR601; +const y4m_ratio_t y4m_sar_PAL_16_9 = Y4M_SAR_PAL_16_9; +const y4m_ratio_t y4m_sar_PAL_SVCD_4_3 = Y4M_SAR_PAL_SVCD_4_3; +const y4m_ratio_t y4m_sar_PAL_SVCD_16_9 = Y4M_SAR_PAL_SVCD_16_9; + +/* useful list of standard display aspect ratios */ +const y4m_ratio_t y4m_dar_UNKNOWN = Y4M_DAR_UNKNOWN; +const y4m_ratio_t y4m_dar_4_3 = Y4M_DAR_4_3; +const y4m_ratio_t y4m_dar_16_9 = Y4M_DAR_16_9; +const y4m_ratio_t y4m_dar_221_100 = Y4M_DAR_221_100; + +/* + * Euler's algorithm for greatest common divisor + */ + +static int gcd(int a, int b) +{ + a = (a >= 0) ? a : -a; + b = (b >= 0) ? b : -b; + + while (b > 0) { + int x = b; + b = a % b; + a = x; + } + return a; +} + + +/************************************************************************* + * + * Remove common factors from a ratio + * + *************************************************************************/ + + +void y4m_ratio_reduce(y4m_ratio_t *r) +{ + int d; + if ((r->n == 0) && (r->d == 0)) return; /* "unknown" */ + d = gcd(r->n, r->d); + r->n /= d; + r->d /= d; +} + + + +/************************************************************************* + * + * Parse "nnn:ddd" into a ratio + * + * returns: Y4M_OK - success + * Y4M_ERR_RANGE - range error + * + *************************************************************************/ + +int y4m_parse_ratio(y4m_ratio_t *r, const char *s) +{ + char *t = strchr(s, ':'); + if (t == NULL) return Y4M_ERR_RANGE; + r->n = atoi(s); + r->d = atoi(t+1); + if (r->d < 0) return Y4M_ERR_RANGE; + /* 0:0 == unknown, so that is ok, otherwise zero denominator is bad */ + if ((r->d == 0) && (r->n != 0)) return Y4M_ERR_RANGE; + y4m_ratio_reduce(r); + return Y4M_OK; +} + + + +/************************************************************************* + * + * Guess the true SAR (sample aspect ratio) from a list of commonly + * encountered values, given the "suggested" display aspect ratio, and + * the true frame width and height. + * + * Returns y4m_sar_UNKNOWN if no match is found. + * + *************************************************************************/ + +/* this is big enough to accommodate the difference between 720 and 704 */ +#define GUESS_ASPECT_TOLERANCE 0.03 + +y4m_ratio_t y4m_guess_sar(int width, int height, y4m_ratio_t dar) +{ + int i; + double implicit_sar = (double)(dar.n * height) / (double)(dar.d * width); + const y4m_ratio_t *sarray[] = + { + &y4m_sar_SQUARE, + &y4m_sar_NTSC_CCIR601, + &y4m_sar_NTSC_16_9, + &y4m_sar_NTSC_SVCD_4_3, + &y4m_sar_NTSC_SVCD_16_9, + &y4m_sar_PAL_CCIR601, + &y4m_sar_PAL_16_9, + &y4m_sar_PAL_SVCD_4_3, + &y4m_sar_PAL_SVCD_16_9, + &y4m_sar_UNKNOWN + }; + for (i = 0; !(Y4M_RATIO_EQL(*(sarray[i]),y4m_sar_UNKNOWN)); i++) { + double ratio = implicit_sar / Y4M_RATIO_DBL(*(sarray[i])); + if ( (ratio > (1.0 - GUESS_ASPECT_TOLERANCE)) && + (ratio < (1.0 + GUESS_ASPECT_TOLERANCE)) ) + return *(sarray[i]); + } + return y4m_sar_UNKNOWN; +}