33389
|
1 /*
|
|
2 * MPlayer output to MNG file
|
|
3 *
|
|
4 * Copyright (C) 2011 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 <stdlib.h>
|
|
24 #include <string.h>
|
|
25 #include <unistd.h>
|
|
26 #include <fcntl.h>
|
|
27 #include <errno.h>
|
|
28 #include <sys/stat.h>
|
|
29 #include <sys/types.h>
|
|
30
|
|
31 #include <zlib.h>
|
|
32
|
|
33 #define MNG_INCLUDE_WRITE_PROCS
|
|
34 #define MNG_ACCESS_CHUNKS
|
|
35 #define MNG_SUPPORT_READ
|
|
36 #define MNG_SUPPORT_DISPLAY
|
|
37 #define MNG_SUPPORT_WRITE
|
|
38 #include <libmng.h>
|
|
39
|
|
40 #include "video_out.h"
|
|
41 #include "video_out_internal.h"
|
|
42 #include "mp_msg.h"
|
33558
|
43 #include "subopt-helper.h"
|
33389
|
44
|
|
45 #define VOMNG_DEFAULT_DELAY_MS (100) /* default delay of a frame */
|
|
46
|
|
47 static vo_info_t info = {
|
|
48 .name = "MNG file",
|
|
49 .short_name = "mng",
|
|
50 .author = "Stefan Schuermans <stefan blinkenarea org>"
|
|
51 };
|
|
52
|
|
53 LIBVO_EXTERN(mng)
|
|
54
|
|
55 /* a frame to be written to the MNG file */
|
|
56 struct vomng_frame {
|
|
57 mng_ptr data; /**< deflate compressed data, malloc-ed */
|
|
58 mng_uint32 len; /**< length of compressed data */
|
|
59 unsigned int time_ms; /**< timestamp of frame (in ms) */
|
|
60 struct vomng_frame *next; /**< next frame */
|
|
61 };
|
|
62
|
|
63 /* properties of MNG output */
|
|
64 struct vomng_properties {
|
|
65 char *out_file_name; /**< name of output file, malloc-ed */
|
|
66 unsigned int width, height; /**< dimensions */
|
|
67 unsigned char *canvas; /**< canvas for frame,
|
|
68 canvas := row ... row,
|
|
69 row := filter_id pix ... pix,
|
|
70 pix := red green blue */
|
|
71 struct vomng_frame *frame_first; /**< list of frames */
|
|
72 struct vomng_frame *frame_last;
|
|
73 int is_init; /**< if initialized */
|
|
74 };
|
|
75
|
|
76 /* private data of MNG vo module */
|
|
77 static struct vomng_properties vomng;
|
|
78
|
|
79 /**
|
|
80 * @brief libmng callback: allocate memory
|
|
81 * @param[in] size size of requested memory block
|
|
82 * @return pointer to memory block, which is initialized to zero
|
|
83 */
|
|
84 static mng_ptr vomng_alloc(mng_size_t size)
|
|
85 {
|
|
86 return calloc(1, size);
|
|
87 }
|
|
88
|
|
89 /**
|
|
90 * @brief libmng callback: free memory
|
|
91 * @param[in] pointer to memory block
|
|
92 * @param[in] size size of requested memory block
|
|
93 */
|
|
94 static void vomng_free(mng_ptr ptr, mng_size_t size)
|
|
95 {
|
|
96 free(ptr);
|
|
97 }
|
|
98
|
|
99 /**
|
|
100 * @brief libmng callback: open stream
|
|
101 * @param[in] mng libmng handle
|
|
102 * @return if stream could be opened
|
|
103 */
|
|
104 static mng_bool vomng_openstream(mng_handle mng)
|
|
105 {
|
|
106 return MNG_TRUE; /* stream is always open wen we get here,
|
|
107 tell libmng that everything is okay */
|
|
108 }
|
|
109
|
|
110 /**
|
|
111 * @brief libmng callback: stream should be closed
|
|
112 * @param[in] mng libmng handle
|
|
113 * @return if stream could be closed
|
|
114 */
|
|
115 static mng_bool vomng_closestream(mng_handle mng)
|
|
116 {
|
|
117 return MNG_TRUE; /* stream will be closed later,
|
|
118 tell libmng that everything is okay */
|
|
119 }
|
|
120
|
|
121 /**
|
|
122 * @brief libmng callback: write libmng data to the open stream
|
|
123 * @param[in] mng libmng handle
|
|
124 * @param[in] *buf pointer to data to write
|
|
125 * @param[in] size size of data to write
|
|
126 * @param[out] *written size of data written
|
|
127 * @return if data was written successfully
|
|
128 */
|
|
129 static mng_bool vomng_writedata(mng_handle mng, mng_ptr buf,
|
|
130 mng_uint32 size, mng_uint32 *written)
|
|
131 {
|
|
132 FILE *file = mng_get_userdata(mng);
|
|
133 *written = fwrite(buf, 1, size, file);
|
|
134 /* according to the example in libmng documentation, true is always
|
|
135 returned here, short writes can be detected by libmng via *written */
|
|
136 return MNG_TRUE;
|
|
137 }
|
|
138
|
|
139 /**
|
|
140 * @brief compress frame data
|
|
141 * @param[in] width width of canvas
|
|
142 * @param[in] height height of canvas
|
|
143 * @param[in] *canvas data on canvas (including MNG filter IDs)
|
|
144 * @param[out] *out_ptr pointer to compressed data, malloc-ed
|
|
145 * @param[out] *out_len length of compressed data
|
|
146 */
|
|
147 static void vomng_canvas_to_compressed(unsigned int width, unsigned int height,
|
|
148 const unsigned char *canvas,
|
|
149 mng_ptr *out_ptr, mng_uint32 *out_len)
|
|
150 {
|
|
151 mng_uint32 raw_len;
|
|
152 unsigned char *ptr;
|
|
153 unsigned long len;
|
|
154
|
|
155 /* default: no output */
|
|
156 *out_ptr = NULL;
|
|
157 *out_len = 0;
|
|
158
|
|
159 /* raw_len := length of input data
|
|
160 - it will be significantly shorter than 32 bit
|
|
161 - the "1 +" is needed because each row starts with the filter ID */
|
|
162 raw_len = height * (1 + width * 3);
|
|
163
|
|
164 /* compress data
|
|
165 - compress2 output size will be smaller than raw_len * 1.001 + 12 (see
|
|
166 man page), so calculate the next larger integer value in len and
|
|
167 allocate abuffer of this size
|
|
168 - len will still contain a value shorter than 32 bit */
|
|
169 len = raw_len + (raw_len + 999) / 1000 + 12;
|
|
170 ptr = malloc(len);
|
|
171 if (!ptr)
|
|
172 return;
|
|
173 compress2(ptr, &len, canvas, raw_len, Z_BEST_COMPRESSION);
|
|
174
|
|
175 /* return allocated compressed data
|
|
176 - we have to convert the output length to a shorter data type as
|
|
177 libmng does not accept an unsigned long as length
|
|
178 - convert here, because we can see here that the conversion is safe
|
|
179 - see comments about raw_len and len above
|
|
180 - compress2 never increases value of len */
|
|
181 *out_ptr = ptr;
|
|
182 *out_len = len;
|
|
183 }
|
|
184
|
|
185 /**
|
|
186 * @brief write frame to MNG file
|
|
187 * @param[in] *frame the frame to write to MNG file
|
|
188 * @param[in] mng libmng handle
|
|
189 * @param[in] width width of canvas
|
|
190 * @param[in] height height of canvas
|
|
191 * @param[in] first_frame if the frame is the first one in the file
|
|
192 * @return 0 on success, 1 on error
|
|
193 */
|
|
194 static int vomng_write_frame(struct vomng_frame *frame, mng_handle mng,
|
|
195 unsigned int width, unsigned int height,
|
|
196 int first_frame)
|
|
197 {
|
|
198 unsigned int delay_ms;
|
|
199
|
|
200 /* determine delay */
|
|
201 if (frame->next)
|
|
202 delay_ms = frame->next->time_ms - frame->time_ms;
|
|
203 else
|
|
204 delay_ms = VOMNG_DEFAULT_DELAY_MS; /* default delay for last frame */
|
|
205
|
|
206 /* write frame headers to MNG file */
|
|
207 if (mng_putchunk_seek(mng, 0, MNG_NULL)) {
|
|
208 mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing SEEK chunk failed\n");
|
|
209 return 1;
|
|
210 }
|
|
211 if (mng_putchunk_fram(mng, MNG_FALSE,
|
|
212 /* keep canvas if not 1st frame */
|
|
213 first_frame ? MNG_FRAMINGMODE_1
|
|
214 : MNG_FRAMINGMODE_NOCHANGE,
|
|
215 0, MNG_NULL, /* no frame name */
|
|
216 MNG_CHANGEDELAY_DEFAULT, /* change only delay */
|
|
217 MNG_CHANGETIMOUT_NO,
|
|
218 MNG_CHANGECLIPPING_NO,
|
|
219 MNG_CHANGESYNCID_NO,
|
|
220 delay_ms, /* new delay */
|
|
221 0, /* no new timeout */
|
|
222 0, 0, 0, 0, 0, /* no new boundary */
|
|
223 0, 0)) { /* no count, no IDs */
|
|
224 mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing FRAM chunk failed\n");
|
|
225 return 1;
|
|
226 }
|
|
227 if (mng_putchunk_defi(mng, 0, /* no ID */
|
|
228 MNG_DONOTSHOW_VISIBLE,
|
|
229 MNG_ABSTRACT,
|
|
230 MNG_TRUE, 0, 0, /* top left location */
|
|
231 MNG_FALSE, 0, 0, 0, 0)) { /* no clipping */
|
|
232 mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing DEFI chunk failed\n");
|
|
233 return 1;
|
|
234 }
|
|
235 if (mng_putchunk_ihdr(mng, width, height, /* dimensions */
|
|
236 8, MNG_COLORTYPE_RGB, /* RBG */
|
|
237 MNG_COMPRESSION_DEFLATE,
|
|
238 MNG_FILTER_ADAPTIVE,
|
|
239 MNG_INTERLACE_NONE)) {
|
|
240 mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing IHDR chunk failed\n");
|
|
241 return 1;
|
|
242 }
|
|
243
|
|
244 /* write frame data */
|
|
245 if (mng_putchunk_idat(mng, frame->len, frame->data)) {
|
|
246 mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing IDAT chunk failed\n");
|
|
247 return 1;
|
|
248 }
|
|
249
|
|
250 /* write frame footers to MNG file */
|
|
251 if (mng_putchunk_iend(mng)) {
|
|
252 mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing IEND chunk failed\n");
|
|
253 return 1;
|
|
254 }
|
|
255
|
|
256 return 0;
|
|
257 }
|
|
258
|
|
259 /**
|
|
260 * @brief write buffered frames to MNG file
|
|
261 * @return 0 on success, 1 on error
|
|
262 */
|
|
263 static int vomng_write_file(void)
|
|
264 {
|
|
265 FILE *file;
|
|
266 mng_handle mng;
|
|
267 struct vomng_frame *frame;
|
|
268 unsigned int frames, duration_ms;
|
|
269 int first;
|
|
270
|
|
271 /* refuse to create empty MNG file */
|
|
272 if (!vomng.frame_first || !vomng.frame_last) {
|
|
273 mp_msg(MSGT_VO, MSGL_ERR, "vomng: not creating empty file\n");
|
|
274 return 1;
|
|
275 }
|
|
276
|
|
277 /* create output file */
|
|
278 file = fopen(vomng.out_file_name, "wb");
|
|
279 if (!file) {
|
|
280 mp_msg(MSGT_VO, MSGL_ERR,
|
|
281 "vomng: could not open output file \"%s\": %s\n",
|
|
282 vomng.out_file_name, strerror(errno));
|
|
283 return 1;
|
|
284 }
|
|
285
|
|
286 /* inititalize MNG library */
|
|
287 mng = mng_initialize(file, vomng_alloc, vomng_free, MNG_NULL);
|
|
288 if (!mng) {
|
|
289 mp_msg(MSGT_VO, MSGL_ERR, "vomng: could not initialize libmng\n");
|
|
290 fclose(file);
|
|
291 return 1;
|
|
292 }
|
|
293 if (mng_setcb_openstream (mng, vomng_openstream ) ||
|
|
294 mng_setcb_closestream(mng, vomng_closestream) ||
|
|
295 mng_setcb_writedata (mng, vomng_writedata )) {
|
|
296 mp_msg(MSGT_VO, MSGL_ERR, "vomng: cannot set callbacks for libmng\n");
|
|
297 mng_cleanup(&mng);
|
|
298 fclose(file);
|
|
299 return 1;
|
|
300 }
|
|
301
|
|
302 /* create new MNG image in memory */
|
|
303 if (mng_create(mng)) {
|
|
304 mp_msg(MSGT_VO, MSGL_ERR, "vomng: cannot create MNG image in memory\n");
|
|
305 mng_cleanup(&mng);
|
|
306 fclose(file);
|
|
307 return 1;
|
|
308 }
|
|
309
|
|
310 /* determine number of frames and total duration */
|
|
311 frames = 0;
|
|
312 for (frame = vomng.frame_first; frame; frame = frame->next)
|
|
313 frames++;
|
|
314 duration_ms = vomng.frame_last->time_ms - vomng.frame_first->time_ms;
|
|
315
|
|
316 /* write MNG header chunks */
|
|
317 if (mng_putchunk_mhdr(mng,
|
|
318 vomng.width, /* dimensions */
|
|
319 vomng.height,
|
|
320 1000, 0, /* ticks per second, layer */
|
|
321 frames, /* number of frames */
|
|
322 duration_ms, /* total duration */
|
|
323 MNG_SIMPLICITY_VALID |
|
|
324 MNG_SIMPLICITY_SIMPLEFEATURES |
|
|
325 MNG_SIMPLICITY_COMPLEXFEATURES) ||
|
|
326 mng_putchunk_save(mng,
|
|
327 MNG_TRUE, 0, 0) || /* empty save chunk */
|
|
328 mng_putchunk_term(mng,
|
|
329 MNG_TERMACTION_CLEAR, /* show last frame forever */
|
|
330 MNG_ITERACTION_CLEAR,
|
|
331 0, 0)) {
|
|
332 mp_msg(MSGT_VO, MSGL_ERR,
|
|
333 "vomng: writing MHDR/SAVE/TERM chunks failed\n");
|
|
334 mng_write(mng); /* write out buffered chunks before cleanup */
|
|
335 mng_cleanup(&mng);
|
|
336 fclose(file);
|
|
337 return 1;
|
|
338 }
|
|
339
|
|
340 /* write frames */
|
|
341 first = 1;
|
|
342 for (frame = vomng.frame_first; frame; frame = frame->next) {
|
|
343 if (vomng_write_frame(frame, mng, vomng.width, vomng.height, first))
|
|
344 break;
|
|
345 first = 0;
|
|
346 }
|
|
347 if (frame) {
|
|
348 mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing frames failed\n");
|
|
349 mng_write(mng); /* write out buffered chunks before cleanup */
|
|
350 mng_cleanup(&mng);
|
|
351 fclose(file);
|
|
352 return 1;
|
|
353 }
|
|
354
|
|
355 /* write MNG end chunk */
|
|
356 if (mng_putchunk_mend(mng)) {
|
|
357 mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing end chunk failed\n");
|
|
358 mng_write(mng); /* write out buffered chunks before cleanup */
|
|
359 mng_cleanup(&mng);
|
|
360 fclose(file);
|
|
361 return 1;
|
|
362 }
|
|
363
|
|
364 /* finish and cleanup */
|
|
365 mng_write(mng); /* write out buffered chunks before cleanup */
|
|
366 mng_cleanup(&mng);
|
|
367 fclose(file);
|
|
368
|
|
369 return 0;
|
|
370 }
|
|
371
|
|
372 /**
|
|
373 * @brief close all files and free all memory of MNG vo module
|
|
374 */
|
|
375 static void vomng_prop_reset(void)
|
|
376 {
|
|
377 struct vomng_frame *frame, *next;
|
|
378
|
|
379 /* we are initialized properly */
|
|
380 if (vomng.is_init) {
|
|
381 /* write buffered frames to MNG file */
|
|
382 if (vomng_write_file())
|
|
383 mp_msg(MSGT_VO, MSGL_ERR,
|
|
384 "vomng: writing output file failed\n");
|
|
385 }
|
|
386
|
|
387 /* reset state */
|
|
388 vomng.is_init = 0;
|
|
389 if (vomng.frame_first) {
|
|
390 frame = vomng.frame_first;
|
|
391 while (frame) {
|
|
392 next = frame->next;
|
|
393 free(frame->data);
|
|
394 free(frame);
|
|
395 frame = next;
|
|
396 }
|
|
397 vomng.frame_first = NULL;
|
|
398 vomng.frame_last = NULL;
|
|
399 }
|
|
400 free(vomng.canvas);
|
|
401 vomng.canvas = NULL;
|
|
402 vomng.width = 0;
|
|
403 vomng.height = 0;
|
|
404 }
|
|
405
|
|
406 /**
|
|
407 * @brief close files, free memory and delete private data of MNG von module
|
|
408 */
|
|
409 static void vomng_prop_cleanup(void)
|
|
410 {
|
|
411 vomng_prop_reset();
|
|
412 free(vomng.out_file_name);
|
|
413 }
|
|
414
|
|
415 /**
|
|
416 * @brief configure MNG vo module
|
|
417 * @param[in] width video width
|
|
418 * @param[in] height video height
|
|
419 * @param[in] d_width (unused)
|
|
420 * @param[in] d_height (unused)
|
|
421 * @param[in] flags (unused)
|
|
422 * @param[in] title (unused)
|
|
423 * @param[in] format video frame format
|
|
424 * @return 0 on success, 1 on error
|
|
425 */
|
|
426 static int config(uint32_t width, uint32_t height,
|
|
427 uint32_t d_width, uint32_t d_height,
|
|
428 uint32_t flags, char *title, uint32_t format)
|
|
429 {
|
|
430 uint32_t row_stride, y;
|
|
431
|
|
432 /* reset state */
|
|
433 vomng_prop_reset();
|
|
434
|
|
435 /* check format */
|
|
436 if (format != IMGFMT_RGB24) {
|
|
437 mp_msg(MSGT_VO, MSGL_ERR,
|
|
438 "vomng: config with invalid format (!= IMGFMT_RGB24)\n");
|
|
439 return 1;
|
|
440 }
|
|
441
|
|
442 /* allocate canvas */
|
|
443 vomng.width = width;
|
|
444 vomng.height = height;
|
|
445 row_stride = 1 + width * 3; /* rows contain filter IDs */
|
|
446 vomng.canvas = calloc(height * row_stride, 1);
|
|
447 if (!vomng.canvas) {
|
|
448 mp_msg(MSGT_VO, MSGL_ERR, "vomng: out of memory\n");
|
|
449 return 1;
|
|
450 }
|
|
451 /* fill in filter IDs for rows */
|
|
452 for (y = 0; y < height; y++)
|
|
453 *(vomng.canvas + row_stride * y) = MNG_FILTER_NONE;
|
|
454
|
|
455 /* we are initialized */
|
|
456 vomng.is_init = 1;
|
|
457
|
|
458 return 0;
|
|
459 }
|
|
460
|
|
461 /**
|
|
462 * @brief draw on screen display (unsupported for MNG vo module)
|
|
463 */
|
|
464 static void draw_osd(void)
|
|
465 {
|
|
466 }
|
|
467
|
|
468 /**
|
|
469 * @brief display data currently on canvas
|
|
470 */
|
|
471 static void flip_page(void)
|
|
472 {
|
|
473 unsigned int last_ms;
|
|
474 struct vomng_frame *frame;
|
|
475
|
|
476 /* get time of last frame in ms
|
|
477 (intensive testing showed that the time obtained from vo_pts
|
|
478 is the time of the previous frame) */
|
|
479 last_ms = (unsigned int)(vo_pts / 90.0 + 0.5);
|
|
480
|
|
481 /* set time of last frame */
|
|
482 if (vomng.frame_last)
|
|
483 vomng.frame_last->time_ms = last_ms;
|
|
484
|
|
485 /* create new frame */
|
|
486 frame = calloc(1, sizeof(*frame));
|
|
487 if (!frame) {
|
|
488 mp_msg(MSGT_VO, MSGL_ERR, "vomng: out of memory\n");
|
|
489 return;
|
|
490 }
|
|
491 /* time of frame is not yet known (see comment about vo_pts about 20
|
|
492 lines above), approximate time using time of last frame and the
|
|
493 default frame delay */
|
|
494 frame->time_ms = last_ms + VOMNG_DEFAULT_DELAY_MS;
|
|
495 frame->next = NULL;
|
|
496
|
|
497 /* compress canvas data */
|
|
498 vomng_canvas_to_compressed(vomng.width, vomng.height, vomng.canvas,
|
|
499 &frame->data, &frame->len);
|
|
500 if (!frame->data) {
|
|
501 mp_msg(MSGT_VO, MSGL_ERR, "vomng: compressing frame failed\n");
|
|
502 free(frame);
|
|
503 return;
|
|
504 }
|
|
505
|
|
506 /* add frame to list */
|
|
507 if (!vomng.frame_first || !vomng.frame_last) {
|
|
508 vomng.frame_first = frame;
|
|
509 vomng.frame_last = frame;
|
|
510 } else {
|
|
511 vomng.frame_last->next = frame;
|
|
512 vomng.frame_last = frame;
|
|
513 }
|
|
514 }
|
|
515
|
|
516 /**
|
|
517 * @brief put frame data onto canvas (not supported)
|
|
518 * @return always 1 to indicate error
|
|
519 */
|
|
520 static int draw_frame(uint8_t *src[])
|
|
521 {
|
|
522 /* draw_frame() not supported
|
|
523 * VFCAP_ACCEPT_STRIDE is set for format
|
|
524 * so draw_slice() will be called instead of this function */
|
|
525 return 1;
|
|
526 }
|
|
527
|
|
528 /**
|
|
529 * @brief deinitialize MNG vo module
|
|
530 */
|
|
531 static void uninit(void)
|
|
532 {
|
|
533 vomng_prop_cleanup();
|
|
534 }
|
|
535
|
|
536 /**
|
|
537 * @brief deal with events (not supported)
|
|
538 */
|
|
539 static void check_events(void)
|
|
540 {
|
|
541 }
|
|
542
|
|
543 /**
|
|
544 * @brief put a slice of frame data onto canvas
|
|
545 * @param[in] srcimg pointer to data
|
|
546 * @param[in] stride line stride in data
|
|
547 * @param[in] wf frame slice width
|
|
548 * @param[in] hf frame slice height
|
|
549 * @param[in] xf leftmost x coordinate of frame slice
|
|
550 * @param[in] yf topmost y coordinate of frame slice
|
|
551 * @return always 0 to indicate success
|
|
552 */
|
|
553 static int draw_slice(uint8_t *srcimg[], int stride[],
|
|
554 int wf, int hf, int xf, int yf)
|
|
555 {
|
|
556 uint8_t *line_ptr;
|
|
557 int line_len, row_stride, y;
|
|
558
|
|
559 /* put pixel data from slice to canvas */
|
|
560 line_ptr = srcimg[0];
|
|
561 line_len = stride[0];
|
|
562 row_stride = 1 + vomng.width * 3; /* rows contain filter IDs */
|
|
563 for (y = 0; y < hf; y++)
|
|
564 memcpy(vomng.canvas + (yf + y) * row_stride + 1 + xf * 3,
|
|
565 line_ptr + y * line_len, wf * 3);
|
|
566
|
|
567 return 0;
|
|
568 }
|
|
569
|
33558
|
570 /** list of suboptions */
|
|
571 static const opt_t subopts[] = {
|
|
572 {"output", OPT_ARG_MSTRZ, &vomng.out_file_name, NULL},
|
|
573 {NULL, 0, NULL, NULL}
|
|
574 };
|
|
575
|
33389
|
576 /**
|
|
577 * @brief pre-initialize MNG vo module
|
|
578 * @param[in] *arg arguments passed to MNG vo module (output file name)
|
|
579 * @return 0 on success, 1 on error
|
|
580 */
|
|
581 static int preinit(const char *arg)
|
|
582 {
|
33558
|
583 if (subopt_parse(arg, subopts)) {
|
|
584 mp_msg(MSGT_VO, MSGL_ERR,
|
|
585 "\n-vo mng command line help:\n"
|
|
586 "Example: mplayer -vo mng:output=file.mng\n"
|
|
587 "\nOptions:\n"
|
|
588 " output=<filename>\n"
|
|
589 " Specify the output file. The default is out.mng.\n"
|
|
590 "\n");
|
33389
|
591 vomng_prop_cleanup();
|
|
592 return 1;
|
|
593 }
|
33558
|
594 if (!vomng.out_file_name)
|
|
595 vomng.out_file_name = strdup("out.mng");
|
33389
|
596
|
|
597 return 0;
|
|
598 }
|
|
599
|
|
600 /**
|
|
601 * @brief get supported formats
|
|
602 * @param[in] format format to check support for
|
|
603 * @return acceptance flags
|
|
604 */
|
|
605 static int query_format(uint32_t format)
|
|
606 {
|
|
607 if (format == IMGFMT_RGB24)
|
|
608 return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW |
|
|
609 VFCAP_ACCEPT_STRIDE;
|
|
610 return 0;
|
|
611 }
|
|
612
|
|
613 /**
|
|
614 * @brief handle control stuff
|
|
615 * @param[in] request control request
|
|
616 * @param[in] *data data (dependent on control request)
|
|
617 * @return response to control request
|
|
618 */
|
|
619 static int control(uint32_t request, void *data)
|
|
620 {
|
|
621 switch (request) {
|
|
622 case VOCTRL_QUERY_FORMAT:
|
|
623 return query_format(*((uint32_t *)data));
|
|
624 }
|
|
625 return VO_NOTIMPL;
|
|
626 }
|
|
627
|