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.
32 #include "stream/stream.h"
36 #define MNG_SUPPORT_READ
37 #define MNG_SUPPORT_DISPLAY
41 * \brief some small fixed start time > 0
43 * Start time must be > 0 for the variable frame time mechanism
44 * (GIF, MATROSKA, MNG) in video.c to work for the first frame.
46 #define MNG_START_PTS 0.01f
49 * \brief private context structure
51 * This structure is used as private data for MPlayer demuxer
52 * and also as private data for the MNG library.
54 * All members ending in \p _ms are in milliseconds
57 stream_t
* stream
; ///< pointer to MNG data input stream
58 mng_handle h_mng
; ///< MNG library image handle
59 int header_processed
; ///< if MNG image header is processed
60 mng_uint32 width
; ///< MNG image width
61 mng_uint32 height
; ///< MNG image height
62 int total_time_ms
; ///< total MNG animation time
63 unsigned char * canvas
; /**< \brief canvas to draw the image onto
66 * \li pixels left-to-right
69 * \li NULL if no canvas yet
71 int displaying
; /**< \brief if displaying already,
72 * i.e. if mng_display has
75 int finished
; ///< if animation is finished
76 int global_time_ms
; ///< current global time for MNG library
77 int anim_cur_time_ms
; ///< current frame time in MNG animation
78 int anim_frame_duration_ms
; ///< current frame duration in MNG animation
79 int show_cur_time_ms
; /**< \brief current time in the show process,
80 * i.e. time of last demux packet
82 int show_next_time_ms
; /**< \brief next time in the show process,
83 * i.e. time of next demux packet
85 int timer_ms
; /**< \brief number of milliseconds after which
86 * libmng wants to be called again
91 * \brief MNG library callback: Allocate a new zero-filled memory block.
92 * \param[in] size memory block size
93 * \return pointer to new memory block
95 static mng_ptr
demux_mng_alloc(mng_size_t size
)
97 return calloc(1, size
);
101 * \brief MNG library callback: Free memory block.
102 * \param[in] ptr pointer to memory block
103 * \param[in] size memory block size
105 static void demux_mng_free(mng_ptr ptr
, mng_size_t size
)
111 * \brief MNG library callback: Open MNG stream.
112 * \param[in] h_mng MNG library image handle
113 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
115 static mng_bool
demux_mng_openstream(mng_handle h_mng
)
117 mng_priv_t
* mng_priv
= mng_get_userdata(h_mng
);
118 stream_t
* stream
= mng_priv
->stream
;
120 // rewind stream to the beginning
121 stream_seek(stream
, stream
->start_pos
);
127 * \brief MNG library callback: Close MNG stream.
128 * \param[in] h_mng MNG library image handle
129 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
131 static mng_bool
demux_mng_closestream(mng_handle h_mng
)
137 * \brief MNG library callback: Read data from stream.
138 * \param[in] h_mng MNG library image handle
139 * \param[in] buf pointer to buffer to fill with data
140 * \param[in] size size of buffer
141 * \param[out] read number of bytes read from stream
142 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
144 static mng_bool
demux_mng_readdata(mng_handle h_mng
, mng_ptr buf
,
145 mng_uint32 size
, mng_uint32
* read
)
147 mng_priv_t
* mng_priv
= mng_get_userdata(h_mng
);
148 stream_t
* stream
= mng_priv
->stream
;
150 // simply read data from stream and return number of bytes or error
151 *read
= stream_read(stream
, buf
, size
);
157 * \brief MNG library callback: Header information is processed now.
158 * \param[in] h_mng MNG library image handle
159 * \param[in] width image width
160 * \param[in] height image height
161 * \return \p MNG_TRUE on success, \p MNG_FALSE on error
163 static mng_bool
demux_mng_processheader(mng_handle h_mng
, mng_uint32 width
,
166 mng_priv_t
* mng_priv
= mng_get_userdata(h_mng
);
168 // remember size in private data
169 mng_priv
->header_processed
= 1;
170 mng_priv
->width
= width
;
171 mng_priv
->height
= height
;
173 // get total animation time
174 mng_priv
->total_time_ms
= mng_get_playtime(h_mng
);
177 mng_priv
->canvas
= malloc(height
* width
* 4);
178 if (!mng_priv
->canvas
) {
179 mp_msg(MSGT_DEMUX
, MSGL_ERR
,
180 "demux_mng: could not allocate canvas of size %dx%d\n",
189 * \brief MNG library callback: Get access to a canvas line.
190 * \param[in] h_mng MNG library image handle
191 * \param[in] line y coordinate of line to access
192 * \return pointer to line on success, \p MNG_NULL on error
194 static mng_ptr
demux_mng_getcanvasline(mng_handle h_mng
, mng_uint32 line
)
196 mng_priv_t
* mng_priv
= mng_get_userdata(h_mng
);
198 // return pointer to canvas line
199 if (line
< mng_priv
->height
&& mng_priv
->canvas
)
200 return (mng_ptr
)(mng_priv
->canvas
+ line
* mng_priv
->width
* 4);
202 return (mng_ptr
)MNG_NULL
;
206 * \brief MNG library callback: A part of the canvas should be shown.
208 * This function is called by libmng whenever it thinks a
209 * rectangular part of the display should be updated. This
210 * can happen multiple times for a frame and/or a single time
211 * for a frame. Only the the part of the display occupied by
212 * the rectangle defined by x, y, width, height is to be updated.
213 * It is possible that some parts of the display are not updated
214 * for many frames. There is no chance here to find out if the
215 * current frame is completed with this update or not.
217 * This mechanism does not match MPlayer's demuxer architecture,
218 * so it will not be used exactly as intended by libmng.
219 * A new frame is generated in the demux_mng_fill_buffer() function
220 * whenever libmng tells us to wait for some time.
222 * \param[in] h_mng MNG library image handle
223 * \param[in] x rectangle's left edge
224 * \param[in] y rectangle's top edge
225 * \param[in] width rectangle's width
226 * \param[in] height rectangle's heigt
227 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
229 static mng_bool
demux_mng_refresh(mng_handle h_mng
, mng_uint32 x
, mng_uint32 y
,
230 mng_uint32 width
, mng_uint32 height
)
232 // nothing to do here, the image data is already on the canvas
237 * \brief MNG library callback: Get how many milliseconds have passed.
238 * \param[in] h_mng MNG library image handle
239 * \return global time in milliseconds
241 static mng_uint32
demux_mng_gettickcount(mng_handle h_mng
)
243 mng_priv_t
* mng_priv
= mng_get_userdata(h_mng
);
245 // return current global time
246 return mng_priv
->global_time_ms
;
250 * \brief MNG library callback: Please call again after some milliseconds.
251 * \param[in] h_mng MNG library image handle
252 * \param[in] msecs number of milliseconds after which to call again
253 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
255 static mng_bool
demux_mng_settimer(mng_handle h_mng
, mng_uint32 msecs
)
257 mng_priv_t
* mng_priv
= mng_get_userdata(h_mng
);
259 // Save number of milliseconds after which to call the MNG library again
261 mng_priv
->timer_ms
= msecs
;
266 * \brief MPlayer callback: Check if stream contains MNG data.
267 * \param[in] demuxer demuxer structure
268 * \return demuxer type constant, \p 0 if unknown
270 static int demux_mng_check_file(demuxer_t
*demuxer
)
273 if (stream_read(demuxer
->stream
, buf
, 4) != 4)
275 if (memcmp(buf
, "\x8AMNG", 4))
277 return DEMUXER_TYPE_MNG
;
281 * \brief MPlayer callback: Fill buffer from MNG stream.
282 * \param[in] demuxer demuxer structure
283 * \param[in] ds demuxer stream
284 * \return \p 1 on success, \p 0 on error
286 static int demux_mng_fill_buffer(demuxer_t
* demuxer
,
289 mng_priv_t
* mng_priv
= demuxer
->priv
;
290 mng_handle h_mng
= mng_priv
->h_mng
;
294 // exit if animation is finished
295 if (mng_priv
->finished
)
298 // advance animation to requested next show time
299 while (mng_priv
->anim_cur_time_ms
+ mng_priv
->anim_frame_duration_ms
300 <= mng_priv
->show_next_time_ms
&& !mng_priv
->finished
) {
302 // advance global and animation time
303 mng_priv
->global_time_ms
+= mng_priv
->anim_frame_duration_ms
;
304 mng_priv
->anim_cur_time_ms
+= mng_priv
->anim_frame_duration_ms
;
306 // Clear variable MNG library will write number of milliseconds to
307 // (via settimer callback).
308 mng_priv
->timer_ms
= 0;
310 // get next image from MNG library
311 if (mng_priv
->displaying
)
312 mng_ret
= mng_display_resume(h_mng
); // resume displaying MNG data
315 mng_ret
= mng_display(h_mng
); // start displaying MNG data to canvas
316 if (mng_ret
&& mng_ret
!= MNG_NEEDTIMERWAIT
) {
317 mp_msg(MSGT_DEMUX
, MSGL_ERR
,
318 "demux_mng: could not display MNG data to canvas: "
319 "mng_retcode %d\n", mng_ret
);
322 mng_priv
->displaying
= 1; // mng_display() has been called now
323 mng_priv
->finished
= mng_ret
== 0; // animation is finished iff
324 // mng_display() returned 0
326 // save current frame duration
327 mng_priv
->anim_frame_duration_ms
= mng_priv
->timer_ms
< 1
328 ? 1 : mng_priv
->timer_ms
;
330 } // while (mng_priv->anim_cur_time_ms + ...
332 // create a new demuxer packet
333 dp
= new_demux_packet(mng_priv
->height
* mng_priv
->width
* 4);
335 // copy image data into demuxer packet
336 memcpy(dp
->buffer
, mng_priv
->canvas
,
337 mng_priv
->height
* mng_priv
->width
* 4);
339 // set current show time to requested show time
340 mng_priv
->show_cur_time_ms
= mng_priv
->show_next_time_ms
;
342 // get time of next frame to show
343 mng_priv
->show_next_time_ms
= mng_priv
->anim_cur_time_ms
344 + mng_priv
->anim_frame_duration_ms
;
346 // Set position and timing information in demuxer video and demuxer packet.
347 // - Time must be time of next frame and always be > 0 for the variable
348 // frame time mechanism (GIF, MATROSKA, MNG) in video.c to work.
349 demuxer
->video
->dpos
++;
350 dp
->pts
= (float)mng_priv
->show_next_time_ms
/ 1000.0f
+ MNG_START_PTS
;
351 dp
->pos
= stream_tell(demuxer
->stream
);
352 ds_add_packet(demuxer
->video
, dp
);
358 * \brief MPlayer callback: Open MNG stream.
359 * \param[in] demuxer demuxer structure
360 * \return demuxer structure on success, \p NULL on error
362 static demuxer_t
* demux_mng_open(demuxer_t
* demuxer
)
364 mng_priv_t
* mng_priv
;
367 sh_video_t
* sh_video
;
369 // create private data structure
370 mng_priv
= calloc(1, sizeof(mng_priv_t
));
372 //stream pointer into private data
373 mng_priv
->stream
= demuxer
->stream
;
375 // initialize MNG image instance
376 h_mng
= mng_initialize((mng_ptr
)mng_priv
, demux_mng_alloc
,
377 demux_mng_free
, MNG_NULL
);
379 mp_msg(MSGT_DEMUX
, MSGL_ERR
,
380 "demux_mng: could not initialize MNG image instance\n");
385 // MNG image handle into private data
386 mng_priv
->h_mng
= h_mng
;
388 // set required MNG callbacks
389 if (mng_setcb_openstream(h_mng
, demux_mng_openstream
) ||
390 mng_setcb_closestream(h_mng
, demux_mng_closestream
) ||
391 mng_setcb_readdata(h_mng
, demux_mng_readdata
) ||
392 mng_setcb_processheader(h_mng
, demux_mng_processheader
) ||
393 mng_setcb_getcanvasline(h_mng
, demux_mng_getcanvasline
) ||
394 mng_setcb_refresh(h_mng
, demux_mng_refresh
) ||
395 mng_setcb_gettickcount(h_mng
, demux_mng_gettickcount
) ||
396 mng_setcb_settimer(h_mng
, demux_mng_settimer
) ||
397 mng_set_canvasstyle(h_mng
, MNG_CANVAS_RGBA8
)) {
398 mp_msg(MSGT_DEMUX
, MSGL_ERR
,
399 "demux_mng: could not set MNG callbacks\n");
405 // start reading MNG data
406 mng_ret
= mng_read(h_mng
);
408 mp_msg(MSGT_DEMUX
, MSGL_ERR
,
409 "demux_mng: could not start reading MNG data: "
410 "mng_retcode %d\n", mng_ret
);
416 // check that MNG header is processed now
417 if (!mng_priv
->header_processed
) {
418 mp_msg(MSGT_DEMUX
, MSGL_ERR
,
419 "demux_mng: internal error: header not processed\n");
425 // create a new video stream header
426 sh_video
= new_sh_video(demuxer
, 0);
428 // Make sure the demuxer knows about the new video stream header
429 // (even though new_sh_video() ought to take care of it).
430 // (Thanks to demux_gif.c for this.)
431 demuxer
->video
->sh
= sh_video
;
433 // Make sure that the video demuxer stream header knows about its
434 // parent video demuxer stream (this is getting wacky), or else
435 // video_read_properties() will choke.
436 // (Thanks to demux_gif.c for this.)
437 sh_video
->ds
= demuxer
->video
;
439 // set format of pixels in video packets
440 sh_video
->format
= mmioFOURCC(32, 'B', 'G', 'R');
442 // set framerate to some value (MNG does not have a fixed framerate)
443 sh_video
->fps
= 5.0f
;
444 sh_video
->frametime
= 1.0f
/ sh_video
->fps
;
446 // set video frame parameters
447 sh_video
->bih
= malloc(sizeof(BITMAPINFOHEADER
));
448 sh_video
->bih
->biCompression
= sh_video
->format
;
449 sh_video
->bih
->biWidth
= mng_priv
->width
;
450 sh_video
->bih
->biHeight
= mng_priv
->height
;
451 sh_video
->bih
->biBitCount
= 32;
452 sh_video
->bih
->biPlanes
= 1;
454 // Set start time to something > 0.
455 // - This is required for the variable frame time mechanism
456 // (GIF, MATROSKA, MNG) in video.c to work for the first frame.
457 sh_video
->ds
->pts
= MNG_START_PTS
;
459 // set private data in demuxer and return demuxer
460 demuxer
->priv
= mng_priv
;
465 * \brief MPlayer callback: Close MNG stream.
466 * \param[in] demuxer demuxer structure
468 static void demux_mng_close(demuxer_t
* demuxer
)
470 mng_priv_t
* mng_priv
= demuxer
->priv
;
474 // shutdown MNG image instance
476 mng_cleanup(&mng_priv
->h_mng
);
479 if (mng_priv
->canvas
)
480 free(mng_priv
->canvas
);
487 * \brief MPlayer callback: Seek in MNG stream.
488 * \param[in] demuxer demuxer structure
489 * \param[in] rel_seek_secs relative seek time in seconds
490 * \param[in] audio_delay unused, MNG does not contain audio
491 * \param[in] flags bit flags, \p 1: absolute, \p 2: fractional position
493 static void demux_mng_seek(demuxer_t
* demuxer
, float rel_seek_secs
,
494 float audio_delay
, int flags
)
496 mng_priv_t
* mng_priv
= demuxer
->priv
;
497 mng_handle h_mng
= mng_priv
->h_mng
;
501 // exit if not ready to seek (header not yet read or not yet displaying)
502 if (!mng_priv
->header_processed
|| !mng_priv
->displaying
)
505 // get number of milliseconds to seek to
506 if (flags
& 2) // seek by fractional position (0.0 ... 1.0)
507 seek_ms
= (int)(rel_seek_secs
* (float)mng_priv
->total_time_ms
);
508 else // seek by time in seconds
509 seek_ms
= (int)(rel_seek_secs
* 1000.0f
+ 0.5f
);
511 // get new position in milliseconds
512 if (flags
& 1) // absolute
515 pos_ms
= mng_priv
->show_cur_time_ms
+ seek_ms
;
520 if (pos_ms
> mng_priv
->total_time_ms
)
521 pos_ms
= mng_priv
->total_time_ms
;
524 // In principle there is a function to seek in MNG: mng_display_gotime().
525 // - Using it did not work out (documentation is very brief,
526 // example code does not exist?).
527 // - The following code works, but its performance is quite bad.
530 if (pos_ms
>= mng_priv
->show_cur_time_ms
) {
532 // Simply advance show time to seek position.
533 // - Everything else will be handled in demux_mng_fill_buffer().
534 mng_priv
->show_next_time_ms
= pos_ms
;
536 } // if (pos_ms > mng_priv->show_time_ms)
539 else { // if (pos_ms > mng_priv->show_time_ms)
541 // Clear variable MNG library will write number of milliseconds to
542 // (via settimer callback).
543 mng_priv
->timer_ms
= 0;
545 // Restart displaying and advance show time to seek position.
546 // - Everything else will be handled in demux_mng_fill_buffer().
547 mng_ret
= mng_display_reset(h_mng
);
548 // If a timer wait is needed, fool libmng that requested time
549 // passed and try again.
550 if (mng_ret
== MNG_NEEDTIMERWAIT
) {
551 mng_priv
->global_time_ms
+= mng_priv
->timer_ms
;
552 mng_ret
= mng_display_reset(h_mng
);
555 mp_msg(MSGT_DEMUX
, MSGL_ERR
,
556 "demux_mng: could not reset MNG display state: "
557 "mng_retcode %d\n", mng_ret
);
560 mng_priv
->displaying
= 0;
561 mng_priv
->finished
= 0;
562 mng_priv
->anim_cur_time_ms
= 0;
563 mng_priv
->anim_frame_duration_ms
= 0;
564 mng_priv
->show_next_time_ms
= pos_ms
;
566 } // if (pos_ms > mng_priv->show_time_ms) ... else
570 * \brief MPlayer callback: Control MNG stream.
571 * \param[in] demuxer demuxer structure
572 * \param[in] cmd code of control command to perform
573 * \param[in,out] arg command argument
574 * \return demuxer control response code
576 static int demux_mng_control(demuxer_t
* demuxer
, int cmd
, void * arg
)
578 mng_priv_t
* mng_priv
= demuxer
->priv
;
582 // get total movie length
583 case DEMUXER_CTRL_GET_TIME_LENGTH
:
584 if (mng_priv
->header_processed
) {
585 *(double *)arg
= (double)mng_priv
->total_time_ms
/ 1000.0;
586 return DEMUXER_CTRL_OK
;
588 return DEMUXER_CTRL_DONTKNOW
;
592 // get position in movie
593 case DEMUXER_CTRL_GET_PERCENT_POS
:
594 if (mng_priv
->header_processed
&& mng_priv
->total_time_ms
> 0) {
595 *(int *)arg
= (100 * mng_priv
->show_cur_time_ms
596 + mng_priv
->total_time_ms
/ 2)
597 / mng_priv
->total_time_ms
;
598 return DEMUXER_CTRL_OK
;
600 return DEMUXER_CTRL_DONTKNOW
;
605 return DEMUXER_CTRL_NOTIMPL
;
610 const demuxer_desc_t demuxer_desc_mng
= {
614 "Stefan Schuermans <stefan@blinkenarea.org>",
615 "MNG files, using libmng",
617 0, // unsafe autodetect (only checking magic at beginning of stream)
618 demux_mng_check_file
,
619 demux_mng_fill_buffer
,