1 /**************************************************************************
2  *
3  * Copyright 2009 Younes Manton.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 
28 /* Force assertions, even on release builds. */
29 #undef NDEBUG
30 #include <assert.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include "testlib.h"
35 
36 #define BLOCK_WIDTH			8
37 #define BLOCK_HEIGHT			8
38 #define BLOCK_SIZE			(BLOCK_WIDTH * BLOCK_HEIGHT)
39 #define MACROBLOCK_WIDTH		16
40 #define MACROBLOCK_HEIGHT		16
41 #define MACROBLOCK_WIDTH_IN_BLOCKS	(MACROBLOCK_WIDTH / BLOCK_WIDTH)
42 #define MACROBLOCK_HEIGHT_IN_BLOCKS	(MACROBLOCK_HEIGHT / BLOCK_HEIGHT)
43 #define BLOCKS_PER_MACROBLOCK		6
44 
45 #define INPUT_WIDTH			64
46 #define INPUT_HEIGHT			64
47 #define INPUT_WIDTH_IN_MACROBLOCKS	(INPUT_WIDTH / MACROBLOCK_WIDTH)
48 #define INPUT_HEIGHT_IN_MACROBLOCKS	(INPUT_HEIGHT / MACROBLOCK_HEIGHT)
49 #define NUM_MACROBLOCKS			(INPUT_WIDTH_IN_MACROBLOCKS * INPUT_HEIGHT_IN_MACROBLOCKS)
50 
51 #define DEFAULT_OUTPUT_WIDTH		INPUT_WIDTH
52 #define DEFAULT_OUTPUT_HEIGHT		INPUT_HEIGHT
53 #define DEFAULT_ACCEPTABLE_ERR		0.01
54 
ParseArgs(int argc,char ** argv,unsigned int * output_width,unsigned int * output_height,double * acceptable_error,int * prompt)55 static void ParseArgs(int argc, char **argv, unsigned int *output_width, unsigned int *output_height, double *acceptable_error, int *prompt)
56 {
57 	int fail = 0;
58 	int i;
59 
60 	*output_width = DEFAULT_OUTPUT_WIDTH;
61 	*output_height = DEFAULT_OUTPUT_HEIGHT;
62 	*acceptable_error = DEFAULT_ACCEPTABLE_ERR;
63 	*prompt = 0;
64 
65 	for (i = 1; i < argc && !fail; ++i)
66 	{
67 		if (!strcmp(argv[i], "-w"))
68 		{
69 			if (sscanf(argv[++i], "%u", output_width) != 1)
70 				fail = 1;
71 		}
72 		else if (!strcmp(argv[i], "-h"))
73 		{
74 			if (sscanf(argv[++i], "%u", output_height) != 1)
75 				fail = 1;
76 		}
77 		else if (!strcmp(argv[i], "-e"))
78 		{
79 			if (sscanf(argv[++i], "%lf", acceptable_error) != 1)
80 				fail = 1;
81 		}
82 		else if (!strcmp(argv[i], "-p"))
83 			*prompt = 1;
84 		else
85 			fail = 1;
86 	}
87 
88 	if (fail)
89 	{
90 		fprintf(
91 			stderr,
92 			"Bad argument.\n"
93 			"\n"
94 			"Usage: %s [options]\n"
95 			"\t-w <width>\tOutput width\n"
96 			"\t-h <height>\tOutput height\n"
97 			"\t-e <error>\tAcceptable margin of error per pixel, from 0 to 1\n"
98 			"\t-p\tPrompt for quit\n",
99 			argv[0]
100 		);
101 		exit(1);
102 	}
103 }
104 
Gradient(short * block,unsigned int start,unsigned int stop,int horizontal,unsigned int intra_unsigned)105 static void Gradient(short *block, unsigned int start, unsigned int stop, int horizontal, unsigned int intra_unsigned)
106 {
107 	unsigned int x, y;
108 	unsigned int range = stop - start;
109 
110 	if (horizontal)
111 	{
112 		for (y = 0; y < BLOCK_HEIGHT; ++y)
113 			for (x = 0; x < BLOCK_WIDTH; ++x) {
114 				*block = (short)(start + range * (x / (float)(BLOCK_WIDTH - 1)));
115 				if (intra_unsigned)
116 					*block += 1 << 10;
117 				block++;
118 			}
119 	}
120 	else
121 	{
122 		for (y = 0; y < BLOCK_HEIGHT; ++y)
123 			for (x = 0; x < BLOCK_WIDTH; ++x) {
124 				*block = (short)(start + range * (y / (float)(BLOCK_WIDTH - 1)));
125 				if (intra_unsigned)
126 					*block += 1 << 10;
127 				block++;
128 			}
129 	}
130 }
131 
main(int argc,char ** argv)132 int main(int argc, char **argv)
133 {
134 	unsigned int		output_width;
135 	unsigned int		output_height;
136 	double			acceptable_error;
137 	int			prompt;
138 	Display			*display;
139 	Window			root, window;
140 	const unsigned int	mc_types[] = {XVMC_MOCOMP | XVMC_MPEG_2};
141 	XvPortID		port_num;
142 	int			surface_type_id;
143 	unsigned int		is_overlay, intra_unsigned;
144 	int			colorkey;
145 	XvMCContext		context;
146 	XvMCSurface		surface;
147 	XvMCBlockArray		block_array;
148 	XvMCMacroBlockArray	mb_array;
149 	int			mbx, mby, bx, by;
150 	XvMCMacroBlock		*mb;
151 	short			*blocks;
152 	int			quit = 0;
153 
154 	ParseArgs(argc, argv, &output_width, &output_height, &acceptable_error, &prompt);
155 
156 	display = XOpenDisplay(NULL);
157 
158 	if (!GetPort
159 	(
160 		display,
161 		INPUT_WIDTH,
162 		INPUT_HEIGHT,
163 		XVMC_CHROMA_FORMAT_420,
164 		mc_types,
165 		sizeof(mc_types)/sizeof(*mc_types),
166 		&port_num,
167 		&surface_type_id,
168 		&is_overlay,
169 		&intra_unsigned
170 	))
171 	{
172 		XCloseDisplay(display);
173 		fprintf(stderr, "Error, unable to find a good port.\n");
174 		exit(1);
175 	}
176 
177 	if (is_overlay)
178 	{
179 		Atom xv_colorkey = XInternAtom(display, "XV_COLORKEY", 0);
180 		XvGetPortAttribute(display, port_num, xv_colorkey, &colorkey);
181 	}
182 
183 	root = XDefaultRootWindow(display);
184 	window = XCreateSimpleWindow(display, root, 0, 0, output_width, output_height, 0, 0, colorkey);
185 
186 	assert(XvMCCreateContext(display, port_num, surface_type_id, INPUT_WIDTH, INPUT_HEIGHT, XVMC_DIRECT, &context) == Success);
187 	assert(XvMCCreateSurface(display, &context, &surface) == Success);
188 	assert(XvMCCreateBlocks(display, &context, NUM_MACROBLOCKS * BLOCKS_PER_MACROBLOCK, &block_array) == Success);
189 	assert(XvMCCreateMacroBlocks(display, &context, NUM_MACROBLOCKS, &mb_array) == Success);
190 
191 	mb = mb_array.macro_blocks;
192 	blocks = block_array.blocks;
193 
194 	for (mby = 0; mby < INPUT_HEIGHT_IN_MACROBLOCKS; ++mby)
195 		for (mbx = 0; mbx < INPUT_WIDTH_IN_MACROBLOCKS; ++mbx)
196 		{
197 			mb->x = mbx;
198 			mb->y = mby;
199 			mb->macroblock_type = XVMC_MB_TYPE_INTRA;
200 			/*mb->motion_type = ;*/
201 			/*mb->motion_vertical_field_select = ;*/
202 			mb->dct_type = XVMC_DCT_TYPE_FRAME;
203 			/*mb->PMV[0][0][0] = ;
204 			mb->PMV[0][0][1] = ;
205 			mb->PMV[0][1][0] = ;
206 			mb->PMV[0][1][1] = ;
207 			mb->PMV[1][0][0] = ;
208 			mb->PMV[1][0][1] = ;
209 			mb->PMV[1][1][0] = ;
210 			mb->PMV[1][1][1] = ;*/
211 			mb->index = (mby * INPUT_WIDTH_IN_MACROBLOCKS + mbx) * BLOCKS_PER_MACROBLOCK;
212 			mb->coded_block_pattern = 0x3F;
213 
214 			mb++;
215 
216 			for (by = 0; by < MACROBLOCK_HEIGHT_IN_BLOCKS; ++by)
217 				for (bx = 0; bx < MACROBLOCK_WIDTH_IN_BLOCKS; ++bx)
218 				{
219 					const int start = 16, stop = 235, range = stop - start;
220 
221 					Gradient
222 					(
223 						blocks,
224 						(short)(start + range * ((mbx * MACROBLOCK_WIDTH + bx * BLOCK_WIDTH) / (float)(INPUT_WIDTH - 1))),
225 						(short)(start + range * ((mbx * MACROBLOCK_WIDTH + bx * BLOCK_WIDTH + BLOCK_WIDTH - 1) / (float)(INPUT_WIDTH - 1))),
226 						1,
227 						intra_unsigned
228 					);
229 
230 					blocks += BLOCK_SIZE;
231 				}
232 
233 			for (by = 0; by < MACROBLOCK_HEIGHT_IN_BLOCKS / 2; ++by)
234 				for (bx = 0; bx < MACROBLOCK_WIDTH_IN_BLOCKS / 2; ++bx)
235 				{
236 					const int start = 16, stop = 240, range = stop - start;
237 
238 					Gradient
239 					(
240 						blocks,
241 						(short)(start + range * ((mbx * MACROBLOCK_WIDTH + bx * BLOCK_WIDTH) / (float)(INPUT_WIDTH - 1))),
242 						(short)(start + range * ((mbx * MACROBLOCK_WIDTH + bx * BLOCK_WIDTH + BLOCK_WIDTH - 1) / (float)(INPUT_WIDTH - 1))),
243 						1,
244 						intra_unsigned
245 					);
246 
247 					blocks += BLOCK_SIZE;
248 
249 					Gradient
250 					(
251 						blocks,
252 						(short)(start + range * ((mbx * MACROBLOCK_WIDTH + bx * BLOCK_WIDTH) / (float)(INPUT_WIDTH - 1))),
253 						(short)(start + range * ((mbx * MACROBLOCK_WIDTH + bx * BLOCK_WIDTH + BLOCK_WIDTH - 1) / (float)(INPUT_WIDTH - 1))),
254 						1,
255 						intra_unsigned
256 					);
257 
258 					blocks += BLOCK_SIZE;
259 				}
260 		}
261 
262 	XSelectInput(display, window, ExposureMask | KeyPressMask);
263 	XMapWindow(display, window);
264 	XSync(display, 0);
265 
266 	/* Test NULL context */
267 	assert(XvMCRenderSurface(display, NULL, XVMC_FRAME_PICTURE, &surface, NULL, NULL, 0, NUM_MACROBLOCKS, 0, &mb_array, &block_array) == XvMCBadContext);
268 	/* Test NULL surface */
269 	assert(XvMCRenderSurface(display, &context, XVMC_FRAME_PICTURE, NULL, NULL, NULL, 0, NUM_MACROBLOCKS, 0, &mb_array, &block_array) == XvMCBadSurface);
270 	/* Test bad picture structure */
271 	assert(XvMCRenderSurface(display, &context, 0, &surface, NULL, NULL, 0, NUM_MACROBLOCKS, 0, &mb_array, &block_array) == BadValue);
272 	/* Test valid params */
273 	assert(XvMCRenderSurface(display, &context, XVMC_FRAME_PICTURE, &surface, NULL, NULL, 0, NUM_MACROBLOCKS, 0, &mb_array, &block_array) == Success);
274 
275 	/* Test NULL surface */
276 	assert(XvMCPutSurface(display, NULL, window, 0, 0, INPUT_WIDTH, INPUT_HEIGHT, 0, 0, output_width, output_height, XVMC_FRAME_PICTURE) == XvMCBadSurface);
277 	/* Test bad window */
278 	/* XXX: X halts with a bad drawable for some reason, doesn't return BadDrawable as expected */
279 	/*assert(XvMCPutSurface(display, &surface, 0, 0, 0, width, height, 0, 0, width, height, XVMC_FRAME_PICTURE) == BadDrawable);*/
280 
281 	if (prompt)
282 	{
283 		puts("Press any button to quit...");
284 
285 		while (!quit)
286 		{
287 			if (XPending(display) > 0)
288 			{
289 				XEvent event;
290 
291 				XNextEvent(display, &event);
292 
293 				switch (event.type)
294 				{
295 					case Expose:
296 					{
297 						/* Test valid params */
298 						assert
299 						(
300 							XvMCPutSurface
301 							(
302 								display, &surface, window,
303 								0, 0, INPUT_WIDTH, INPUT_HEIGHT,
304 								0, 0, output_width, output_height,
305 								XVMC_FRAME_PICTURE
306 							) == Success
307 						);
308 						break;
309 					}
310 					case KeyPress:
311 					{
312 						quit = 1;
313 						break;
314 					}
315 				}
316 			}
317 		}
318 	}
319 
320 	assert(XvMCDestroyBlocks(display, &block_array) == Success);
321 	assert(XvMCDestroyMacroBlocks(display, &mb_array) == Success);
322 	assert(XvMCDestroySurface(display, &surface) == Success);
323 	assert(XvMCDestroyContext(display, &context) == Success);
324 
325 	XvUngrabPort(display, port_num, CurrentTime);
326 	XDestroyWindow(display, window);
327 	XCloseDisplay(display);
328 
329 	return 0;
330 }
331