manpage: Fix outdated -vo gl usage example.
[mplayer/glamo.git] / libmpdemux / demux_mng.c
blob719f11e35b1391cd5fd1dff5679f4b039a1ac269
1 /*
2 * MNG file demuxer for MPlayer
4 * Copyright (C) 2008 Stefan Schuermans <stefan blinkenarea org>
6 * This file is part of MPlayer.
8 * MPlayer is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * MPlayer is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
27 #include "config.h"
29 #include "mp_msg.h"
31 #include "stream/stream.h"
32 #include "demuxer.h"
33 #include "stheader.h"
35 #define MNG_SUPPORT_READ
36 #define MNG_SUPPORT_DISPLAY
37 #include <libmng.h>
39 /**
40 * \brief some small fixed start time > 0
42 * Start time must be > 0 for the variable frame time mechanism
43 * (GIF, MATROSKA, MNG) in video.c to work for the first frame.
45 #define MNG_START_PTS 0.01f
47 /**
48 * \brief private context structure
50 * This structure is used as private data for MPlayer demuxer
51 * and also as private data for the MNG library.
53 * All members ending in \p _ms are in milliseconds
55 typedef struct {
56 stream_t * stream; ///< pointer to MNG data input stream
57 mng_handle h_mng; ///< MNG library image handle
58 int header_processed; ///< if MNG image header is processed
59 mng_uint32 width; ///< MNG image width
60 mng_uint32 height; ///< MNG image height
61 int total_time_ms; ///< total MNG animation time
62 unsigned char * canvas; /**< \brief canvas to draw the image onto
63 * \details
64 * \li lines top-down
65 * \li pixels left-to-right
66 * \li channels RGB
67 * \li no padding
68 * \li NULL if no canvas yet
70 int displaying; /**< \brief if displaying already,
71 * i.e. if mng_display has
72 * already been called
74 int finished; ///< if animation is finished
75 int global_time_ms; ///< current global time for MNG library
76 int anim_cur_time_ms; ///< current frame time in MNG animation
77 int anim_frame_duration_ms; ///< current frame duration in MNG animation
78 int show_cur_time_ms; /**< \brief current time in the show process,
79 * i.e. time of last demux packet
81 int show_next_time_ms; /**< \brief next time in the show process,
82 * i.e. time of next demux packet
84 int timer_ms; /**< \brief number of milliseconds after which
85 * libmng wants to be called again
87 } mng_priv_t;
89 /**
90 * \brief MNG library callback: Allocate a new zero-filled memory block.
91 * \param[in] size memory block size
92 * \return pointer to new memory block
94 static mng_ptr demux_mng_alloc(mng_size_t size)
96 return calloc(1, size);
99 /**
100 * \brief MNG library callback: Free memory block.
101 * \param[in] ptr pointer to memory block
102 * \param[in] size memory block size
104 static void demux_mng_free(mng_ptr ptr, mng_size_t size)
106 free(ptr);
110 * \brief MNG library callback: Open MNG stream.
111 * \param[in] h_mng MNG library image handle
112 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
114 static mng_bool demux_mng_openstream(mng_handle h_mng)
116 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
117 stream_t * stream = mng_priv->stream;
119 // rewind stream to the beginning
120 stream_seek(stream, stream->start_pos);
122 return MNG_TRUE;
126 * \brief MNG library callback: Close MNG stream.
127 * \param[in] h_mng MNG library image handle
128 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
130 static mng_bool demux_mng_closestream(mng_handle h_mng)
132 return MNG_TRUE;
136 * \brief MNG library callback: Read data from stream.
137 * \param[in] h_mng MNG library image handle
138 * \param[in] buf pointer to buffer to fill with data
139 * \param[in] size size of buffer
140 * \param[out] read number of bytes read from stream
141 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
143 static mng_bool demux_mng_readdata(mng_handle h_mng, mng_ptr buf,
144 mng_uint32 size, mng_uint32 * read)
146 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
147 stream_t * stream = mng_priv->stream;
149 // simply read data from stream and return number of bytes or error
150 *read = stream_read(stream, buf, size);
152 return MNG_TRUE;
156 * \brief MNG library callback: Header information is processed now.
157 * \param[in] h_mng MNG library image handle
158 * \param[in] width image width
159 * \param[in] height image height
160 * \return \p MNG_TRUE on success, \p MNG_FALSE on error
162 static mng_bool demux_mng_processheader(mng_handle h_mng, mng_uint32 width,
163 mng_uint32 height)
165 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
167 // remember size in private data
168 mng_priv->header_processed = 1;
169 mng_priv->width = width;
170 mng_priv->height = height;
172 // get total animation time
173 mng_priv->total_time_ms = mng_get_playtime(h_mng);
175 // allocate canvas
176 mng_priv->canvas = malloc(height * width * 4);
177 if (!mng_priv->canvas) {
178 mp_msg(MSGT_DEMUX, MSGL_ERR,
179 "demux_mng: could not allocate canvas of size %dx%d\n",
180 width, height);
181 return MNG_FALSE;
184 return MNG_TRUE;
188 * \brief MNG library callback: Get access to a canvas line.
189 * \param[in] h_mng MNG library image handle
190 * \param[in] line y coordinate of line to access
191 * \return pointer to line on success, \p MNG_NULL on error
193 static mng_ptr demux_mng_getcanvasline(mng_handle h_mng, mng_uint32 line)
195 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
197 // return pointer to canvas line
198 if (line < mng_priv->height && mng_priv->canvas)
199 return (mng_ptr)(mng_priv->canvas + line * mng_priv->width * 4);
200 else
201 return (mng_ptr)MNG_NULL;
205 * \brief MNG library callback: A part of the canvas should be shown.
207 * This function is called by libmng whenever it thinks a
208 * rectangular part of the display should be updated. This
209 * can happen multiple times for a frame and/or a single time
210 * for a frame. Only the the part of the display occupied by
211 * the rectangle defined by x, y, width, height is to be updated.
212 * It is possible that some parts of the display are not updated
213 * for many frames. There is no chance here to find out if the
214 * current frame is completed with this update or not.
216 * This mechanism does not match MPlayer's demuxer architecture,
217 * so it will not be used exactly as intended by libmng.
218 * A new frame is generated in the demux_mng_fill_buffer() function
219 * whenever libmng tells us to wait for some time.
221 * \param[in] h_mng MNG library image handle
222 * \param[in] x rectangle's left edge
223 * \param[in] y rectangle's top edge
224 * \param[in] width rectangle's width
225 * \param[in] height rectangle's heigt
226 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
228 static mng_bool demux_mng_refresh(mng_handle h_mng, mng_uint32 x, mng_uint32 y,
229 mng_uint32 width, mng_uint32 height)
231 // nothing to do here, the image data is already on the canvas
232 return MNG_TRUE;
236 * \brief MNG library callback: Get how many milliseconds have passed.
237 * \param[in] h_mng MNG library image handle
238 * \return global time in milliseconds
240 static mng_uint32 demux_mng_gettickcount(mng_handle h_mng)
242 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
244 // return current global time
245 return mng_priv->global_time_ms;
249 * \brief MNG library callback: Please call again after some milliseconds.
250 * \param[in] h_mng MNG library image handle
251 * \param[in] msecs number of milliseconds after which to call again
252 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
254 static mng_bool demux_mng_settimer(mng_handle h_mng, mng_uint32 msecs)
256 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
258 // Save number of milliseconds after which to call the MNG library again
259 // in private data.
260 mng_priv->timer_ms = msecs;
261 return MNG_TRUE;
265 * \brief MPlayer callback: Check if stream contains MNG data.
266 * \param[in] demuxer demuxer structure
267 * \return demuxer type constant, \p 0 if unknown
269 static int demux_mng_check_file(demuxer_t *demuxer)
271 char buf[4];
272 if (stream_read(demuxer->stream, buf, 4) != 4)
273 return 0;
274 if (memcmp(buf, "\x8AMNG", 4))
275 return 0;
276 return DEMUXER_TYPE_MNG;
280 * \brief MPlayer callback: Fill buffer from MNG stream.
281 * \param[in] demuxer demuxer structure
282 * \param[in] ds demuxer stream
283 * \return \p 1 on success, \p 0 on error
285 static int demux_mng_fill_buffer(demuxer_t * demuxer,
286 demux_stream_t * ds)
288 mng_priv_t * mng_priv = demuxer->priv;
289 mng_handle h_mng = mng_priv->h_mng;
290 mng_retcode mng_ret;
291 demux_packet_t * dp;
293 // exit if animation is finished
294 if (mng_priv->finished)
295 return 0;
297 // advance animation to requested next show time
298 while (mng_priv->anim_cur_time_ms + mng_priv->anim_frame_duration_ms
299 <= mng_priv->show_next_time_ms && !mng_priv->finished) {
301 // advance global and animation time
302 mng_priv->global_time_ms += mng_priv->anim_frame_duration_ms;
303 mng_priv->anim_cur_time_ms += mng_priv->anim_frame_duration_ms;
305 // Clear variable MNG library will write number of milliseconds to
306 // (via settimer callback).
307 mng_priv->timer_ms = 0;
309 // get next image from MNG library
310 if (mng_priv->displaying)
311 mng_ret = mng_display_resume(h_mng); // resume displaying MNG data
312 // to canvas
313 else
314 mng_ret = mng_display(h_mng); // start displaying MNG data to canvas
315 if (mng_ret && mng_ret != MNG_NEEDTIMERWAIT) {
316 mp_msg(MSGT_DEMUX, MSGL_ERR,
317 "demux_mng: could not display MNG data to canvas: "
318 "mng_retcode %d\n", mng_ret);
319 return 0;
321 mng_priv->displaying = 1; // mng_display() has been called now
322 mng_priv->finished = mng_ret == 0; // animation is finished iff
323 // mng_display() returned 0
325 // save current frame duration
326 mng_priv->anim_frame_duration_ms = mng_priv->timer_ms < 1
327 ? 1 : mng_priv->timer_ms;
329 } // while (mng_priv->anim_cur_time_ms + ...
331 // create a new demuxer packet
332 dp = new_demux_packet(mng_priv->height * mng_priv->width * 4);
334 // copy image data into demuxer packet
335 memcpy(dp->buffer, mng_priv->canvas,
336 mng_priv->height * mng_priv->width * 4);
338 // set current show time to requested show time
339 mng_priv->show_cur_time_ms = mng_priv->show_next_time_ms;
341 // get time of next frame to show
342 mng_priv->show_next_time_ms = mng_priv->anim_cur_time_ms
343 + mng_priv->anim_frame_duration_ms;
345 // Set position and timing information in demuxer video and demuxer packet.
346 // - Time must be time of next frame and always be > 0 for the variable
347 // frame time mechanism (GIF, MATROSKA, MNG) in video.c to work.
348 demuxer->video->dpos++;
349 dp->pts = (float)mng_priv->show_next_time_ms / 1000.0f + MNG_START_PTS;
350 dp->pos = stream_tell(demuxer->stream);
351 ds_add_packet(demuxer->video, dp);
353 return 1;
357 * \brief MPlayer callback: Open MNG stream.
358 * \param[in] demuxer demuxer structure
359 * \return demuxer structure on success, \p NULL on error
361 static demuxer_t * demux_mng_open(demuxer_t * demuxer)
363 mng_priv_t * mng_priv;
364 mng_handle h_mng;
365 mng_retcode mng_ret;
366 sh_video_t * sh_video;
368 // create private data structure
369 mng_priv = calloc(1, sizeof(mng_priv_t));
371 //stream pointer into private data
372 mng_priv->stream = demuxer->stream;
374 // initialize MNG image instance
375 h_mng = mng_initialize((mng_ptr)mng_priv, demux_mng_alloc,
376 demux_mng_free, MNG_NULL);
377 if (!h_mng) {
378 mp_msg(MSGT_DEMUX, MSGL_ERR,
379 "demux_mng: could not initialize MNG image instance\n");
380 free(mng_priv);
381 return NULL;
384 // MNG image handle into private data
385 mng_priv->h_mng = h_mng;
387 // set required MNG callbacks
388 if (mng_setcb_openstream(h_mng, demux_mng_openstream) ||
389 mng_setcb_closestream(h_mng, demux_mng_closestream) ||
390 mng_setcb_readdata(h_mng, demux_mng_readdata) ||
391 mng_setcb_processheader(h_mng, demux_mng_processheader) ||
392 mng_setcb_getcanvasline(h_mng, demux_mng_getcanvasline) ||
393 mng_setcb_refresh(h_mng, demux_mng_refresh) ||
394 mng_setcb_gettickcount(h_mng, demux_mng_gettickcount) ||
395 mng_setcb_settimer(h_mng, demux_mng_settimer) ||
396 mng_set_canvasstyle(h_mng, MNG_CANVAS_RGBA8)) {
397 mp_msg(MSGT_DEMUX, MSGL_ERR,
398 "demux_mng: could not set MNG callbacks\n");
399 mng_cleanup(&h_mng);
400 free(mng_priv);
401 return NULL;
404 // start reading MNG data
405 mng_ret = mng_read(h_mng);
406 if (mng_ret) {
407 mp_msg(MSGT_DEMUX, MSGL_ERR,
408 "demux_mng: could not start reading MNG data: "
409 "mng_retcode %d\n", mng_ret);
410 mng_cleanup(&h_mng);
411 free(mng_priv);
412 return NULL;
415 // check that MNG header is processed now
416 if (!mng_priv->header_processed) {
417 mp_msg(MSGT_DEMUX, MSGL_ERR,
418 "demux_mng: internal error: header not processed\n");
419 mng_cleanup(&h_mng);
420 free(mng_priv);
421 return NULL;
424 // create a new video stream header
425 sh_video = new_sh_video(demuxer, 0);
427 // Make sure the demuxer knows about the new video stream header
428 // (even though new_sh_video() ought to take care of it).
429 // (Thanks to demux_gif.c for this.)
430 demuxer->video->sh = sh_video;
432 // Make sure that the video demuxer stream header knows about its
433 // parent video demuxer stream (this is getting wacky), or else
434 // video_read_properties() will choke.
435 // (Thanks to demux_gif.c for this.)
436 sh_video->ds = demuxer->video;
438 // set format of pixels in video packets
439 sh_video->format = mmioFOURCC(32, 'B', 'G', 'R');
441 // set framerate to some value (MNG does not have a fixed framerate)
442 sh_video->fps = 5.0f;
443 sh_video->frametime = 1.0f / sh_video->fps;
445 // set video frame parameters
446 sh_video->bih = malloc(sizeof(BITMAPINFOHEADER));
447 sh_video->bih->biCompression = sh_video->format;
448 sh_video->bih->biWidth = mng_priv->width;
449 sh_video->bih->biHeight = mng_priv->height;
450 sh_video->bih->biBitCount = 32;
451 sh_video->bih->biPlanes = 1;
453 // Set start time to something > 0.
454 // - This is required for the variable frame time mechanism
455 // (GIF, MATROSKA, MNG) in video.c to work for the first frame.
456 sh_video->ds->pts = MNG_START_PTS;
458 // set private data in demuxer and return demuxer
459 demuxer->priv = mng_priv;
460 return demuxer;
464 * \brief MPlayer callback: Close MNG stream.
465 * \param[in] demuxer demuxer structure
467 static void demux_mng_close(demuxer_t* demuxer)
469 mng_priv_t * mng_priv = demuxer->priv;
471 if (mng_priv) {
473 // shutdown MNG image instance
474 if (mng_priv->h_mng)
475 mng_cleanup(&mng_priv->h_mng);
477 // free private data
478 if (mng_priv->canvas)
479 free(mng_priv->canvas);
481 free(mng_priv);
486 * \brief MPlayer callback: Seek in MNG stream.
487 * \param[in] demuxer demuxer structure
488 * \param[in] rel_seek_secs relative seek time in seconds
489 * \param[in] audio_delay unused, MNG does not contain audio
490 * \param[in] flags bit flags, \p 1: absolute, \p 2: fractional position
492 static void demux_mng_seek(demuxer_t * demuxer, float rel_seek_secs,
493 float audio_delay, int flags)
495 mng_priv_t * mng_priv = demuxer->priv;
496 mng_handle h_mng = mng_priv->h_mng;
497 mng_retcode mng_ret;
498 int seek_ms, pos_ms;
500 // exit if not ready to seek (header not yet read or not yet displaying)
501 if (!mng_priv->header_processed || !mng_priv->displaying)
502 return;
504 // get number of milliseconds to seek to
505 if (flags & 2) // seek by fractional position (0.0 ... 1.0)
506 seek_ms = (int)(rel_seek_secs * (float)mng_priv->total_time_ms);
507 else // seek by time in seconds
508 seek_ms = (int)(rel_seek_secs * 1000.0f + 0.5f);
510 // get new position in milliseconds
511 if (flags & 1) // absolute
512 pos_ms = seek_ms;
513 else // relative
514 pos_ms = mng_priv->show_cur_time_ms + seek_ms;
516 // fix position
517 if (pos_ms < 0)
518 pos_ms = 0;
519 if (pos_ms > mng_priv->total_time_ms)
520 pos_ms = mng_priv->total_time_ms;
522 // FIXME
523 // In principle there is a function to seek in MNG: mng_display_gotime().
524 // - Using it did not work out (documentation is very brief,
525 // example code does not exist?).
526 // - The following code works, but its performance is quite bad.
528 // seeking forward
529 if (pos_ms >= mng_priv->show_cur_time_ms) {
531 // Simply advance show time to seek position.
532 // - Everything else will be handled in demux_mng_fill_buffer().
533 mng_priv->show_next_time_ms = pos_ms;
535 } // if (pos_ms > mng_priv->show_time_ms)
537 // seeking backward
538 else { // if (pos_ms > mng_priv->show_time_ms)
540 // Clear variable MNG library will write number of milliseconds to
541 // (via settimer callback).
542 mng_priv->timer_ms = 0;
544 // Restart displaying and advance show time to seek position.
545 // - Everything else will be handled in demux_mng_fill_buffer().
546 mng_ret = mng_display_reset(h_mng);
547 // If a timer wait is needed, fool libmng that requested time
548 // passed and try again.
549 if (mng_ret == MNG_NEEDTIMERWAIT) {
550 mng_priv->global_time_ms += mng_priv->timer_ms;
551 mng_ret = mng_display_reset(h_mng);
553 if (mng_ret) {
554 mp_msg(MSGT_DEMUX, MSGL_ERR,
555 "demux_mng: could not reset MNG display state: "
556 "mng_retcode %d\n", mng_ret);
557 return;
559 mng_priv->displaying = 0;
560 mng_priv->finished = 0;
561 mng_priv->anim_cur_time_ms = 0;
562 mng_priv->anim_frame_duration_ms = 0;
563 mng_priv->show_next_time_ms = pos_ms;
565 } // if (pos_ms > mng_priv->show_time_ms) ... else
569 * \brief MPlayer callback: Control MNG stream.
570 * \param[in] demuxer demuxer structure
571 * \param[in] cmd code of control command to perform
572 * \param[in,out] arg command argument
573 * \return demuxer control response code
575 static int demux_mng_control(demuxer_t * demuxer, int cmd, void * arg)
577 mng_priv_t * mng_priv = demuxer->priv;
579 switch(cmd) {
581 // get total movie length
582 case DEMUXER_CTRL_GET_TIME_LENGTH:
583 if (mng_priv->header_processed) {
584 *(double *)arg = (double)mng_priv->total_time_ms / 1000.0;
585 return DEMUXER_CTRL_OK;
586 } else {
587 return DEMUXER_CTRL_DONTKNOW;
589 break;
591 // get position in movie
592 case DEMUXER_CTRL_GET_PERCENT_POS:
593 if (mng_priv->header_processed && mng_priv->total_time_ms > 0) {
594 *(int *)arg = (100 * mng_priv->show_cur_time_ms
595 + mng_priv->total_time_ms / 2)
596 / mng_priv->total_time_ms;
597 return DEMUXER_CTRL_OK;
598 } else {
599 return DEMUXER_CTRL_DONTKNOW;
601 break;
603 default:
604 return DEMUXER_CTRL_NOTIMPL;
606 } // switch (cmd)
609 const demuxer_desc_t demuxer_desc_mng = {
610 "MNG demuxer",
611 "mng",
612 "MNG",
613 "Stefan Schuermans <stefan@blinkenarea.org>",
614 "MNG files, using libmng",
615 DEMUXER_TYPE_MNG,
616 0, // unsafe autodetect (only checking magic at beginning of stream)
617 demux_mng_check_file,
618 demux_mng_fill_buffer,
619 demux_mng_open,
620 demux_mng_close,
621 demux_mng_seek,
622 demux_mng_control