Mercurial > mplayer.hg
annotate libmpdemux/demux_mng.c @ 35466:0583b12e11d7
Fix bug with playlist.
The first file played twice in a newly created playlist
that previously was empty.
author | ib |
---|---|
date | Sun, 02 Dec 2012 15:55:01 +0000 |
parents | 8fa2f43cb760 |
children | f3c835ddce85 |
rev | line source |
---|---|
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 | |
32123 | 447 sh_video->bih = malloc(sizeof(*sh_video->bih)); |
28018 | 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 | |
32537
8fa2f43cb760
Remove most of the NULL pointer check before free all over the code
cboesch
parents:
32123
diff
changeset
|
479 free(mng_priv->canvas); |
28018 | 480 |
481 free(mng_priv); | |
482 } | |
483 } | |
484 | |
485 /** | |
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 | |
491 */ | |
492 static void demux_mng_seek(demuxer_t * demuxer, float rel_seek_secs, | |
493 float audio_delay, int flags) | |
494 { | |
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; | |
499 | |
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; | |
503 | |
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); | |
509 | |
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; | |
515 | |
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; | |
521 | |
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. | |
527 | |
528 // seeking forward | |
529 if (pos_ms >= mng_priv->show_cur_time_ms) { | |
530 | |
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; | |
534 | |
535 } // if (pos_ms > mng_priv->show_time_ms) | |
536 | |
537 // seeking backward | |
538 else { // if (pos_ms > mng_priv->show_time_ms) | |
539 | |
540 // Clear variable MNG library will write number of milliseconds to | |
541 // (via settimer callback). | |
542 mng_priv->timer_ms = 0; | |
543 | |
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); | |
552 } | |
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; | |
558 } | |
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; | |
564 | |
565 } // if (pos_ms > mng_priv->show_time_ms) ... else | |
566 } | |
567 | |
568 /** | |
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 | |
574 */ | |
575 static int demux_mng_control(demuxer_t * demuxer, int cmd, void * arg) | |
576 { | |
577 mng_priv_t * mng_priv = demuxer->priv; | |
578 | |
579 switch(cmd) { | |
580 | |
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; | |
588 } | |
589 break; | |
590 | |
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; | |
600 } | |
601 break; | |
602 | |
603 default: | |
604 return DEMUXER_CTRL_NOTIMPL; | |
605 | |
606 } // switch (cmd) | |
607 } | |
608 | |
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 | |
623 }; |