2 * Copyright (C) 2005 Rik Snel <rsnel@cube.dyndns.org>, license GPL v2 or later
3 * - based on vd_mpegpes.c by A'rpi (C) 2002-2003
4 * - guess_mjpeg_type code stolen from lav_io.c (C) 2000 Rainer Johanni
5 * <Rainer@Johanni.de> from the mjpegtools package
14 /* some convenient #define's, is this portable enough? */
15 #define VERBOSE(...) mp_msg(MSGT_DECVIDEO, MSGL_V, "vd_zrmjpeg: " __VA_ARGS__)
16 #define ERROR(...) mp_msg(MSGT_DECVIDEO, MSGL_ERR, "vd_zrmjpeg: " __VA_ARGS__)
17 #define WARNING(...) mp_msg(MSGT_DECVIDEO, MSGL_WARN, \
18 "vd_zrmjpeg: " __VA_ARGS__)
20 #include "vd_internal.h"
22 static vd_info_t info
=
24 "Zoran MJPEG Video passthrough",
26 "Rik Snel <snel@phys.uu.nl>",
27 "Rik Snel <snel@phys.uu.nl>",
28 "for hw decoders (DC10(+)/buz/lml33)"
33 #include "libvo/video_out.h"
37 unsigned int preferred_csp
;
40 static int query_format(sh_video_t
*sh
, unsigned int format
) {
41 vd_zrmjpeg_ctx_t
*ctx
= sh
->context
;
42 if (format
== ctx
->preferred_csp
) return VFCAP_CSP_SUPPORTED
;
46 // to set/get/query special features/parameters
47 static int control(sh_video_t
*sh
, int cmd
, void* arg
, ...) {
49 case VDCTRL_QUERY_FORMAT
:
50 return query_format(sh
, *((unsigned int*)arg
));
52 return CONTROL_UNKNOWN
;
56 static int init(sh_video_t
*sh
) {
57 vd_zrmjpeg_ctx_t
*ctx
;
59 VERBOSE("init called\n");
60 ctx
= malloc(sizeof(*ctx
));
62 memset(ctx
, 0, sizeof(*ctx
));
65 /* defer init of vo until the first frame is known */
68 return mpcodecs_config_vo(sh
, sh
->disp_w
, sh
->disp_h
, IMGFMT_ZRMJPEGIT
);
73 static void uninit(sh_video_t
*sh
) {
77 /* parts directly stolen from scan_jpg() and lav_open_input_file */
78 static int get_int2(unsigned char *buf
) {
79 return buf
[0]*256 + buf
[1];
85 #define M_SOI 0xD8 /* Start Of Image (beginning of datastream) */
86 #define M_EOI 0xD9 /* End Of Image (end of datastream) */
87 #define M_SOS 0xDA /* Start Of Scan (begins compressed data) */
91 /* returns 0 in case of failure */
92 static unsigned int guess_mjpeg_type(unsigned char *data
, unsigned int size
,
95 int marker
, length
, height
, ncomps
, i
, hf
[3], vf
[3];
96 unsigned int app0
= 0, header
= 0;
98 /* The initial marker must be SIO */
100 ERROR("JPEG data too short (%d bytes)\n", size
);
104 if (data
[0] != 0xFF || data
[1] != M_SOI
) {
105 ERROR("JPEG data must start with FFD8, but doesn't\n");
109 p
= 2; /* pointer within jpeg data */
113 while(data
[p
] != 0xFF) {
115 if (p
>= size
) return 0;
118 /* get marker code, skip duplicate FF's */
119 while(data
[p
] == 0xFF) {
121 if (p
>= size
) return 0;
126 /* marker may have an associated length */
127 if (p
<= size
- 2) length
= get_int2(data
+p
);
134 VERBOSE("found offset of header %u\n",
142 VERBOSE("found offset of APP0 %u\n",
147 /* these markers shouldn't have parameters,
148 * i.e. we don't need to skip anaything */
149 if (marker
== 0 || marker
== 1 ||
150 (marker
>= 0xd0 && marker
< 0xd8))
153 if (p
+ length
<= size
) p
+= length
;
155 ERROR("input JPEG too short, data missing\n");
161 ERROR("JPEG header (with resolution and sampling factors) not found\n");
165 if (data
[header
+ 9] != 3) {
166 ERROR("JPEG has wrong number of components\n");
170 /* get the horizontal and vertical sample factors */
171 for (i
= 0; i
< 3; i
++) {
172 hf
[i
] = data
[header
+ 10 + 3*i
+ 1]>>4;
173 vf
[i
] = data
[header
+ 10 + 3*i
+ 1]&0x0F;
176 if (hf
[0] != 2 || hf
[1] != 1 || hf
[2] != 1 ||
177 vf
[0] != 1 || vf
[1] != 1 || vf
[2] != 1) {
178 ERROR("JPEG has wrong internal image format\n");
179 } else VERBOSE("JPEG has colorspace YUV422 with minimal sampling factors (good)\n");
181 height
= get_int2(data
+ header
+ 5);
182 if (height
== d_height
) {
183 VERBOSE("data is non interlaced\n");
184 return IMGFMT_ZRMJPEGNI
;
187 if (2*height
!= d_height
) {
188 ERROR("something very inconsistent happened\n");
193 if (app0
&& get_int2(data
+ app0
+ 2) >= 5 &&
194 strncasecmp((char*)(data
+ app0
+ 4), "AVI1", 4) == 0) {
195 if (data
[app0
+8] == 1) {
196 VERBOSE("data is interlaced, APP0: top-first (1)\n");
197 return IMGFMT_ZRMJPEGIT
;
199 VERBOSE("data is interlaced, APP0: bottom-first (%d)\n",
201 return IMGFMT_ZRMJPEGIB
;
204 VERBOSE("data is interlaced, no (valid) APP0 marker, "
205 "guessing top-first\n");
206 return IMGFMT_ZRMJPEGIT
;
214 static mp_image_t
* decode(sh_video_t
*sh
, void* data
, int len
, int flags
) {
216 vd_zrmjpeg_ctx_t
*ctx
= sh
->context
;
218 if (!ctx
->vo_inited
) {
219 ctx
->preferred_csp
= guess_mjpeg_type(data
, len
, sh
->disp_h
);
220 if (ctx
->preferred_csp
== 0) return NULL
;
221 mpcodecs_config_vo(sh
, sh
->disp_w
, sh
->disp_h
,
226 mpi
= mpcodecs_get_image(sh
, MP_IMGTYPE_EXPORT
, 0,
227 sh
->disp_w
, sh
->disp_h
);
229 mpi
->planes
[0]=(uint8_t*)data
;
230 mpi
->planes
[1]=(uint8_t*)len
;