28018
|
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_SUPPORT_READ
|
|
37 #define MNG_SUPPORT_DISPLAY
|
|
38 #include <libmng.h>
|
|
39
|
|
40 /**
|
|
41 * \brief some small fixed start time > 0
|
|
42 *
|
|
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.
|
|
45 */
|
|
46 #define MNG_START_PTS 0.01f
|
|
47
|
|
48 /**
|
|
49 * \brief private context structure
|
|
50 *
|
|
51 * This structure is used as private data for MPlayer demuxer
|
|
52 * and also as private data for the MNG library.
|
|
53 *
|
|
54 * All members ending in \p _ms are in milliseconds
|
|
55 */
|
|
56 typedef struct {
|
|
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
|
|
64 * \details
|
|
65 * \li lines top-down
|
|
66 * \li pixels left-to-right
|
|
67 * \li channels RGB
|
|
68 * \li no padding
|
|
69 * \li NULL if no canvas yet
|
|
70 */
|
|
71 int displaying; /**< \brief if displaying already,
|
|
72 * i.e. if mng_display has
|
|
73 * already been called
|
|
74 */
|
|
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
|
|
81 */
|
|
82 int show_next_time_ms; /**< \brief next time in the show process,
|
|
83 * i.e. time of next demux packet
|
|
84 */
|
|
85 int timer_ms; /**< \brief number of milliseconds after which
|
|
86 * libmng wants to be called again
|
|
87 */
|
|
88 } mng_priv_t;
|
|
89
|
|
90 /**
|
|
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
|
|
94 */
|
|
95 static mng_ptr demux_mng_alloc(mng_size_t size)
|
|
96 {
|
|
97 return calloc(1, size);
|
|
98 }
|
|
99
|
|
100 /**
|
|
101 * \brief MNG library callback: Free memory block.
|
|
102 * \param[in] ptr pointer to memory block
|
|
103 * \param[in] size memory block size
|
|
104 */
|
|
105 static void demux_mng_free(mng_ptr ptr, mng_size_t size)
|
|
106 {
|
|
107 free(ptr);
|
|
108 }
|
|
109
|
|
110 /**
|
|
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)
|
|
114 */
|
|
115 static mng_bool demux_mng_openstream(mng_handle h_mng)
|
|
116 {
|
|
117 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
|
|
118 stream_t * stream = mng_priv->stream;
|
|
119
|
|
120 // rewind stream to the beginning
|
|
121 stream_seek(stream, stream->start_pos);
|
|
122
|
|
123 return MNG_TRUE;
|
|
124 }
|
|
125
|
|
126 /**
|
|
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)
|
|
130 */
|
|
131 static mng_bool demux_mng_closestream(mng_handle h_mng)
|
|
132 {
|
|
133 return MNG_TRUE;
|
|
134 }
|
|
135
|
|
136 /**
|
|
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)
|
|
143 */
|
|
144 static mng_bool demux_mng_readdata(mng_handle h_mng, mng_ptr buf,
|
|
145 mng_uint32 size, mng_uint32 * read)
|
|
146 {
|
|
147 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
|
|
148 stream_t * stream = mng_priv->stream;
|
|
149
|
|
150 // simply read data from stream and return number of bytes or error
|
|
151 *read = stream_read(stream, buf, size);
|
|
152
|
|
153 return MNG_TRUE;
|
|
154 }
|
|
155
|
|
156 /**
|
|
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
|
|
162 */
|
|
163 static mng_bool demux_mng_processheader(mng_handle h_mng, mng_uint32 width,
|
|
164 mng_uint32 height)
|
|
165 {
|
|
166 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
|
|
167
|
|
168 // remember size in private data
|
|
169 mng_priv->header_processed = 1;
|
|
170 mng_priv->width = width;
|
|
171 mng_priv->height = height;
|
|
172
|
|
173 // get total animation time
|
|
174 mng_priv->total_time_ms = mng_get_playtime(h_mng);
|
|
175
|
|
176 // allocate canvas
|
|
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",
|
|
181 width, height);
|
|
182 return MNG_FALSE;
|
|
183 }
|
|
184
|
|
185 return MNG_TRUE;
|
|
186 }
|
|
187
|
|
188 /**
|
|
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
|
|
193 */
|
|
194 static mng_ptr demux_mng_getcanvasline(mng_handle h_mng, mng_uint32 line)
|
|
195 {
|
|
196 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
|
|
197
|
|
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);
|
|
201 else
|
|
202 return (mng_ptr)MNG_NULL;
|
|
203 }
|
|
204
|
|
205 /**
|
|
206 * \brief MNG library callback: A part of the canvas should be shown.
|
|
207 *
|
|
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.
|
|
216 *
|
|
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.
|
|
221 *
|
|
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)
|
|
228 */
|
|
229 static mng_bool demux_mng_refresh(mng_handle h_mng, mng_uint32 x, mng_uint32 y,
|
|
230 mng_uint32 width, mng_uint32 height)
|
|
231 {
|
|
232 // nothing to do here, the image data is already on the canvas
|
|
233 return MNG_TRUE;
|
|
234 }
|
|
235
|
|
236 /**
|
|
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
|
|
240 */
|
|
241 static mng_uint32 demux_mng_gettickcount(mng_handle h_mng)
|
|
242 {
|
|
243 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
|
|
244
|
|
245 // return current global time
|
|
246 return mng_priv->global_time_ms;
|
|
247 }
|
|
248
|
|
249 /**
|
|
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)
|
|
254 */
|
|
255 static mng_bool demux_mng_settimer(mng_handle h_mng, mng_uint32 msecs)
|
|
256 {
|
|
257 mng_priv_t * mng_priv = mng_get_userdata(h_mng);
|
|
258
|
|
259 // Save number of milliseconds after which to call the MNG library again
|
|
260 // in private data.
|
|
261 mng_priv->timer_ms = msecs;
|
|
262 return MNG_TRUE;
|
|
263 }
|
|
264
|
|
265 /**
|
|
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
|
|
269 */
|
|
270 static int demux_mng_check_file(demuxer_t *demuxer)
|
|
271 {
|
|
272 char buf[4];
|
|
273 if (stream_read(demuxer->stream, buf, 4) != 4)
|
|
274 return 0;
|
|
275 if (memcmp(buf, "\x8AMNG", 4))
|
|
276 return 0;
|
|
277 return DEMUXER_TYPE_MNG;
|
|
278 }
|
|
279
|
|
280 /**
|
|
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
|
|
285 */
|
|
286 static int demux_mng_fill_buffer(demuxer_t * demuxer,
|
|
287 demux_stream_t * ds)
|
|
288 {
|
|
289 mng_priv_t * mng_priv = demuxer->priv;
|
|
290 mng_handle h_mng = mng_priv->h_mng;
|
|
291 mng_retcode mng_ret;
|
|
292 demux_packet_t * dp;
|
|
293
|
|
294 // exit if animation is finished
|
|
295 if (mng_priv->finished)
|
|
296 return 0;
|
|
297
|
|
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) {
|
|
301
|
|
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;
|
|
305
|
|
306 // Clear variable MNG library will write number of milliseconds to
|
|
307 // (via settimer callback).
|
|
308 mng_priv->timer_ms = 0;
|
|
309
|
|
310 // get next image from MNG library
|
|
311 if (mng_priv->displaying)
|
|
312 mng_ret = mng_display_resume(h_mng); // resume displaying MNG data
|
|
313 // to canvas
|
|
314 else
|
|
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);
|
|
320 return 0;
|
|
321 }
|
|
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
|
|
325
|
|
326 // save current frame duration
|
|
327 mng_priv->anim_frame_duration_ms = mng_priv->timer_ms < 1
|
|
328 ? 1 : mng_priv->timer_ms;
|
|
329
|
|
330 } // while (mng_priv->anim_cur_time_ms + ...
|
|
331
|
|
332 // create a new demuxer packet
|
|
333 dp = new_demux_packet(mng_priv->height * mng_priv->width * 4);
|
|
334
|
|
335 // copy image data into demuxer packet
|
|
336 memcpy(dp->buffer, mng_priv->canvas,
|
|
337 mng_priv->height * mng_priv->width * 4);
|
|
338
|
|
339 // set current show time to requested show time
|
|
340 mng_priv->show_cur_time_ms = mng_priv->show_next_time_ms;
|
|
341
|
|
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;
|
|
345
|
|
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);
|
|
353
|
|
354 return 1;
|
|
355 }
|
|
356
|
|
357 /**
|
|
358 * \brief MPlayer callback: Open MNG stream.
|
|
359 * \param[in] demuxer demuxer structure
|
|
360 * \return demuxer structure on success, \p NULL on error
|
|
361 */
|
|
362 static demuxer_t * demux_mng_open(demuxer_t * demuxer)
|
|
363 {
|
|
364 mng_priv_t * mng_priv;
|
|
365 mng_handle h_mng;
|
|
366 mng_retcode mng_ret;
|
|
367 sh_video_t * sh_video;
|
|
368
|
|
369 // create private data structure
|
|
370 mng_priv = calloc(1, sizeof(mng_priv_t));
|
|
371
|
|
372 //stream pointer into private data
|
|
373 mng_priv->stream = demuxer->stream;
|
|
374
|
|
375 // initialize MNG image instance
|
|
376 h_mng = mng_initialize((mng_ptr)mng_priv, demux_mng_alloc,
|
|
377 demux_mng_free, MNG_NULL);
|
|
378 if (!h_mng) {
|
|
379 mp_msg(MSGT_DEMUX, MSGL_ERR,
|
|
380 "demux_mng: could not initialize MNG image instance\n");
|
|
381 free(mng_priv);
|
|
382 return NULL;
|
|
383 }
|
|
384
|
|
385 // MNG image handle into private data
|
|
386 mng_priv->h_mng = h_mng;
|
|
387
|
|
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");
|
|
400 mng_cleanup(&h_mng);
|
|
401 free(mng_priv);
|
|
402 return NULL;
|
|
403 }
|
|
404
|
|
405 // start reading MNG data
|
|
406 mng_ret = mng_read(h_mng);
|
|
407 if (mng_ret) {
|
|
408 mp_msg(MSGT_DEMUX, MSGL_ERR,
|
|
409 "demux_mng: could not start reading MNG data: "
|
|
410 "mng_retcode %d\n", mng_ret);
|
|
411 mng_cleanup(&h_mng);
|
|
412 free(mng_priv);
|
|
413 return NULL;
|
|
414 }
|
|
415
|
|
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");
|
|
420 mng_cleanup(&h_mng);
|
|
421 free(mng_priv);
|
|
422 return NULL;
|
|
423 }
|
|
424
|
|
425 // create a new video stream header
|
|
426 sh_video = new_sh_video(demuxer, 0);
|
|
427
|
|
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;
|
|
432
|
|
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;
|
|
438
|
|
439 // set format of pixels in video packets
|
|
440 sh_video->format = mmioFOURCC(32, 'B', 'G', 'R');
|
|
441
|
|
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;
|
|
445
|
|
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;
|
|
453
|
|
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;
|
|
458
|
|
459 // set private data in demuxer and return demuxer
|
|
460 demuxer->priv = mng_priv;
|
|
461 return demuxer;
|
|
462 }
|
|
463
|
|
464 /**
|
|
465 * \brief MPlayer callback: Close MNG stream.
|
|
466 * \param[in] demuxer demuxer structure
|
|
467 */
|
|
468 static void demux_mng_close(demuxer_t* demuxer)
|
|
469 {
|
|
470 mng_priv_t * mng_priv = demuxer->priv;
|
|
471
|
|
472 if (mng_priv) {
|
|
473
|
|
474 // shutdown MNG image instance
|
|
475 if (mng_priv->h_mng)
|
|
476 mng_cleanup(&mng_priv->h_mng);
|
|
477
|
|
478 // free private data
|
|
479 if (mng_priv->canvas)
|
|
480 free(mng_priv->canvas);
|
|
481
|
|
482 free(mng_priv);
|
|
483 }
|
|
484 }
|
|
485
|
|
486 /**
|
|
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
|
|
492 */
|
|
493 static void demux_mng_seek(demuxer_t * demuxer, float rel_seek_secs,
|
|
494 float audio_delay, int flags)
|
|
495 {
|
|
496 mng_priv_t * mng_priv = demuxer->priv;
|
|
497 mng_handle h_mng = mng_priv->h_mng;
|
|
498 mng_retcode mng_ret;
|
|
499 int seek_ms, pos_ms;
|
|
500
|
|
501 // exit if not ready to seek (header not yet read or not yet displaying)
|
|
502 if (!mng_priv->header_processed || !mng_priv->displaying)
|
|
503 return;
|
|
504
|
|
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);
|
|
510
|
|
511 // get new position in milliseconds
|
|
512 if (flags & 1) // absolute
|
|
513 pos_ms = seek_ms;
|
|
514 else // relative
|
|
515 pos_ms = mng_priv->show_cur_time_ms + seek_ms;
|
|
516
|
|
517 // fix position
|
|
518 if (pos_ms < 0)
|
|
519 pos_ms = 0;
|
|
520 if (pos_ms > mng_priv->total_time_ms)
|
|
521 pos_ms = mng_priv->total_time_ms;
|
|
522
|
|
523 // FIXME
|
|
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.
|
|
528
|
|
529 // seeking forward
|
|
530 if (pos_ms >= mng_priv->show_cur_time_ms) {
|
|
531
|
|
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;
|
|
535
|
|
536 } // if (pos_ms > mng_priv->show_time_ms)
|
|
537
|
|
538 // seeking backward
|
|
539 else { // if (pos_ms > mng_priv->show_time_ms)
|
|
540
|
|
541 // Clear variable MNG library will write number of milliseconds to
|
|
542 // (via settimer callback).
|
|
543 mng_priv->timer_ms = 0;
|
|
544
|
|
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);
|
|
553 }
|
|
554 if (mng_ret) {
|
|
555 mp_msg(MSGT_DEMUX, MSGL_ERR,
|
|
556 "demux_mng: could not reset MNG display state: "
|
|
557 "mng_retcode %d\n", mng_ret);
|
|
558 return;
|
|
559 }
|
|
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;
|
|
565
|
|
566 } // if (pos_ms > mng_priv->show_time_ms) ... else
|
|
567 }
|
|
568
|
|
569 /**
|
|
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
|
|
575 */
|
|
576 static int demux_mng_control(demuxer_t * demuxer, int cmd, void * arg)
|
|
577 {
|
|
578 mng_priv_t * mng_priv = demuxer->priv;
|
|
579
|
|
580 switch(cmd) {
|
|
581
|
|
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;
|
|
587 } else {
|
|
588 return DEMUXER_CTRL_DONTKNOW;
|
|
589 }
|
|
590 break;
|
|
591
|
|
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;
|
|
599 } else {
|
|
600 return DEMUXER_CTRL_DONTKNOW;
|
|
601 }
|
|
602 break;
|
|
603
|
|
604 default:
|
|
605 return DEMUXER_CTRL_NOTIMPL;
|
|
606
|
|
607 } // switch (cmd)
|
|
608 }
|
|
609
|
|
610 const demuxer_desc_t demuxer_desc_mng = {
|
|
611 "MNG demuxer",
|
|
612 "mng",
|
|
613 "MNG",
|
|
614 "Stefan Schuermans <stefan@blinkenarea.org>",
|
|
615 "MNG files, using libmng",
|
|
616 DEMUXER_TYPE_MNG,
|
|
617 0, // unsafe autodetect (only checking magic at beginning of stream)
|
|
618 demux_mng_check_file,
|
|
619 demux_mng_fill_buffer,
|
|
620 demux_mng_open,
|
|
621 demux_mng_close,
|
|
622 demux_mng_seek,
|
|
623 demux_mng_control
|
|
624 };
|