blob: 656fdbb43e399654c0b88bbce82b3de75b31db63 [file] [log] [blame]
Joe Drago61e34422020-02-13 15:37:49 -08001// Copyright 2020 Joe Drago. All rights reserved.
2// SPDX-License-Identifier: BSD-2-Clause
3
4#include "testcase.h"
5
6#include "compare.h"
7#include "y4m.h"
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13static const char * choiceToString(avifCodecChoice choice)
14{
15 switch (choice) {
16 case AVIF_CODEC_CHOICE_AUTO:
17 return "auto";
18 case AVIF_CODEC_CHOICE_AOM:
19 return "aom";
20 case AVIF_CODEC_CHOICE_DAV1D:
21 return "dav1d";
wantehchang8d150b42020-02-21 14:08:38 -080022 case AVIF_CODEC_CHOICE_LIBGAV1:
23 return "libgav1";
Joe Drago61e34422020-02-13 15:37:49 -080024 case AVIF_CODEC_CHOICE_RAV1E:
25 return "rav1e";
26 }
27 return "unknown";
28}
29
30static avifCodecChoice stringToChoice(const char * str)
31{
32 if (!strcmp(str, "aom")) {
33 return AVIF_CODEC_CHOICE_AOM;
34 } else if (!strcmp(str, "dav1d")) {
35 return AVIF_CODEC_CHOICE_DAV1D;
wantehchang8d150b42020-02-21 14:08:38 -080036 } else if (!strcmp(str, "libgav1")) {
37 return AVIF_CODEC_CHOICE_LIBGAV1;
Joe Drago61e34422020-02-13 15:37:49 -080038 } else if (!strcmp(str, "rav1e")) {
39 return AVIF_CODEC_CHOICE_RAV1E;
40 }
41 return AVIF_CODEC_CHOICE_AUTO;
42}
43
Joe Dragoa7699ef2020-02-13 15:48:44 -080044TestCase * testCaseCreate(void)
Joe Drago61e34422020-02-13 15:37:49 -080045{
46 TestCase * tc = malloc(sizeof(TestCase));
47 memset(tc, 0, sizeof(TestCase));
48 return tc;
49}
50
51void testCaseDestroy(TestCase * tc)
52{
53 if (tc->name) {
54 free(tc->name);
55 }
56 if (tc->inputFilename) {
57 free(tc->inputFilename);
58 }
59 free(tc);
60}
61
62void testCaseSetInputFilename(TestCase * tc, const char * inputFilename)
63{
64 if (tc->inputFilename) {
65 free(tc->inputFilename);
66 }
67 tc->inputFilename = strdup(inputFilename);
68}
69
70void testCaseGenerateName(TestCase * tc)
71{
72 char basenameBuffer[1024];
73 if (tc->inputFilename) {
74 strcpy(basenameBuffer, tc->inputFilename);
75 char * dotLoc = strrchr(basenameBuffer, '.');
76 if (dotLoc) {
77 *dotLoc = 0;
78 }
79 } else {
80 basenameBuffer[0] = 0;
81 }
82
83 char nameBuffer[1024];
Joe Drago99a7ff52020-02-13 16:29:52 -080084 if (snprintf(nameBuffer,
85 sizeof(nameBuffer),
86 "%s_%s_to_%s_qp%d_%d_speed%d",
87 basenameBuffer,
88 choiceToString(tc->encodeChoice),
89 choiceToString(tc->decodeChoice),
90 tc->minQuantizer,
91 tc->maxQuantizer,
92 tc->speed) < 0) {
93 nameBuffer[0] = 0;
94 }
Joe Drago30d8c682020-02-13 16:18:36 -080095 nameBuffer[sizeof(nameBuffer) - 1] = 0;
Joe Drago61e34422020-02-13 15:37:49 -080096 if (tc->name) {
97 free(tc->name);
98 }
99 tc->name = strdup(nameBuffer);
100}
101
102static const char * jsonGetString(cJSON * parent, const char * key, const char * def)
103{
104 if (!parent || !cJSON_IsObject(parent)) {
105 return def;
106 }
107
108 cJSON * childItem = cJSON_GetObjectItem(parent, key);
109 if (!childItem || !cJSON_IsString(childItem)) {
110 return def;
111 }
112 return childItem->valuestring;
113}
114
115static int jsonGetInt(cJSON * parent, const char * key, int def)
116{
117 if (!parent || !cJSON_IsObject(parent)) {
118 return def;
119 }
120
121 cJSON * childItem = cJSON_GetObjectItem(parent, key);
122 if (!childItem || !cJSON_IsNumber(childItem)) {
123 return def;
124 }
125 return childItem->valueint;
126}
127
128static float jsonGetFloat(cJSON * parent, const char * key, float def)
129{
130 if (!parent || !cJSON_IsObject(parent)) {
131 return def;
132 }
133
134 cJSON * childItem = cJSON_GetObjectItem(parent, key);
135 if (!childItem || !cJSON_IsNumber(childItem)) {
136 return def;
137 }
138 return (float)childItem->valuedouble;
139}
140
141static avifBool jsonGetBool(cJSON * parent, const char * key, avifBool def)
142{
143 if (!parent || !cJSON_IsObject(parent)) {
144 return def;
145 }
146
147 cJSON * childItem = cJSON_GetObjectItem(parent, key);
148 if (!childItem || !cJSON_IsBool(childItem)) {
149 return def;
150 }
Joe Drago951a0022020-03-09 16:19:44 -0700151 return (childItem->type == cJSON_True);
Joe Drago61e34422020-02-13 15:37:49 -0800152}
153
154TestCase * testCaseFromJSON(cJSON * json)
155{
156 TestCase * tc = testCaseCreate();
157 tc->name = strdup(jsonGetString(json, "name", "unknown"));
158 tc->inputFilename = strdup(jsonGetString(json, "input", "unknown"));
159
160 tc->encodeChoice = stringToChoice(jsonGetString(json, "enc", "aom"));
161 tc->decodeChoice = stringToChoice(jsonGetString(json, "dec", "aom"));
162 tc->minQuantizer = jsonGetInt(json, "minQP", 0);
163 tc->maxQuantizer = jsonGetInt(json, "maxQP", 0);
164 tc->speed = jsonGetInt(json, "speed", 0);
165 tc->active = jsonGetBool(json, "active", AVIF_FALSE);
166
167 tc->maxThreshold = jsonGetInt(json, "max", 0);
168 tc->avgThreshold = jsonGetFloat(json, "avg", 0);
169 return tc;
170}
171
172cJSON * testCaseToJSON(TestCase * tc)
173{
174 cJSON * json = cJSON_CreateObject();
175
176 if (tc->name) {
177 cJSON_AddStringToObject(json, "name", tc->name);
178 }
179 if (tc->inputFilename) {
180 cJSON_AddStringToObject(json, "input", tc->inputFilename);
181 }
182
183 cJSON_AddStringToObject(json, "enc", choiceToString(tc->encodeChoice));
184 cJSON_AddStringToObject(json, "dec", choiceToString(tc->decodeChoice));
185 cJSON_AddNumberToObject(json, "minQP", tc->minQuantizer);
186 cJSON_AddNumberToObject(json, "maxQP", tc->maxQuantizer);
187 cJSON_AddNumberToObject(json, "speed", tc->speed);
188 cJSON_AddBoolToObject(json, "active", tc->active);
189
190 cJSON_AddNumberToObject(json, "max", tc->maxThreshold);
191 cJSON_AddNumberToObject(json, "avg", tc->avgThreshold);
192 return json;
193}
194
195int testCaseRun(TestCase * tc, const char * dataDir, avifBool generating)
196{
197 if (!tc->name || !tc->inputFilename) {
198 return AVIF_FALSE;
199 }
200
201 char y4mFilename[2048];
Joe Drago30d8c682020-02-13 16:18:36 -0800202 snprintf(y4mFilename, sizeof(y4mFilename), "%s/%s", dataDir, tc->inputFilename);
203 y4mFilename[sizeof(y4mFilename) - 1] = 0;
Joe Drago61e34422020-02-13 15:37:49 -0800204
205 avifImage * image = avifImageCreateEmpty();
206 if (!y4mRead(image, y4mFilename)) {
207 avifImageDestroy(image);
208 printf("ERROR[%s]: Can't read y4m: %s\n", tc->name, y4mFilename);
209 return AVIF_FALSE;
210 }
211
212 avifBool result = AVIF_TRUE;
213 avifRWData encodedData = AVIF_DATA_EMPTY;
214 avifEncoder * encoder = NULL;
215 avifDecoder * decoder = NULL;
216
217 encoder = avifEncoderCreate();
218 encoder->codecChoice = tc->encodeChoice;
219 encoder->maxThreads = 4; // TODO: pick something better here
220 if (avifEncoderWrite(encoder, image, &encodedData) != AVIF_RESULT_OK) {
221 printf("ERROR[%s]: Encode failed\n", tc->name);
222 result = AVIF_FALSE;
223 goto cleanup;
224 }
225
226 decoder = avifDecoderCreate();
227 decoder->codecChoice = tc->decodeChoice;
228 avifResult decodeResult = avifDecoderParse(decoder, (avifROData *)&encodedData);
229 if (decodeResult != AVIF_RESULT_OK) {
230 printf("ERROR[%s]: Decode failed\n", tc->name);
231 result = AVIF_FALSE;
232 goto cleanup;
233 }
234
235 avifResult nextImageResult = avifDecoderNextImage(decoder);
236 if (nextImageResult != AVIF_RESULT_OK) {
237 printf("ERROR[%s]: NextImage failed\n", tc->name);
238 result = AVIF_FALSE;
239 goto cleanup;
240 }
241
242 ImageComparison ic;
243 if (!compareYUVA(&ic, image, decoder->image)) {
244 printf("ERROR[%s]: compare bailed out\n", tc->name);
245 result = AVIF_FALSE;
246 goto cleanup;
247 }
248
249 if (generating) {
250 int maxThreshold = ic.maxDiff;
251 if (maxThreshold > 0) {
252 // Not lossless, give one more codepoint of wiggle room
253 ++maxThreshold;
254 }
255 float avgThreshold = ic.avgDiff + 0.25f;
256
257 tc->maxThreshold = maxThreshold;
258 tc->avgThreshold = avgThreshold;
259 printf("Generated[%s]: Thresholds - Max %d, Avg %f\n", tc->name, tc->maxThreshold, tc->avgThreshold);
260 } else {
261 if (ic.maxDiff > tc->maxThreshold) {
262 printf("ERROR[%s]: max diff threshold exceeded: %d > %d\n", tc->name, ic.maxDiff, tc->maxThreshold);
263 result = AVIF_FALSE;
264 goto cleanup;
265 }
266 if (ic.avgDiff > tc->avgThreshold) {
267 printf("FAILED[%s]: avg diff threshold exceeded: %f > %f\n", tc->name, ic.avgDiff, tc->avgThreshold);
268 result = AVIF_FALSE;
269 goto cleanup;
270 }
271 printf("OK[%s]\n", tc->name);
272 }
273
274cleanup:
275 if (decoder) {
276 avifDecoderDestroy(decoder);
277 }
278 avifRWDataFree(&encodedData);
279 if (encoder) {
280 avifEncoderDestroy(encoder);
281 }
282 avifImageDestroy(image);
283 (void)generating;
284 return result;
285}