[MPlayer-cvslog] r33477 - in trunk: Makefile configure libvo/video_out.c libvo/vo_mng.c
cboesch
subversion at mplayerhq.hu
Sat May 21 17:04:50 CEST 2011
Author: cboesch
Date: Sat May 21 17:04:50 2011
New Revision: 33477
Log:
Add MNG output support.
Patch by Stefan Schuermans, stefan blinkenarea dot org.
Added:
trunk/libvo/vo_mng.c
Modified:
trunk/Makefile
trunk/configure
trunk/libvo/video_out.c
Modified: trunk/Makefile
==============================================================================
--- trunk/Makefile Sat May 21 16:26:27 2011 (r33476)
+++ trunk/Makefile Sat May 21 17:04:50 2011 (r33477)
@@ -575,6 +575,7 @@ SRCS_MPLAYER-$(LIBMENU_DVBIN) += libmenu
SRCS_MPLAYER-$(LIRC) += input/lirc.c
SRCS_MPLAYER-$(MD5SUM) += libvo/vo_md5sum.c
SRCS_MPLAYER-$(MGA) += libvo/vo_mga.c
+SRCS_MPLAYER-$(MNG) += libvo/vo_mng.c
SRCS_MPLAYER-$(NAS) += libao2/ao_nas.c
SRCS_MPLAYER-$(NETWORKING) += udp_sync.c
SRCS_MPLAYER-$(OPENAL) += libao2/ao_openal.c
Modified: trunk/configure
==============================================================================
--- trunk/configure Sat May 21 16:26:27 2011 (r33476)
+++ trunk/configure Sat May 21 17:04:50 2011 (r33477)
@@ -380,7 +380,7 @@ Optional features:
Codecs:
--enable-gif enable GIF support [autodetect]
--enable-png enable PNG input/output support [autodetect]
- --enable-mng enable MNG input support [autodetect]
+ --enable-mng enable MNG input/output support [autodetect]
--enable-jpeg enable JPEG input/output support [autodetect]
--enable-libcdio enable libcdio support [autodetect]
--enable-liblzo enable liblzo support [autodetect]
@@ -4859,8 +4859,10 @@ echores "$_mng"
if test "$_mng" = yes ; then
def_mng='#define CONFIG_MNG 1'
extra_ldflags="$extra_ldflags -lmng -lz"
+ vomodules="mng $vomodules"
else
def_mng='#undef CONFIG_MNG'
+ novomodules="mng $novomodules"
fi
echocheck "JPEG support"
Modified: trunk/libvo/video_out.c
==============================================================================
--- trunk/libvo/video_out.c Sat May 21 16:26:27 2011 (r33476)
+++ trunk/libvo/video_out.c Sat May 21 17:04:50 2011 (r33477)
@@ -135,6 +135,7 @@ extern const vo_functions_t video_out_co
extern const vo_functions_t video_out_quartz;
extern const vo_functions_t video_out_pnm;
extern const vo_functions_t video_out_md5sum;
+extern const vo_functions_t video_out_mng;
/* The following declarations are _not_ const because functions pointers
* get overloaded during (re)initialization. */
@@ -291,6 +292,9 @@ const vo_functions_t* const video_out_dr
#ifdef CONFIG_MD5SUM
&video_out_md5sum,
#endif
+#ifdef CONFIG_MNG
+ &video_out_mng,
+#endif
NULL
};
Added: trunk/libvo/vo_mng.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/libvo/vo_mng.c Sat May 21 17:04:50 2011 (r33477)
@@ -0,0 +1,620 @@
+/*
+ * MPlayer output to MNG file
+ *
+ * Copyright (C) 2011 Stefan Schuermans <stefan blinkenarea org>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer 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.
+ *
+ * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <zlib.h>
+
+#define MNG_INCLUDE_WRITE_PROCS
+#define MNG_ACCESS_CHUNKS
+#define MNG_SUPPORT_READ
+#define MNG_SUPPORT_DISPLAY
+#define MNG_SUPPORT_WRITE
+#include <libmng.h>
+
+#include "video_out.h"
+#include "video_out_internal.h"
+#include "mp_msg.h"
+
+#define VOMNG_DEFAULT_DELAY_MS (100) /* default delay of a frame */
+
+static vo_info_t info = {
+ .name = "MNG file",
+ .short_name = "mng",
+ .author = "Stefan Schuermans <stefan blinkenarea org>"
+};
+
+LIBVO_EXTERN(mng)
+
+/* a frame to be written to the MNG file */
+struct vomng_frame {
+ mng_ptr data; /**< deflate compressed data, malloc-ed */
+ mng_uint32 len; /**< length of compressed data */
+ unsigned int time_ms; /**< timestamp of frame (in ms) */
+ struct vomng_frame *next; /**< next frame */
+};
+
+/* properties of MNG output */
+struct vomng_properties {
+ char *out_file_name; /**< name of output file, malloc-ed */
+ unsigned int width, height; /**< dimensions */
+ unsigned char *canvas; /**< canvas for frame,
+ canvas := row ... row,
+ row := filter_id pix ... pix,
+ pix := red green blue */
+ struct vomng_frame *frame_first; /**< list of frames */
+ struct vomng_frame *frame_last;
+ int is_init; /**< if initialized */
+};
+
+/* private data of MNG vo module */
+static struct vomng_properties vomng;
+
+/**
+ * @brief libmng callback: allocate memory
+ * @param[in] size size of requested memory block
+ * @return pointer to memory block, which is initialized to zero
+ */
+static mng_ptr vomng_alloc(mng_size_t size)
+{
+ return calloc(1, size);
+}
+
+/**
+ * @brief libmng callback: free memory
+ * @param[in] pointer to memory block
+ * @param[in] size size of requested memory block
+ */
+static void vomng_free(mng_ptr ptr, mng_size_t size)
+{
+ free(ptr);
+}
+
+/**
+ * @brief libmng callback: open stream
+ * @param[in] mng libmng handle
+ * @return if stream could be opened
+ */
+static mng_bool vomng_openstream(mng_handle mng)
+{
+ return MNG_TRUE; /* stream is always open wen we get here,
+ tell libmng that everything is okay */
+}
+
+/**
+ * @brief libmng callback: stream should be closed
+ * @param[in] mng libmng handle
+ * @return if stream could be closed
+ */
+static mng_bool vomng_closestream(mng_handle mng)
+{
+ return MNG_TRUE; /* stream will be closed later,
+ tell libmng that everything is okay */
+}
+
+/**
+ * @brief libmng callback: write libmng data to the open stream
+ * @param[in] mng libmng handle
+ * @param[in] *buf pointer to data to write
+ * @param[in] size size of data to write
+ * @param[out] *written size of data written
+ * @return if data was written successfully
+ */
+static mng_bool vomng_writedata(mng_handle mng, mng_ptr buf,
+ mng_uint32 size, mng_uint32 *written)
+{
+ FILE *file = mng_get_userdata(mng);
+ *written = fwrite(buf, 1, size, file);
+ /* according to the example in libmng documentation, true is always
+ returned here, short writes can be detected by libmng via *written */
+ return MNG_TRUE;
+}
+
+/**
+ * @brief compress frame data
+ * @param[in] width width of canvas
+ * @param[in] height height of canvas
+ * @param[in] *canvas data on canvas (including MNG filter IDs)
+ * @param[out] *out_ptr pointer to compressed data, malloc-ed
+ * @param[out] *out_len length of compressed data
+ */
+static void vomng_canvas_to_compressed(unsigned int width, unsigned int height,
+ const unsigned char *canvas,
+ mng_ptr *out_ptr, mng_uint32 *out_len)
+{
+ mng_uint32 raw_len;
+ unsigned char *ptr;
+ unsigned long len;
+
+ /* default: no output */
+ *out_ptr = NULL;
+ *out_len = 0;
+
+ /* raw_len := length of input data
+ - it will be significantly shorter than 32 bit
+ - the "1 +" is needed because each row starts with the filter ID */
+ raw_len = height * (1 + width * 3);
+
+ /* compress data
+ - compress2 output size will be smaller than raw_len * 1.001 + 12 (see
+ man page), so calculate the next larger integer value in len and
+ allocate abuffer of this size
+ - len will still contain a value shorter than 32 bit */
+ len = raw_len + (raw_len + 999) / 1000 + 12;
+ ptr = malloc(len);
+ if (!ptr)
+ return;
+ compress2(ptr, &len, canvas, raw_len, Z_BEST_COMPRESSION);
+
+ /* return allocated compressed data
+ - we have to convert the output length to a shorter data type as
+ libmng does not accept an unsigned long as length
+ - convert here, because we can see here that the conversion is safe
+ - see comments about raw_len and len above
+ - compress2 never increases value of len */
+ *out_ptr = ptr;
+ *out_len = len;
+}
+
+/**
+ * @brief write frame to MNG file
+ * @param[in] *frame the frame to write to MNG file
+ * @param[in] mng libmng handle
+ * @param[in] width width of canvas
+ * @param[in] height height of canvas
+ * @param[in] first_frame if the frame is the first one in the file
+ * @return 0 on success, 1 on error
+ */
+static int vomng_write_frame(struct vomng_frame *frame, mng_handle mng,
+ unsigned int width, unsigned int height,
+ int first_frame)
+{
+ unsigned int delay_ms;
+
+ /* determine delay */
+ if (frame->next)
+ delay_ms = frame->next->time_ms - frame->time_ms;
+ else
+ delay_ms = VOMNG_DEFAULT_DELAY_MS; /* default delay for last frame */
+
+ /* write frame headers to MNG file */
+ if (mng_putchunk_seek(mng, 0, MNG_NULL)) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing SEEK chunk failed\n");
+ return 1;
+ }
+ if (mng_putchunk_fram(mng, MNG_FALSE,
+ /* keep canvas if not 1st frame */
+ first_frame ? MNG_FRAMINGMODE_1
+ : MNG_FRAMINGMODE_NOCHANGE,
+ 0, MNG_NULL, /* no frame name */
+ MNG_CHANGEDELAY_DEFAULT, /* change only delay */
+ MNG_CHANGETIMOUT_NO,
+ MNG_CHANGECLIPPING_NO,
+ MNG_CHANGESYNCID_NO,
+ delay_ms, /* new delay */
+ 0, /* no new timeout */
+ 0, 0, 0, 0, 0, /* no new boundary */
+ 0, 0)) { /* no count, no IDs */
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing FRAM chunk failed\n");
+ return 1;
+ }
+ if (mng_putchunk_defi(mng, 0, /* no ID */
+ MNG_DONOTSHOW_VISIBLE,
+ MNG_ABSTRACT,
+ MNG_TRUE, 0, 0, /* top left location */
+ MNG_FALSE, 0, 0, 0, 0)) { /* no clipping */
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing DEFI chunk failed\n");
+ return 1;
+ }
+ if (mng_putchunk_ihdr(mng, width, height, /* dimensions */
+ 8, MNG_COLORTYPE_RGB, /* RBG */
+ MNG_COMPRESSION_DEFLATE,
+ MNG_FILTER_ADAPTIVE,
+ MNG_INTERLACE_NONE)) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing IHDR chunk failed\n");
+ return 1;
+ }
+
+ /* write frame data */
+ if (mng_putchunk_idat(mng, frame->len, frame->data)) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing IDAT chunk failed\n");
+ return 1;
+ }
+
+ /* write frame footers to MNG file */
+ if (mng_putchunk_iend(mng)) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing IEND chunk failed\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief write buffered frames to MNG file
+ * @return 0 on success, 1 on error
+ */
+static int vomng_write_file(void)
+{
+ FILE *file;
+ mng_handle mng;
+ struct vomng_frame *frame;
+ unsigned int frames, duration_ms;
+ int first;
+
+ /* refuse to create empty MNG file */
+ if (!vomng.frame_first || !vomng.frame_last) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: not creating empty file\n");
+ return 1;
+ }
+
+ /* create output file */
+ file = fopen(vomng.out_file_name, "wb");
+ if (!file) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "vomng: could not open output file \"%s\": %s\n",
+ vomng.out_file_name, strerror(errno));
+ return 1;
+ }
+
+ /* inititalize MNG library */
+ mng = mng_initialize(file, vomng_alloc, vomng_free, MNG_NULL);
+ if (!mng) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: could not initialize libmng\n");
+ fclose(file);
+ return 1;
+ }
+ if (mng_setcb_openstream (mng, vomng_openstream ) ||
+ mng_setcb_closestream(mng, vomng_closestream) ||
+ mng_setcb_writedata (mng, vomng_writedata )) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: cannot set callbacks for libmng\n");
+ mng_cleanup(&mng);
+ fclose(file);
+ return 1;
+ }
+
+ /* create new MNG image in memory */
+ if (mng_create(mng)) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: cannot create MNG image in memory\n");
+ mng_cleanup(&mng);
+ fclose(file);
+ return 1;
+ }
+
+ /* determine number of frames and total duration */
+ frames = 0;
+ for (frame = vomng.frame_first; frame; frame = frame->next)
+ frames++;
+ duration_ms = vomng.frame_last->time_ms - vomng.frame_first->time_ms;
+
+ /* write MNG header chunks */
+ if (mng_putchunk_mhdr(mng,
+ vomng.width, /* dimensions */
+ vomng.height,
+ 1000, 0, /* ticks per second, layer */
+ frames, /* number of frames */
+ duration_ms, /* total duration */
+ MNG_SIMPLICITY_VALID |
+ MNG_SIMPLICITY_SIMPLEFEATURES |
+ MNG_SIMPLICITY_COMPLEXFEATURES) ||
+ mng_putchunk_save(mng,
+ MNG_TRUE, 0, 0) || /* empty save chunk */
+ mng_putchunk_term(mng,
+ MNG_TERMACTION_CLEAR, /* show last frame forever */
+ MNG_ITERACTION_CLEAR,
+ 0, 0)) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "vomng: writing MHDR/SAVE/TERM chunks failed\n");
+ mng_write(mng); /* write out buffered chunks before cleanup */
+ mng_cleanup(&mng);
+ fclose(file);
+ return 1;
+ }
+
+ /* write frames */
+ first = 1;
+ for (frame = vomng.frame_first; frame; frame = frame->next) {
+ if (vomng_write_frame(frame, mng, vomng.width, vomng.height, first))
+ break;
+ first = 0;
+ }
+ if (frame) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing frames failed\n");
+ mng_write(mng); /* write out buffered chunks before cleanup */
+ mng_cleanup(&mng);
+ fclose(file);
+ return 1;
+ }
+
+ /* write MNG end chunk */
+ if (mng_putchunk_mend(mng)) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing end chunk failed\n");
+ mng_write(mng); /* write out buffered chunks before cleanup */
+ mng_cleanup(&mng);
+ fclose(file);
+ return 1;
+ }
+
+ /* finish and cleanup */
+ mng_write(mng); /* write out buffered chunks before cleanup */
+ mng_cleanup(&mng);
+ fclose(file);
+
+ return 0;
+}
+
+/**
+ * @brief close all files and free all memory of MNG vo module
+ */
+static void vomng_prop_reset(void)
+{
+ struct vomng_frame *frame, *next;
+
+ /* we are initialized properly */
+ if (vomng.is_init) {
+ /* write buffered frames to MNG file */
+ if (vomng_write_file())
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "vomng: writing output file failed\n");
+ }
+
+ /* reset state */
+ vomng.is_init = 0;
+ if (vomng.frame_first) {
+ frame = vomng.frame_first;
+ while (frame) {
+ next = frame->next;
+ free(frame->data);
+ free(frame);
+ frame = next;
+ }
+ vomng.frame_first = NULL;
+ vomng.frame_last = NULL;
+ }
+ free(vomng.canvas);
+ vomng.canvas = NULL;
+ vomng.width = 0;
+ vomng.height = 0;
+}
+
+/**
+ * @brief close files, free memory and delete private data of MNG von module
+ */
+static void vomng_prop_cleanup(void)
+{
+ vomng_prop_reset();
+ free(vomng.out_file_name);
+}
+
+/**
+ * @brief configure MNG vo module
+ * @param[in] width video width
+ * @param[in] height video height
+ * @param[in] d_width (unused)
+ * @param[in] d_height (unused)
+ * @param[in] flags (unused)
+ * @param[in] title (unused)
+ * @param[in] format video frame format
+ * @return 0 on success, 1 on error
+ */
+static int config(uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height,
+ uint32_t flags, char *title, uint32_t format)
+{
+ uint32_t row_stride, y;
+
+ /* reset state */
+ vomng_prop_reset();
+
+ /* check format */
+ if (format != IMGFMT_RGB24) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "vomng: config with invalid format (!= IMGFMT_RGB24)\n");
+ return 1;
+ }
+
+ /* allocate canvas */
+ vomng.width = width;
+ vomng.height = height;
+ row_stride = 1 + width * 3; /* rows contain filter IDs */
+ vomng.canvas = calloc(height * row_stride, 1);
+ if (!vomng.canvas) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: out of memory\n");
+ return 1;
+ }
+ /* fill in filter IDs for rows */
+ for (y = 0; y < height; y++)
+ *(vomng.canvas + row_stride * y) = MNG_FILTER_NONE;
+
+ /* we are initialized */
+ vomng.is_init = 1;
+
+ return 0;
+}
+
+/**
+ * @brief draw on screen display (unsupported for MNG vo module)
+ */
+static void draw_osd(void)
+{
+}
+
+/**
+ * @brief display data currently on canvas
+ */
+static void flip_page(void)
+{
+ unsigned int last_ms;
+ struct vomng_frame *frame;
+
+ /* get time of last frame in ms
+ (intensive testing showed that the time obtained from vo_pts
+ is the time of the previous frame) */
+ last_ms = (unsigned int)(vo_pts / 90.0 + 0.5);
+
+ /* set time of last frame */
+ if (vomng.frame_last)
+ vomng.frame_last->time_ms = last_ms;
+
+ /* create new frame */
+ frame = calloc(1, sizeof(*frame));
+ if (!frame) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: out of memory\n");
+ return;
+ }
+ /* time of frame is not yet known (see comment about vo_pts about 20
+ lines above), approximate time using time of last frame and the
+ default frame delay */
+ frame->time_ms = last_ms + VOMNG_DEFAULT_DELAY_MS;
+ frame->next = NULL;
+
+ /* compress canvas data */
+ vomng_canvas_to_compressed(vomng.width, vomng.height, vomng.canvas,
+ &frame->data, &frame->len);
+ if (!frame->data) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: compressing frame failed\n");
+ free(frame);
+ return;
+ }
+
+ /* add frame to list */
+ if (!vomng.frame_first || !vomng.frame_last) {
+ vomng.frame_first = frame;
+ vomng.frame_last = frame;
+ } else {
+ vomng.frame_last->next = frame;
+ vomng.frame_last = frame;
+ }
+}
+
+/**
+ * @brief put frame data onto canvas (not supported)
+ * @return always 1 to indicate error
+ */
+static int draw_frame(uint8_t *src[])
+{
+ /* draw_frame() not supported
+ * VFCAP_ACCEPT_STRIDE is set for format
+ * so draw_slice() will be called instead of this function */
+ return 1;
+}
+
+/**
+ * @brief deinitialize MNG vo module
+ */
+static void uninit(void)
+{
+ vomng_prop_cleanup();
+}
+
+/**
+ * @brief deal with events (not supported)
+ */
+static void check_events(void)
+{
+}
+
+/**
+ * @brief put a slice of frame data onto canvas
+ * @param[in] srcimg pointer to data
+ * @param[in] stride line stride in data
+ * @param[in] wf frame slice width
+ * @param[in] hf frame slice height
+ * @param[in] xf leftmost x coordinate of frame slice
+ * @param[in] yf topmost y coordinate of frame slice
+ * @return always 0 to indicate success
+ */
+static int draw_slice(uint8_t *srcimg[], int stride[],
+ int wf, int hf, int xf, int yf)
+{
+ uint8_t *line_ptr;
+ int line_len, row_stride, y;
+
+ /* put pixel data from slice to canvas */
+ line_ptr = srcimg[0];
+ line_len = stride[0];
+ row_stride = 1 + vomng.width * 3; /* rows contain filter IDs */
+ for (y = 0; y < hf; y++)
+ memcpy(vomng.canvas + (yf + y) * row_stride + 1 + xf * 3,
+ line_ptr + y * line_len, wf * 3);
+
+ return 0;
+}
+
+/**
+ * @brief pre-initialize MNG vo module
+ * @param[in] *arg arguments passed to MNG vo module (output file name)
+ * @return 0 on success, 1 on error
+ */
+static int preinit(const char *arg)
+{
+ /* get name of output file */
+ if (!arg || !*arg) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: MNG output file must be given,"
+ " example: -vo mng:output.mng\n");
+ vomng_prop_cleanup();
+ return 1;
+ }
+ vomng.out_file_name = strdup(arg);
+ if (!vomng.out_file_name) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vomng: out of memory\n");
+ vomng_prop_cleanup();
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief get supported formats
+ * @param[in] format format to check support for
+ * @return acceptance flags
+ */
+static int query_format(uint32_t format)
+{
+ if (format == IMGFMT_RGB24)
+ return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW |
+ VFCAP_ACCEPT_STRIDE;
+ return 0;
+}
+
+/**
+ * @brief handle control stuff
+ * @param[in] request control request
+ * @param[in] *data data (dependent on control request)
+ * @return response to control request
+ */
+static int control(uint32_t request, void *data)
+{
+ switch (request) {
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(*((uint32_t *)data));
+ }
+ return VO_NOTIMPL;
+}
+
More information about the MPlayer-cvslog
mailing list