2 * Copyright (C) 2005 Rik Snel <rsnel@cube.dyndns.org>
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
7 * This file is part of MPlayer.
9 * MPlayer is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * MPlayer is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 /* some convenient #define's, is this portable enough? */
32 #define VERBOSE(...) mp_msg(MSGT_DECVIDEO, MSGL_V, "vd_zrmjpeg: " __VA_ARGS__)
33 #define ERROR(...) mp_msg(MSGT_DECVIDEO, MSGL_ERR, "vd_zrmjpeg: " __VA_ARGS__)
34 #define WARNING(...) mp_msg(MSGT_DECVIDEO, MSGL_WARN, \
35 "vd_zrmjpeg: " __VA_ARGS__)
37 #include "vd_internal.h"
39 static vd_info_t info
=
41 "Zoran MJPEG Video passthrough",
43 "Rik Snel <snel@phys.uu.nl>",
44 "Rik Snel <snel@phys.uu.nl>",
45 "for hw decoders (DC10(+)/buz/lml33)"
50 #include "libvo/video_out.h"
54 unsigned int preferred_csp
;
57 static int query_format(sh_video_t
*sh
, unsigned int format
) {
58 vd_zrmjpeg_ctx_t
*ctx
= sh
->context
;
59 if (format
== ctx
->preferred_csp
) return VFCAP_CSP_SUPPORTED
;
63 // to set/get/query special features/parameters
64 static int control(sh_video_t
*sh
, int cmd
, void* arg
, ...) {
66 case VDCTRL_QUERY_FORMAT
:
67 return query_format(sh
, *((unsigned int*)arg
));
69 return CONTROL_UNKNOWN
;
73 static int init(sh_video_t
*sh
) {
74 vd_zrmjpeg_ctx_t
*ctx
;
76 VERBOSE("init called\n");
77 ctx
= malloc(sizeof(*ctx
));
79 memset(ctx
, 0, sizeof(*ctx
));
82 /* defer init of vo until the first frame is known */
85 return mpcodecs_config_vo(sh
, sh
->disp_w
, sh
->disp_h
, IMGFMT_ZRMJPEGIT
);
90 static void uninit(sh_video_t
*sh
) {
94 /* parts directly stolen from scan_jpg() and lav_open_input_file */
95 static int get_int2(unsigned char *buf
) {
96 return buf
[0]*256 + buf
[1];
102 #define M_SOI 0xD8 /* Start Of Image (beginning of datastream) */
103 #define M_EOI 0xD9 /* End Of Image (end of datastream) */
104 #define M_SOS 0xDA /* Start Of Scan (begins compressed data) */
108 /* returns 0 in case of failure */
109 static unsigned int guess_mjpeg_type(unsigned char *data
, unsigned int size
,
112 int marker
, length
, height
, i
, hf
[3], vf
[3];
113 unsigned int app0
= 0, header
= 0;
115 /* The initial marker must be SIO */
117 ERROR("JPEG data too short (%d bytes)\n", size
);
121 if (data
[0] != 0xFF || data
[1] != M_SOI
) {
122 ERROR("JPEG data must start with FFD8, but doesn't\n");
126 p
= 2; /* pointer within jpeg data */
130 while(data
[p
] != 0xFF) {
132 if (p
>= size
) return 0;
135 /* get marker code, skip duplicate FF's */
136 while(data
[p
] == 0xFF) {
138 if (p
>= size
) return 0;
143 /* marker may have an associated length */
144 if (p
<= size
- 2) length
= get_int2(data
+p
);
151 VERBOSE("found offset of header %u\n",
159 VERBOSE("found offset of APP0 %u\n",
164 /* these markers shouldn't have parameters,
165 * i.e. we don't need to skip anaything */
166 if (marker
== 0 || marker
== 1 ||
167 (marker
>= 0xd0 && marker
< 0xd8))
170 if (p
+ length
<= size
) p
+= length
;
172 ERROR("input JPEG too short, data missing\n");
178 ERROR("JPEG header (with resolution and sampling factors) not found\n");
182 if (data
[header
+ 9] != 3) {
183 ERROR("JPEG has wrong number of components\n");
187 /* get the horizontal and vertical sample factors */
188 for (i
= 0; i
< 3; i
++) {
189 hf
[i
] = data
[header
+ 10 + 3*i
+ 1]>>4;
190 vf
[i
] = data
[header
+ 10 + 3*i
+ 1]&0x0F;
193 if (hf
[0] != 2 || hf
[1] != 1 || hf
[2] != 1 ||
194 vf
[0] != 1 || vf
[1] != 1 || vf
[2] != 1) {
195 ERROR("JPEG has wrong internal image format\n");
196 } else VERBOSE("JPEG has colorspace YUV422 with minimal sampling factors (good)\n");
198 height
= get_int2(data
+ header
+ 5);
199 if (height
== d_height
) {
200 VERBOSE("data is non interlaced\n");
201 return IMGFMT_ZRMJPEGNI
;
204 if (2*height
!= d_height
) {
205 ERROR("something very inconsistent happened\n");
210 if (app0
&& get_int2(data
+ app0
+ 2) >= 5 &&
211 strncasecmp((char*)(data
+ app0
+ 4), "AVI1", 4) == 0) {
212 if (data
[app0
+8] == 1) {
213 VERBOSE("data is interlaced, APP0: top-first (1)\n");
214 return IMGFMT_ZRMJPEGIT
;
216 VERBOSE("data is interlaced, APP0: bottom-first (%d)\n",
218 return IMGFMT_ZRMJPEGIB
;
221 VERBOSE("data is interlaced, no (valid) APP0 marker, "
222 "guessing top-first\n");
223 return IMGFMT_ZRMJPEGIT
;
231 static mp_image_t
* decode(sh_video_t
*sh
, void* data
, int len
, int flags
) {
233 vd_zrmjpeg_ctx_t
*ctx
= sh
->context
;
235 if (!ctx
->vo_initialized
) {
236 ctx
->preferred_csp
= guess_mjpeg_type(data
, len
, sh
->disp_h
);
237 if (ctx
->preferred_csp
== 0) return NULL
;
238 mpcodecs_config_vo(sh
, sh
->disp_w
, sh
->disp_h
,
240 ctx
->vo_initialized
= 1;
243 mpi
= mpcodecs_get_image(sh
, MP_IMGTYPE_EXPORT
, 0,
244 sh
->disp_w
, sh
->disp_h
);
246 mpi
->planes
[0]=(uint8_t*)data
;
247 mpi
->planes
[1]=(uint8_t*)len
;