diff --git a/libvo/vo_pnm.c b/libvo/vo_pnm.c new file mode 100644 index 0000000000..14819e4504 --- /dev/null +++ b/libvo/vo_pnm.c @@ -0,0 +1,666 @@ +/* ------------------------------------------------------------------------- */ + +/* + * vo_pnm.c, PPM/PGM/PGMYUV Video Output Driver for MPlayer + * + * + * Written by Ivo van Poorten. (GPL)2004 + * + * + * Changelog + * + * 2004-09-09 First draft. + * 2004-09-16 Second draft. It now acts on VOCTRL_DRAW_IMAGE and does not + * maintain a local copy of the image if the format is YV12. + * Speed improvement and uses less memory. + * + * + */ + +/* ------------------------------------------------------------------------- */ + +/* Global Includes */ + +#include +#include +#include +#include +#include +#include /* for log10() */ + +/* ------------------------------------------------------------------------- */ + +/* Local Includes */ + +#include "config.h" +#include "mp_msg.h" +#include "video_out.h" +#include "video_out_internal.h" +#include "mplayer.h" /* for exit_player() */ +#include "help_mp.h" + +/* ------------------------------------------------------------------------- */ + +/* Defines */ + +/* Used for temporary buffers to store file- and pathnames */ +#define BUFLENGTH 512 + +#define PNM_ASCII_MODE 0 +#define PNM_RAW_MODE 1 +#define PNM_TYPE_PPM 0 +#define PNM_TYPE_PGM 1 +#define PNM_TYPE_PGMYUV 2 + +#define PNM_LINE_OF_ASCII "%03d %03d %03d %03d %03d %03d %03d %03d %03d %03d %03d %03d %03d %03d %03d\n" +#define PNM_LINE15(a,b) a[b], a[b+1], a[b+2], a[b+3], a[b+4], a[b+5], a[b+6], \ + a[b+7], a[b+8], a[b+9], a[b+10], a[b+11], a[b+12], \ + a[b+13], a[b+14] + +/* ------------------------------------------------------------------------- */ + +/* Info */ + +static vo_info_t info= +{ + "PPM/PGM/PGMYUV file", + "pnm", + "Ivo van Poorten (ivop@euronet.nl)", + "" +}; + +LIBVO_EXTERN (pnm) + +/* ------------------------------------------------------------------------- */ + +/* Global Variables */ + +int pnm_type = PNM_TYPE_PPM; +int pnm_mode = PNM_RAW_MODE; + +char *pnm_outdir = NULL; +char *pnm_subdirs = NULL; +int pnm_maxfiles = 1000; +char *pnm_file_extension = NULL; + +/* ------------------------------------------------------------------------- */ + +/** \brief Memory allocation failed. + * + * The video output driver failed to allocate a block of memory it needed. + * It displays a message and exits the player. + * + * \return nothing It does not return. + */ + +void pnm_malloc_failed(void) { + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s\n", info.short_name, + MSGTR_MemAllocFailed); + exit_player(MSGTR_Exit_error); +} + +/* ------------------------------------------------------------------------- */ + +/** \brief An error occured while writing to a file. + * + * The program failed to write data to a file. + * It displays a message and exits the player. + * + * \return nothing It does not return. + */ + +void pnm_write_error(void) { + mp_msg(MSGT_VO, MSGL_ERR, MSGTR_ErrorWritingFile, info.short_name); + exit_player(MSGTR_Exit_error); +} + +/* ------------------------------------------------------------------------- */ + +/** \brief Pre-initialisation. + * + * This function is called before initialising the video output driver. It + * parses all suboptions and sets variables accordingly. If an error occurs + * (like an option being out of range, not having any value or an unknown + * option is stumbled upon) the player will exit. + * + * \param arg A string containing all the suboptions passed to the video + * output driver. + * + * \return 0 All went well. + */ + +static uint32_t preinit(const char *arg) +{ + char *buf; /* buf is used to store parsed string values */ + int value; /* storage for parsed integer values */ + + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s\n", info.short_name, + MSGTR_VO_ParsingSuboptions); + + if (arg) { + + while (*arg != '\0') { + if (!strncmp(arg, ":", 1)) { + arg++; + continue; /* multiple ':' is not really an error */ + } if (!strncmp(arg, "ascii", 5)) { + arg += 5; + pnm_mode = PNM_ASCII_MODE; + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s\n", info.short_name, + MSGTR_VO_PNM_ASCIIMode); + } else if (!strncmp(arg, "raw", 3)) { + arg += 3; + pnm_mode = PNM_RAW_MODE; + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s\n", info.short_name, + MSGTR_VO_PNM_RawMode); + } else if (!strncmp(arg, "ppm", 3)) { + arg += 3; + pnm_type = PNM_TYPE_PPM; + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s\n", info.short_name, + MSGTR_VO_PNM_PPMType); + } else if (!strncmp(arg, "pgmyuv", 6)) { + arg += 6; + pnm_type = PNM_TYPE_PGMYUV; + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s\n", info.short_name, + MSGTR_VO_PNM_PGMYUVType); + } else if (!strncmp(arg, "pgm", 3)) { + arg += 3; + pnm_type = PNM_TYPE_PGM; + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s\n", info.short_name, + MSGTR_VO_PNM_PGMType); + } else if (!strncmp(arg, "outdir=", 7)) { + arg += 7; + buf = malloc(strlen(arg)+1); /* maximum length possible */ + if (!buf) { + pnm_malloc_failed(); /* message and exit_player! */ + } + if (sscanf(arg, "%[^:]", buf) == 1) { + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s --> %s\n", + info.short_name, "outdir", buf); + arg += strlen(buf); + pnm_outdir = strdup(buf); + if (!pnm_outdir) pnm_malloc_failed(); + free(buf); + } else { + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s - %s\n", + info.short_name, "outdir", + MSGTR_VO_NoValueSpecified); + exit_player(MSGTR_Exit_error); + } + } else if (!strncmp(arg, "subdirs=", 8)) { + arg += 8; + buf = malloc(strlen(arg)+1); /* maximum length possible */ + if (!buf) { + pnm_malloc_failed(); + } + if (sscanf(arg, "%[^:]", buf) == 1) { + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s --> %s\n", + info.short_name, "subdirs", buf); + arg += strlen(buf); + pnm_subdirs = strdup(buf); + if (!pnm_subdirs) pnm_malloc_failed(); + free(buf); + } else { + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s - %s\n", + info.short_name, "subdirs", + MSGTR_VO_NoValueSpecified); + exit_player(MSGTR_Exit_error); + } + } else if (!strncmp(arg, "maxfiles=", 9)) { + arg += 9; + if (sscanf(arg, "%d", &value) == 1) { + if (value < 1) { + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s - %s %s.\n", + info.short_name, "maxfiles", + MSGTR_VO_ValueOutOfRange, ">=1"); + exit_player(MSGTR_Exit_error); + } else { + pnm_maxfiles = value; + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s --> %d\n", + info.short_name, "maxfiles", value); + } + } else { + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s - %s\n", + info.short_name, "maxfiles", + MSGTR_VO_NoValueSpecified); + exit_player(MSGTR_Exit_error); + } + /* only here if value is set and sane */ + if (value) { + arg += (int)log10(value) + 1; + } else { + arg++; /* log10(0) fails */ + } + } else { + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s %-20s...\n", info.short_name, + MSGTR_VO_UnknownSuboptions, arg); + exit_player(MSGTR_Exit_error); + } + } /* end while */ + } /* endif */ + + /* If pnm_outdir is not set by an option, resort to default of "." */ + if (!pnm_outdir) { + pnm_outdir = strdup("."); + if (!pnm_outdir) pnm_malloc_failed(); + } + + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s\n", info.short_name, + MSGTR_VO_SuboptionsParsedOK); + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/** \brief Create a directory. + * + * This function creates a directory. If it already exists, it tests if + * it's a directory and not something else, and if it is, it tests whether + * the directory is writable or not. + * + * \param buf Pointer to directory name. + * \param verbose Verbose on success. If verbose is non-zero, it will print + * a message if it was successful in creating the directory. + * + * \return nothing In case anything fails, the player will exit. If it + * returns, everything went well. + */ + +void pnm_mkdir(char *buf, int verbose) { + struct stat stat_p; + +/* Silly MING32 bug workaround */ +#ifndef __MINGW32__ + if ( mkdir(buf, 0755) < 0 ) { +#else + if ( mkdir(buf) < 0 ) { +#endif + switch (errno) { /* use switch in case other errors need to be caught + and handled in the future */ + case EEXIST: + if ( stat(buf, &stat_p ) < 0 ) { + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s: %s\n", info.short_name, + MSGTR_VO_GenericError, strerror(errno) ); + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s %s\n", info.short_name, + MSGTR_VO_UnableToAccess,buf); + exit_player(MSGTR_Exit_error); + } + if ( !S_ISDIR(stat_p.st_mode) ) { + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s %s\n", info.short_name, + buf, MSGTR_VO_ExistsButNoDirectory); + exit_player(MSGTR_Exit_error); + } + if ( !(stat_p.st_mode & S_IWUSR) ) { + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s - %s\n", info.short_name, + buf, MSGTR_VO_DirExistsButNotWritable); + exit_player(MSGTR_Exit_error); + } + + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s - %s\n", info.short_name, + buf, MSGTR_VO_DirExistsAndIsWritable); + break; + + default: + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s: %s\n", info.short_name, + MSGTR_VO_GenericError, strerror(errno) ); + mp_msg(MSGT_VO, MSGL_ERR, "%s: %s - %s\n", info.short_name, + buf, MSGTR_VO_CantCreateDirectory); + exit_player(MSGTR_Exit_error); + } /* end switch */ + } else if ( verbose ) { + mp_msg(MSGT_VO, MSGL_INFO, "%s: %s - %s\n", info.short_name, + buf, MSGTR_VO_DirectoryCreateSuccess); + } /* end if */ +} + +/* ------------------------------------------------------------------------- */ + +/** \brief Configure the video output driver. + * + * This functions configures the video output driver. It determines the + * width and height of the image(s) and creates the output directory. + * + * \return 0 All went well. + */ + +static uint32_t config(uint32_t width, uint32_t height, uint32_t d_width, + uint32_t d_height, uint32_t fullscreen, char *title, + uint32_t format) +{ + char buf[BUFLENGTH]; + + if (vo_config_count > 0 ) { /* Already configured */ + return 0; + } + + /* Create outdir. */ + + snprintf(buf, BUFLENGTH, "%s", pnm_outdir); + pnm_mkdir(buf, 1); /* This function only returns if creation was + successful. If not, the player will exit. */ + + if (pnm_type == PNM_TYPE_PPM) { + pnm_file_extension = strdup("ppm"); + } else if (pnm_type == PNM_TYPE_PGM) { + pnm_file_extension = strdup("pgm"); + } else if (pnm_type == PNM_TYPE_PGMYUV) { + pnm_file_extension = strdup("pgmyuv"); + } + if (!pnm_file_extension) pnm_malloc_failed(); + + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/** \brief Write PNM file to output file + * + * This function writes PPM, PGM or PGMYUV data to an output file, depending + * on which type was selected on the commandline. pnm_type and pnm_mode are + * global variables. Depending on which mode was selected, it will write + * a RAW or an ASCII file. + * + * \param outfile Filedescriptor of output file. + * \param mpi The image to write. + * + * \return none The player will exit if anything goes wrong. + */ + +void pnm_write_pnm(FILE *outfile, mp_image_t *mpi) +{ + uint32_t w = mpi->w; + uint32_t h = mpi->h; + uint8_t *rgbimage = mpi->planes[0]; + uint8_t *planeY = mpi->planes[0]; + uint8_t *planeU = mpi->planes[1]; + uint8_t *planeV = mpi->planes[2]; + uint8_t *curline; + uint32_t strideY = mpi->stride[0]; + uint32_t strideU = mpi->stride[1]; + uint32_t strideV = mpi->stride[2]; + + int i, j; + + if (pnm_mode == PNM_RAW_MODE) { + + if (pnm_type == PNM_TYPE_PPM) { + if ( fprintf(outfile, "P6\n%d %d\n255\n", w, h) < 0 ) + pnm_write_error(); + if ( fwrite(rgbimage, w * 3, h, outfile) < h ) pnm_write_error(); + } else if (pnm_type == PNM_TYPE_PGM) { + if ( fprintf(outfile, "P5\n%d %d\n255\n", w, h) < 0 ) + pnm_write_error(); + for (i=0; iflags & MP_IMGFLAG_PLANAR) { /* Planar */ + if (mpi->flags & MP_IMGFLAG_YUV) { /* Planar YUV */ + pnm_write_image(mpi); + return VO_TRUE; + } else { /* Planar RGB */ + return VO_FALSE; + } + } else { /* Packed */ + if (mpi->flags & MP_IMGFLAG_YUV) { /* Packed YUV */ + return VO_FALSE; + } else { /* Packed RGB */ + pnm_write_image(mpi); + return VO_TRUE; + } + } + + return VO_FALSE; +} + +/* ------------------------------------------------------------------------- */ + +static uint32_t draw_frame(uint8_t *src[]) +{ + mp_msg(MSGT_VO, MSGL_V, "%s: draw_frame() is called!\n", info.short_name); + return -1; +} + +/* ------------------------------------------------------------------------- */ + +static uint32_t draw_slice(uint8_t *src[], int stride[], int w, int h, + int x, int y) +{ + return 0; +} + +/* ------------------------------------------------------------------------- */ + +static uint32_t query_format(uint32_t format) +{ + /* Ensure that for PPM we get Packed RGB and for PGM(YUV) we get + * Planar YUV */ + if (pnm_type == PNM_TYPE_PPM) { + if (format == IMGFMT_RGB24) { + return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW; + } + } else if ( (pnm_type == PNM_TYPE_PGM) || (pnm_type == PNM_TYPE_PGMYUV) ) { + if (format == IMGFMT_YV12) { + return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW; + } + } + + return 0; +} + +/* ------------------------------------------------------------------------- */ + +static uint32_t control(uint32_t request, void *data, ...) +{ + switch (request) { + case VOCTRL_QUERY_FORMAT: + return query_format(*((uint32_t*)data)); + case VOCTRL_DRAW_IMAGE: + return draw_image(data); + } + return VO_NOTIMPL; +} + +/* ------------------------------------------------------------------------- */ + +static void uninit(void) +{ + if (pnm_subdirs) { + free(pnm_subdirs); + pnm_subdirs = NULL; + } + if (pnm_outdir) { + free(pnm_outdir); + pnm_outdir = NULL; + } +} + +/* ------------------------------------------------------------------------- */ + +static void check_events(void) +{ +} + +/* ------------------------------------------------------------------------- */ + +static void draw_osd(void) +{ +} + +/* ------------------------------------------------------------------------- */ + +static void flip_page (void) +{ +} + +/* ------------------------------------------------------------------------- */ + +#undef BUFLENGTH +#undef PNM_RAW_MODE +#undef PNM_ASCII_MODE +#undef PNM_TYPE_PPM +#undef PNM_TYPE_PGM +#undef PNM_TYPE_PGMYUV + +/* ------------------------------------------------------------------------- */ +