2 * vo_yuv4mpeg.c, yuv4mpeg (mjpegtools) interface
5 * Robert Kesterson <robertk@robertk.com>
6 * Based on the pgm output plugin, the rgb2rgb postproc filter, divxdec,
9 * This is undoubtedly incomplete, inaccurate, or just plain wrong. :-)
11 * 2002/06/19 Klaus Stengel <Klaus.Stengel@asamnet.de>
12 * - added support for interlaced output
13 * Activate by using '-vo yuv4mpeg:interlaced'
14 * or '-vo yuv4mpeg:interlaced_bf' if your source has
16 * - added some additional checks to catch problems
18 * 2002/04/17 Juergen Hammelmann <juergen.hammelmann@gmx.de>
19 * - added support for output of subtitles
20 * best, if you give option '-osdlevel 0' to mplayer for
21 * no watching the seek+timer
32 #include "video_out.h"
33 #include "video_out_internal.h"
40 #include "fastmemcpy.h"
41 #include "postproc/rgb2rgb.h"
42 #include "libmpcodecs/vf_scale.h"
44 static vo_info_t info
=
46 "yuv4mpeg output for mjpegtools (to \"stream.yuv\")",
48 "Robert Kesterson <robertk@robertk.com>",
52 LIBVO_EXTERN (yuv4mpeg
)
54 static int image_width
;
55 static int image_height
;
57 static uint8_t *image
= NULL
;
58 static uint8_t *image_y
= NULL
;
59 static uint8_t *image_u
= NULL
;
60 static uint8_t *image_v
= NULL
;
62 static uint8_t *rgb_buffer
= NULL
;
63 static uint8_t *rgb_line_buffer
= NULL
;
65 static int using_format
= 0;
67 static int write_bytes
;
69 #define Y4M_ILACE_NONE 'p' /* non-interlaced, progressive frame */
70 #define Y4M_ILACE_TOP_FIRST 't' /* interlaced, top-field first */
71 #define Y4M_ILACE_BOTTOM_FIRST 'b' /* interlaced, bottom-field first */
73 /* Set progressive mode as default */
74 static int config_interlace
= Y4M_ILACE_NONE
;
75 #define Y4M_IS_INTERLACED (config_interlace != Y4M_ILACE_NONE)
77 static uint32_t config(uint32_t width
, uint32_t height
, uint32_t d_width
,
78 uint32_t d_height
, uint32_t fullscreen
, char *title
,
81 image_height
= height
;
83 using_format
= format
;
85 if (Y4M_IS_INTERLACED
)
89 mp_msg(MSGT_VO
,MSGL_FATAL
,
90 MSGTR_VO_YUV4MPEG_InterlacedHeightDivisibleBy4
);
94 rgb_line_buffer
= malloc(image_width
* 3);
97 mp_msg(MSGT_VO
,MSGL_FATAL
,
98 MSGTR_VO_YUV4MPEG_InterlacedLineBufAllocFail
);
102 if (using_format
== IMGFMT_YV12
)
103 mp_msg(MSGT_VO
,MSGL_WARN
,
104 MSGTR_VO_YUV4MPEG_InterlacedInputNotRGB
);
109 mp_msg(MSGT_VO
,MSGL_FATAL
,
110 MSGTR_VO_YUV4MPEG_WidthDivisibleBy2
);
114 if(using_format
!= IMGFMT_YV12
)
116 sws_rgb2rgb_init(get_sws_cpuflags());
117 rgb_buffer
= malloc(image_width
* image_height
* 3);
120 mp_msg(MSGT_VO
,MSGL_FATAL
,
121 MSGTR_VO_YUV4MPEG_NoMemRGBFrameBuf
);
126 write_bytes
= image_width
* image_height
* 3 / 2;
127 image
= malloc(write_bytes
);
129 yuv_out
= fopen("stream.yuv", "wb");
130 if (!yuv_out
|| image
== 0)
132 mp_msg(MSGT_VO
,MSGL_FATAL
,
133 MSGTR_VO_YUV4MPEG_OutFileOpenError
);
137 image_u
= image_y
+ image_width
* image_height
;
138 image_v
= image_u
+ image_width
* image_height
/ 4;
141 // But it should work as long as the file isn't interlaced
142 // or otherwise unusual (the "Ip A0:0" part).
144 /* At least the interlacing is ok now */
145 fprintf(yuv_out
, "YUV4MPEG2 W%d H%d F%ld:%ld I%c A0:0\n",
146 image_width
, image_height
, (long)(vo_fps
* 1000000.0),
147 (long)1000000, config_interlace
);
153 /* Only use when h divisable by 2! */
154 static void swap_fields(uint8_t *ptr
, const int h
, const int stride
)
158 for (i
=0; i
<h
; i
+=2)
160 memcpy(rgb_line_buffer
, ptr
+ stride
* i
, stride
);
161 memcpy(ptr
+ stride
* i
, ptr
+ stride
* (i
+1), stride
);
162 memcpy(ptr
+ stride
* (i
+1), rgb_line_buffer
, stride
);
166 static void draw_alpha(int x0
, int y0
, int w
, int h
, unsigned char *src
,
167 unsigned char *srca
, int stride
) {
168 switch (using_format
)
171 vo_draw_alpha_yv12(w
, h
, src
, srca
, stride
,
172 image
+ y0
* image_width
+ x0
, image_width
);
177 if (config_interlace
!= Y4M_ILACE_BOTTOM_FIRST
)
178 vo_draw_alpha_rgb24(w
, h
, src
, srca
, stride
,
179 rgb_buffer
+ (y0
* image_width
+ x0
) * 3, image_width
* 3);
182 swap_fields (rgb_buffer
, image_height
, image_width
* 3);
184 vo_draw_alpha_rgb24(w
, h
, src
, srca
, stride
,
185 rgb_buffer
+ (y0
* image_width
+ x0
) * 3, image_width
* 3);
187 swap_fields (rgb_buffer
, image_height
, image_width
* 3);
193 static void draw_osd(void)
195 vo_draw_text(image_width
, image_height
, draw_alpha
);
198 static void deinterleave_fields(uint8_t *ptr
, const int stride
,
199 const int img_height
)
201 unsigned int i
, j
, k_start
= 1, modv
= img_height
- 1;
202 unsigned char *line_state
= malloc(modv
);
204 for (i
=0; i
<modv
; i
++)
209 while(k_start
< modv
)
212 memcpy(rgb_line_buffer
, ptr
+ stride
* i
, stride
);
214 while (!line_state
[j
])
219 memcpy(ptr
+ stride
* i
, ptr
+ stride
* j
, stride
);
221 memcpy(ptr
+ stride
* i
, rgb_line_buffer
, stride
);
223 while(k_start
< modv
&& line_state
[k_start
])
229 static void vo_y4m_write(const void *ptr
, const size_t num_bytes
)
231 if (fwrite(ptr
, 1, num_bytes
, yuv_out
) != num_bytes
)
232 mp_msg(MSGT_VO
,MSGL_ERR
,
233 MSGTR_VO_YUV4MPEG_OutFileWriteError
);
236 static int write_last_frame(void)
239 uint8_t *upper_y
, *upper_u
, *upper_v
, *rgb_buffer_lower
;
240 int rgb_stride
, uv_stride
, field_height
;
241 unsigned int i
, low_ofs
;
243 fprintf(yuv_out
, "FRAME\n");
245 if (using_format
!= IMGFMT_YV12
)
247 rgb_stride
= image_width
* 3;
248 uv_stride
= image_width
/ 2;
250 if (Y4M_IS_INTERLACED
)
252 field_height
= image_height
/ 2;
255 upper_u
= upper_y
+ image_width
* field_height
;
256 upper_v
= upper_u
+ image_width
* field_height
/ 4;
257 low_ofs
= image_width
* field_height
* 3 / 2;
258 rgb_buffer_lower
= rgb_buffer
+ rgb_stride
* field_height
;
261 for(i
= 0; i
< field_height
; i
++)
263 vo_y4m_write(upper_y
+ image_width
* i
, image_width
);
264 vo_y4m_write(upper_y
+ image_width
* i
+ low_ofs
, image_width
);
267 /* Write U and V plane */
268 for(i
= 0; i
< field_height
/ 2; i
++)
270 vo_y4m_write(upper_u
+ uv_stride
* i
, uv_stride
);
271 vo_y4m_write(upper_u
+ uv_stride
* i
+ low_ofs
, uv_stride
);
273 for(i
= 0; i
< field_height
/ 2; i
++)
275 vo_y4m_write(upper_v
+ uv_stride
* i
, uv_stride
);
276 vo_y4m_write(upper_v
+ uv_stride
* i
+ low_ofs
, uv_stride
);
278 return VO_TRUE
; /* Image written; We have to stop here */
281 /* Write progressive frame */
282 vo_y4m_write(image
, write_bytes
);
286 static void flip_page (void)
288 uint8_t *upper_y
, *upper_u
, *upper_v
, *rgb_buffer_lower
;
289 int rgb_stride
, uv_stride
, field_height
;
290 unsigned int i
, low_ofs
;
292 fprintf(yuv_out
, "FRAME\n");
294 if (using_format
!= IMGFMT_YV12
)
296 rgb_stride
= image_width
* 3;
297 uv_stride
= image_width
/ 2;
299 if (Y4M_IS_INTERLACED
)
301 field_height
= image_height
/ 2;
304 upper_u
= upper_y
+ image_width
* field_height
;
305 upper_v
= upper_u
+ image_width
* field_height
/ 4;
306 low_ofs
= image_width
* field_height
* 3 / 2;
307 rgb_buffer_lower
= rgb_buffer
+ rgb_stride
* field_height
;
309 deinterleave_fields(rgb_buffer
, rgb_stride
, image_height
);
311 rgb24toyv12(rgb_buffer
, upper_y
, upper_u
, upper_v
,
312 image_width
, field_height
,
313 image_width
, uv_stride
, rgb_stride
);
314 rgb24toyv12(rgb_buffer_lower
, upper_y
+ low_ofs
,
315 upper_u
+ low_ofs
, upper_v
+ low_ofs
,
316 image_width
, field_height
,
317 image_width
, uv_stride
, rgb_stride
);
320 for(i
= 0; i
< field_height
; i
++)
322 vo_y4m_write(upper_y
+ image_width
* i
, image_width
);
323 vo_y4m_write(upper_y
+ image_width
* i
+ low_ofs
, image_width
);
326 /* Write U and V plane */
327 for(i
= 0; i
< field_height
/ 2; i
++)
329 vo_y4m_write(upper_u
+ uv_stride
* i
, uv_stride
);
330 vo_y4m_write(upper_u
+ uv_stride
* i
+ low_ofs
, uv_stride
);
332 for(i
= 0; i
< field_height
/ 2; i
++)
334 vo_y4m_write(upper_v
+ uv_stride
* i
, uv_stride
);
335 vo_y4m_write(upper_v
+ uv_stride
* i
+ low_ofs
, uv_stride
);
337 return; /* Image written; We have to stop here */
340 rgb24toyv12(rgb_buffer
, image_y
, image_u
, image_v
,
341 image_width
, image_height
,
342 image_width
, uv_stride
, rgb_stride
);
345 /* Write progressive frame */
346 vo_y4m_write(image
, write_bytes
);
349 static uint32_t draw_slice(uint8_t *srcimg
[], int stride
[], int w
,int h
,int x
,int y
)
352 uint8_t *dst
, *src
= srcimg
[0];
354 switch (using_format
)
359 dst
= image_y
+ image_width
* y
+ x
;
360 for (i
= 0; i
< h
; i
++)
368 int imgstride
= image_width
>> 1;
369 uint8_t *src1
= srcimg
[1];
370 uint8_t *src2
= srcimg
[2];
371 uint8_t *dstu
= image_u
+ imgstride
* (y
>> 1) + (x
>> 1);
372 uint8_t *dstv
= image_v
+ imgstride
* (y
>> 1) + (x
>> 1);
373 for (i
= 0; i
< h
/ 2; i
++)
375 memcpy(dstu
, src1
, w
>> 1);
376 memcpy(dstv
, src2
, w
>> 1);
387 dst
= rgb_buffer
+ (image_width
* y
+ x
) * 3;
388 for (i
= 0; i
< h
; i
++)
390 memcpy(dst
, src
, w
* 3);
392 dst
+= image_width
* 3;
399 static uint32_t draw_frame(uint8_t * src
[])
404 // gets done in draw_slice
409 memcpy(rgb_buffer
, src
[0], image_width
* image_height
* 3);
415 static uint32_t query_format(uint32_t format
)
418 if (Y4M_IS_INTERLACED
)
420 /* When processing interlaced material we want to get the raw RGB
421 * data and do the YV12 conversion ourselves to have the chrominance
422 * information sampled correct. */
427 return VFCAP_CSP_SUPPORTED
|VFCAP_OSD
|VFCAP_ACCEPT_STRIDE
;
430 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
|VFCAP_OSD
|VFCAP_ACCEPT_STRIDE
;
439 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
|VFCAP_OSD
|VFCAP_ACCEPT_STRIDE
;
442 return VFCAP_CSP_SUPPORTED
|VFCAP_OSD
|VFCAP_ACCEPT_STRIDE
;
448 static void uninit(void)
463 free(rgb_line_buffer
);
464 rgb_line_buffer
= NULL
;
468 static void check_events(void)
473 static uint32_t preinit(const char *arg
)
475 int arg_unrecognized
= 0;
479 /* configure output mode */
480 if (strcmp(arg
, "interlaced"))
483 config_interlace
= Y4M_ILACE_TOP_FIRST
;
485 if (strcmp(arg
, "interlaced_bf"))
488 config_interlace
= Y4M_ILACE_BOTTOM_FIRST
;
490 /* If both tests failed the argument is invalid */
491 if (arg_unrecognized
== 2)
493 mp_msg(MSGT_VO
,MSGL_FATAL
,
494 MSGTR_VO_YUV4MPEG_UnknownSubDev
,arg
);
499 /* Inform user which output mode is used */
500 switch (config_interlace
)
502 case Y4M_ILACE_TOP_FIRST
:
503 mp_msg(MSGT_VO
,MSGL_STATUS
,
504 MSGTR_VO_YUV4MPEG_InterlacedTFFMode
);
506 case Y4M_ILACE_BOTTOM_FIRST
:
507 mp_msg(MSGT_VO
,MSGL_STATUS
,
508 MSGTR_VO_YUV4MPEG_InterlacedBFFMode
);
511 mp_msg(MSGT_VO
,MSGL_STATUS
,
512 MSGTR_VO_YUV4MPEG_ProgressiveMode
);
518 static uint32_t control(uint32_t request
, void *data
, ...)
521 case VOCTRL_QUERY_FORMAT
:
522 return query_format(*((uint32_t*)data
));
523 case VOCTRL_DUPLICATE_FRAME
:
524 return write_last_frame();