1 /*
2 * MNG file demuxer for MPlayer
3 *
4 * Copyright (C) 2008 Stefan Schuermans <stefan blinkenarea org>
5 *
6 * This file is part of MPlayer.
7 *
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.
12 *
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.
17 *
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.
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26
27 #include "config.h"
28
29 #include "mp_msg.h"
30 #include "help_mp.h"
31
32 #include "stream/stream.h"
33 #include "demuxer.h"
34 #include "stheader.h"
35
36 #define MNG_NO_INCLUDE_JNG
37 #define MNG_SUPPORT_READ
38 #define MNG_SUPPORT_DISPLAY
39 #include <libmng.h>
40
41 /**
42 * \brief some small fixed start time > 0
43 *
44 * Start time must be > 0 for the variable frame time mechanism
45 * (GIF, MATROSKA, MNG) in video.c to work for the first frame.
46 */
47 #define MNG_START_PTS 0.01f
48
49 /**
50 * \brief private context structure
51 *
52 * This structure is used as private data for MPlayer demuxer
53 * and also as private data for the MNG library.
54 *
55 * All members ending in \p _ms are in milliseconds
56 */
57 typedef struct {
58 stream_t * stream; ///< pointer to MNG data input stream
59 mng_handle h_mng; ///< MNG library image handle
60 int header_processed; ///< if MNG image header is processed
61 mng_uint32 width; ///< MNG image width
62 mng_uint32 height; ///< MNG image height
63 int total_time_ms; ///< total MNG animation time
64 unsigned char * canvas; /**< \brief canvas to draw the image onto
65 * \details
66 * \li lines top-down
67 * \li pixels left-to-right
68 * \li channels RGB
69 * \li no padding
70 * \li NULL if no canvas yet
71 */
72 int displaying; /**< \brief if displaying already,
73 * i.e. if mng_display has
74 * already been called
75 */
76 int finished; ///< if animation is finished
77 int global_time_ms; ///< current global time for MNG library
78 int anim_cur_time_ms; ///< current frame time in MNG animation
79 int anim_frame_duration_ms; ///< current frame duration in MNG animation
80 int show_cur_time_ms; /**< \brief current time in the show process,
81 * i.e. time of last demux packet
82 */
83 int show_next_time_ms; /**< \brief next time in the show process,
84 * i.e. time of next demux packet
85 */
86 int timer_ms; /**< \brief number of milliseconds after which
87 * libmng wants to be called again
88 */
89 } mng_priv_t;
90
91 /**
92 * \brief MNG library callback: Allocate a new zero-filled memory block.
93 * \param[in] size memory block size
94 * \return pointer to new memory block
95 */
demux_mng_alloc(mng_size_t size)96 static mng_ptr demux_mng_alloc(mng_size_t size)
97 {
98 return calloc(1, size);
99 }
100
101 /**
102 * \brief MNG library callback: Free memory block.
103 * \param[in] ptr pointer to memory block
104 * \param[in] size memory block size
105 */
demux_mng_free(mng_ptr ptr,mng_size_t size)106 static void demux_mng_free(mng_ptr ptr, mng_size_t size)
107 {
108 free(ptr);
109 }
110
111 /**
112 * \brief MNG library callback: Open MNG stream.
113 * \param[in] h_mng MNG library image handle
114 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
115 */
demux_mng_openstream(mng_handle h_mng)116 static mng_bool demux_mng_openstream(mng_handle h_mng)
117 {
118 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
119 stream_t * stream = mng_priv->stream;
120
121 // rewind stream to the beginning
122 stream_seek(stream, stream->start_pos);
123
124 return MNG_TRUE;
125 }
126
127 /**
128 * \brief MNG library callback: Close MNG stream.
129 * \param[in] h_mng MNG library image handle
130 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
131 */
demux_mng_closestream(mng_handle h_mng)132 static mng_bool demux_mng_closestream(mng_handle h_mng)
133 {
134 return MNG_TRUE;
135 }
136
137 /**
138 * \brief MNG library callback: Read data from stream.
139 * \param[in] h_mng MNG library image handle
140 * \param[in] buf pointer to buffer to fill with data
141 * \param[in] size size of buffer
142 * \param[out] read number of bytes read from stream
143 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
144 */
demux_mng_readdata(mng_handle h_mng,mng_ptr buf,mng_uint32 size,mng_uint32 * read)145 static mng_bool demux_mng_readdata(mng_handle h_mng, mng_ptr buf,
146 mng_uint32 size, mng_uint32 * read)
147 {
148 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
149 stream_t * stream = mng_priv->stream;
150
151 // simply read data from stream and return number of bytes or error
152 *read = stream_read(stream, buf, size);
153
154 return MNG_TRUE;
155 }
156
157 /**
158 * \brief MNG library callback: Header information is processed now.
159 * \param[in] h_mng MNG library image handle
160 * \param[in] width image width
161 * \param[in] height image height
162 * \return \p MNG_TRUE on success, \p MNG_FALSE on error
163 */
demux_mng_processheader(mng_handle h_mng,mng_uint32 width,mng_uint32 height)164 static mng_bool demux_mng_processheader(mng_handle h_mng, mng_uint32 width,
165 mng_uint32 height)
166 {
167 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
168
169 // remember size in private data
170 mng_priv->header_processed = 1;
171 mng_priv->width = width;
172 mng_priv->height = height;
173
174 // get total animation time
175 mng_priv->total_time_ms = mng_get_playtime(h_mng);
176
177 // allocate canvas
178 mng_priv->canvas = malloc(height * width * 4);
179 if (!mng_priv->canvas) {
180 mp_msg(MSGT_DEMUX, MSGL_ERR,
181 "demux_mng: could not allocate canvas of size %dx%d\n",
182 width, height);
183 return MNG_FALSE;
184 }
185
186 return MNG_TRUE;
187 }
188
189 /**
190 * \brief MNG library callback: Get access to a canvas line.
191 * \param[in] h_mng MNG library image handle
192 * \param[in] line y coordinate of line to access
193 * \return pointer to line on success, \p MNG_NULL on error
194 */
demux_mng_getcanvasline(mng_handle h_mng,mng_uint32 line)195 static mng_ptr demux_mng_getcanvasline(mng_handle h_mng, mng_uint32 line)
196 {
197 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
198
199 // return pointer to canvas line
200 if (line < mng_priv->height && mng_priv->canvas)
201 return (mng_ptr)(mng_priv->canvas + line * mng_priv->width * 4);
202 else
203 return (mng_ptr)MNG_NULL;
204 }
205
206 /**
207 * \brief MNG library callback: A part of the canvas should be shown.
208 *
209 * This function is called by libmng whenever it thinks a
210 * rectangular part of the display should be updated. This
211 * can happen multiple times for a frame and/or a single time
212 * for a frame. Only the the part of the display occupied by
213 * the rectangle defined by x, y, width, height is to be updated.
214 * It is possible that some parts of the display are not updated
215 * for many frames. There is no chance here to find out if the
216 * current frame is completed with this update or not.
217 *
218 * This mechanism does not match MPlayer's demuxer architecture,
219 * so it will not be used exactly as intended by libmng.
220 * A new frame is generated in the demux_mng_fill_buffer() function
221 * whenever libmng tells us to wait for some time.
222 *
223 * \param[in] h_mng MNG library image handle
224 * \param[in] x rectangle's left edge
225 * \param[in] y rectangle's top edge
226 * \param[in] width rectangle's width
227 * \param[in] height rectangle's heigt
228 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
229 */
demux_mng_refresh(mng_handle h_mng,mng_uint32 x,mng_uint32 y,mng_uint32 width,mng_uint32 height)230 static mng_bool demux_mng_refresh(mng_handle h_mng, mng_uint32 x, mng_uint32 y,
231 mng_uint32 width, mng_uint32 height)
232 {
233 // nothing to do here, the image data is already on the canvas
234 return MNG_TRUE;
235 }
236
237 /**
238 * \brief MNG library callback: Get how many milliseconds have passed.
239 * \param[in] h_mng MNG library image handle
240 * \return global time in milliseconds
241 */
demux_mng_gettickcount(mng_handle h_mng)242 static mng_uint32 demux_mng_gettickcount(mng_handle h_mng)
243 {
244 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
245
246 // return current global time
247 return mng_priv->global_time_ms;
248 }
249
250 /**
251 * \brief MNG library callback: Please call again after some milliseconds.
252 * \param[in] h_mng MNG library image handle
253 * \param[in] msecs number of milliseconds after which to call again
254 * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens)
255 */
demux_mng_settimer(mng_handle h_mng,mng_uint32 msecs)256 static mng_bool demux_mng_settimer(mng_handle h_mng, mng_uint32 msecs)
257 {
258 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
259
260 // Save number of milliseconds after which to call the MNG library again
261 // in private data.
262 mng_priv->timer_ms = msecs;
263 return MNG_TRUE;
264 }
265
266 /**
267 * \brief MPlayer callback: Check if stream contains MNG data.
268 * \param[in] demuxer demuxer structure
269 * \return demuxer type constant, \p 0 if unknown
270 */
demux_mng_check_file(demuxer_t * demuxer)271 static int demux_mng_check_file(demuxer_t *demuxer)
272 {
273 char buf[4];
274 if (stream_read(demuxer->stream, buf, 4) != 4)
275 return 0;
276 if (memcmp(buf, "\x8AMNG", 4))
277 return 0;
278 return DEMUXER_TYPE_MNG;
279 }
280
281 /**
282 * \brief MPlayer callback: Fill buffer from MNG stream.
283 * \param[in] demuxer demuxer structure
284 * \param[in] ds demuxer stream
285 * \return \p 1 on success, \p 0 on error
286 */
demux_mng_fill_buffer(demuxer_t * demuxer,demux_stream_t * ds)287 static int demux_mng_fill_buffer(demuxer_t * demuxer,
288 demux_stream_t * ds)
289 {
290 mng_priv_t * mng_priv = demuxer->priv;
291 mng_handle h_mng = mng_priv->h_mng;
292 mng_retcode mng_ret;
293 demux_packet_t * dp;
294
295 // exit if animation is finished
296 if (mng_priv->finished)
297 return 0;
298
299 // advance animation to requested next show time
300 while (mng_priv->anim_cur_time_ms + mng_priv->anim_frame_duration_ms
301 <= mng_priv->show_next_time_ms && !mng_priv->finished) {
302
303 // advance global and animation time
304 mng_priv->global_time_ms += mng_priv->anim_frame_duration_ms;
305 mng_priv->anim_cur_time_ms += mng_priv->anim_frame_duration_ms;
306
307 // Clear variable MNG library will write number of milliseconds to
308 // (via settimer callback).
309 mng_priv->timer_ms = 0;
310
311 // get next image from MNG library
312 if (mng_priv->displaying)
313 mng_ret = mng_display_resume(h_mng); // resume displaying MNG data
314 // to canvas
315 else
316 mng_ret = mng_display(h_mng); // start displaying MNG data to canvas
317 if (mng_ret && mng_ret != MNG_NEEDTIMERWAIT) {
318 mp_msg(MSGT_DEMUX, MSGL_ERR,
319 "demux_mng: could not display MNG data to canvas: "
320 "mng_retcode %d\n", mng_ret);
321 return 0;
322 }
323 mng_priv->displaying = 1; // mng_display() has been called now
324 mng_priv->finished = mng_ret == 0; // animation is finished iff
325 // mng_display() returned 0
326
327 // save current frame duration
328 mng_priv->anim_frame_duration_ms = mng_priv->timer_ms < 1
329 ? 1 : mng_priv->timer_ms;
330
331 } // while (mng_priv->anim_cur_time_ms + ...
332
333 // create a new demuxer packet
334 dp = new_demux_packet(mng_priv->height * mng_priv->width * 4);
335
336 // copy image data into demuxer packet
337 memcpy(dp->buffer, mng_priv->canvas,
338 mng_priv->height * mng_priv->width * 4);
339
340 // set current show time to requested show time
341 mng_priv->show_cur_time_ms = mng_priv->show_next_time_ms;
342
343 // get time of next frame to show
344 mng_priv->show_next_time_ms = mng_priv->anim_cur_time_ms
345 + mng_priv->anim_frame_duration_ms;
346
347 // Set position and timing information in demuxer video and demuxer packet.
348 // - Time must be time of next frame and always be > 0 for the variable
349 // frame time mechanism (GIF, MATROSKA, MNG) in video.c to work.
350 demuxer->video->dpos++;
351 dp->pts = (float)mng_priv->show_next_time_ms / 1000.0f + MNG_START_PTS;
352 dp->pos = stream_tell(demuxer->stream);
353 ds_add_packet(demuxer->video, dp);
354
355 return 1;
356 }
357
358 /**
359 * \brief MPlayer callback: Open MNG stream.
360 * \param[in] demuxer demuxer structure
361 * \return demuxer structure on success, \p NULL on error
362 */
demux_mng_open(demuxer_t * demuxer)363 static demuxer_t * demux_mng_open(demuxer_t * demuxer)
364 {
365 mng_priv_t * mng_priv;
366 mng_handle h_mng;
367 mng_retcode mng_ret;
368 sh_video_t * sh_video;
369
370 // create private data structure
371 mng_priv = calloc(1, sizeof(mng_priv_t));
372
373 //stream pointer into private data
374 mng_priv->stream = demuxer->stream;
375
376 // initialize MNG image instance
377 h_mng = mng_initialize((mng_ptr)mng_priv, demux_mng_alloc,
378 demux_mng_free, MNG_NULL);
379 if (!h_mng) {
380 mp_msg(MSGT_DEMUX, MSGL_ERR,
381 "demux_mng: could not initialize MNG image instance\n");
382 free(mng_priv);
383 return NULL;
384 }
385
386 // MNG image handle into private data
387 mng_priv->h_mng = h_mng;
388
389 // set required MNG callbacks
390 if (mng_setcb_openstream(h_mng, demux_mng_openstream) ||
391 mng_setcb_closestream(h_mng, demux_mng_closestream) ||
392 mng_setcb_readdata(h_mng, demux_mng_readdata) ||
393 mng_setcb_processheader(h_mng, demux_mng_processheader) ||
394 mng_setcb_getcanvasline(h_mng, demux_mng_getcanvasline) ||
395 mng_setcb_refresh(h_mng, demux_mng_refresh) ||
396 mng_setcb_gettickcount(h_mng, demux_mng_gettickcount) ||
397 mng_setcb_settimer(h_mng, demux_mng_settimer) ||
398 mng_set_canvasstyle(h_mng, MNG_CANVAS_RGBA8)) {
399 mp_msg(MSGT_DEMUX, MSGL_ERR,
400 "demux_mng: could not set MNG callbacks\n");
401 mng_cleanup(&h_mng);
402 free(mng_priv);
403 return NULL;
404 }
405
406 // start reading MNG data
407 mng_ret = mng_read(h_mng);
408 if (mng_ret) {
409 mp_msg(MSGT_DEMUX, MSGL_ERR,
410 "demux_mng: could not start reading MNG data: "
411 "mng_retcode %d\n", mng_ret);
412 mng_cleanup(&h_mng);
413 free(mng_priv);
414 return NULL;
415 }
416
417 // check that MNG header is processed now
418 if (!mng_priv->header_processed) {
419 mp_msg(MSGT_DEMUX, MSGL_ERR,
420 "demux_mng: internal error: header not processed\n");
421 mng_cleanup(&h_mng);
422 free(mng_priv);
423 return NULL;
424 }
425
426 // create a new video stream header
427 sh_video = new_sh_video(demuxer, 0);
428
429 // Make sure the demuxer knows about the new video stream header
430 // (even though new_sh_video() ought to take care of it).
431 // (Thanks to demux_gif.c for this.)
432 demuxer->video->id = 0;
433 demuxer->video->sh = sh_video;
434
435 // set format of pixels in video packets
436 sh_video->format = mmioFOURCC(32, 'B', 'G', 'R');
437
438 // set framerate to some value (MNG does not have a fixed framerate)
439 sh_video->fps = 5.0f;
440 sh_video->frametime = 1.0f / sh_video->fps;
441
442 // set video frame parameters
443 sh_video->bih = malloc(sizeof(*sh_video->bih));
444 sh_video->bih->biCompression = sh_video->format;
445 sh_video->bih->biWidth = mng_priv->width;
446 sh_video->bih->biHeight = mng_priv->height;
447 sh_video->bih->biBitCount = 32;
448 sh_video->bih->biPlanes = 1;
449
450 // Set start time to something > 0.
451 // - This is required for the variable frame time mechanism
452 // (GIF, MATROSKA, MNG) in video.c to work for the first frame.
453 sh_video->ds->pts = MNG_START_PTS;
454
455 // set private data in demuxer and return demuxer
456 demuxer->priv = mng_priv;
457 return demuxer;
458 }
459
460 /**
461 * \brief MPlayer callback: Close MNG stream.
462 * \param[in] demuxer demuxer structure
463 */
demux_mng_close(demuxer_t * demuxer)464 static void demux_mng_close(demuxer_t* demuxer)
465 {
466 mng_priv_t * mng_priv = demuxer->priv;
467
468 if (mng_priv) {
469
470 // shutdown MNG image instance
471 if (mng_priv->h_mng)
472 mng_cleanup(&mng_priv->h_mng);
473
474 // free private data
475 free(mng_priv->canvas);
476
477 free(mng_priv);
478 }
479 }
480
481 /**
482 * \brief MPlayer callback: Seek in MNG stream.
483 * \param[in] demuxer demuxer structure
484 * \param[in] rel_seek_secs relative seek time in seconds
485 * \param[in] audio_delay unused, MNG does not contain audio
486 * \param[in] flags bit flags, \p 1: absolute, \p 2: fractional position
487 */
demux_mng_seek(demuxer_t * demuxer,float rel_seek_secs,float audio_delay,int flags)488 static void demux_mng_seek(demuxer_t * demuxer, float rel_seek_secs,
489 float audio_delay, int flags)
490 {
491 mng_priv_t * mng_priv = demuxer->priv;
492 mng_handle h_mng = mng_priv->h_mng;
493 mng_retcode mng_ret;
494 int seek_ms, pos_ms;
495
496 // exit if not ready to seek (header not yet read or not yet displaying)
497 if (!mng_priv->header_processed || !mng_priv->displaying)
498 return;
499
500 // get number of milliseconds to seek to
501 if (flags & 2) // seek by fractional position (0.0 ... 1.0)
502 seek_ms = (int)(rel_seek_secs * (float)mng_priv->total_time_ms);
503 else // seek by time in seconds
504 seek_ms = (int)(rel_seek_secs * 1000.0f + 0.5f);
505
506 // get new position in milliseconds
507 if (flags & 1) // absolute
508 pos_ms = seek_ms;
509 else // relative
510 pos_ms = mng_priv->show_cur_time_ms + seek_ms;
511
512 // fix position
513 if (pos_ms < 0)
514 pos_ms = 0;
515 if (pos_ms > mng_priv->total_time_ms)
516 pos_ms = mng_priv->total_time_ms;
517
518 // FIXME
519 // In principle there is a function to seek in MNG: mng_display_gotime().
520 // - Using it did not work out (documentation is very brief,
521 // example code does not exist?).
522 // - The following code works, but its performance is quite bad.
523
524 // seeking forward
525 if (pos_ms >= mng_priv->show_cur_time_ms) {
526
527 // Simply advance show time to seek position.
528 // - Everything else will be handled in demux_mng_fill_buffer().
529 mng_priv->show_next_time_ms = pos_ms;
530
531 } // if (pos_ms > mng_priv->show_time_ms)
532
533 // seeking backward
534 else { // if (pos_ms > mng_priv->show_time_ms)
535
536 // Clear variable MNG library will write number of milliseconds to
537 // (via settimer callback).
538 mng_priv->timer_ms = 0;
539
540 // Restart displaying and advance show time to seek position.
541 // - Everything else will be handled in demux_mng_fill_buffer().
542 mng_ret = mng_display_reset(h_mng);
543 // If a timer wait is needed, fool libmng that requested time
544 // passed and try again.
545 if (mng_ret == MNG_NEEDTIMERWAIT) {
546 mng_priv->global_time_ms += mng_priv->timer_ms;
547 mng_ret = mng_display_reset(h_mng);
548 }
549 if (mng_ret) {
550 mp_msg(MSGT_DEMUX, MSGL_ERR,
551 "demux_mng: could not reset MNG display state: "
552 "mng_retcode %d\n", mng_ret);
553 return;
554 }
555 mng_priv->displaying = 0;
556 mng_priv->finished = 0;
557 mng_priv->anim_cur_time_ms = 0;
558 mng_priv->anim_frame_duration_ms = 0;
559 mng_priv->show_next_time_ms = pos_ms;
560
561 } // if (pos_ms > mng_priv->show_time_ms) ... else
562 }
563
564 /**
565 * \brief MPlayer callback: Control MNG stream.
566 * \param[in] demuxer demuxer structure
567 * \param[in] cmd code of control command to perform
568 * \param[in,out] arg command argument
569 * \return demuxer control response code
570 */
demux_mng_control(demuxer_t * demuxer,int cmd,void * arg)571 static int demux_mng_control(demuxer_t * demuxer, int cmd, void * arg)
572 {
573 mng_priv_t * mng_priv = demuxer->priv;
574
575 switch(cmd) {
576
577 // get total movie length
578 case DEMUXER_CTRL_GET_TIME_LENGTH:
579 if (mng_priv->header_processed) {
580 *(double *)arg = (double)mng_priv->total_time_ms / 1000.0;
581 return DEMUXER_CTRL_OK;
582 } else {
583 return DEMUXER_CTRL_DONTKNOW;
584 }
585 break;
586
587 // get position in movie
588 case DEMUXER_CTRL_GET_PERCENT_POS:
589 if (mng_priv->header_processed && mng_priv->total_time_ms > 0) {
590 *(int *)arg = (100 * mng_priv->show_cur_time_ms
591 + mng_priv->total_time_ms / 2)
592 / mng_priv->total_time_ms;
593 return DEMUXER_CTRL_OK;
594 } else {
595 return DEMUXER_CTRL_DONTKNOW;
596 }
597 break;
598
599 default:
600 return DEMUXER_CTRL_NOTIMPL;
601
602 } // switch (cmd)
603 }
604
605 const demuxer_desc_t demuxer_desc_mng = {
606 "MNG demuxer",
607 "mng",
608 "MNG",
609 "Stefan Schuermans <stefan@blinkenarea.org>",
610 "MNG files, using libmng",
611 DEMUXER_TYPE_MNG,
612 0, // unsafe autodetect (only checking magic at beginning of stream)
613 demux_mng_check_file,
614 demux_mng_fill_buffer,
615 demux_mng_open,
616 demux_mng_close,
617 demux_mng_seek,
618 demux_mng_control
619 };
620