Coverage Report

Created: 2023-11-19 06:24

/src/gpac/modules/validator/validator.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Cyril Concolato, Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2010-2023
6
 *      All rights reserved
7
 *
8
 *  This file is part of GPAC / Test Suite Validator Recorder sub-project
9
 *
10
 *  GPAC is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU Lesser General Public License as published by
12
 *  the Free Software Foundation; either version 2, or (at your option)
13
 *  any later version.
14
 *
15
 *  GPAC is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU Lesser General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU Lesser General Public
21
 *  License along with this library; see the file COPYING.  If not, write to
22
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
 *
24
 */
25
26
#include <gpac/modules/compositor_ext.h>
27
#include <gpac/filters.h>
28
#include <gpac/internal/compositor_dev.h>
29
#include <gpac/internal/media_dev.h>
30
#include <gpac/xml.h>
31
32
#ifndef GPAC_DISABLE_COMPOSITOR
33
34
typedef struct __validation_module
35
{
36
  GF_Compositor *compositor;
37
38
  Bool is_recording;
39
  Bool trace_mode;
40
41
  /* Clock used to synchronize events in recording and playback*/
42
  GF_ObjectManager *root_odm;
43
44
  /* Next event to process */
45
  Bool next_event_snapshot;
46
  GF_Event next_event;
47
  u32 xvs_event_index;
48
  u32 next_time;
49
  Bool evt_loaded;
50
51
  GF_VideoListener video_listener;
52
53
  /* XML Validation List (the list of files to be tested) */
54
  char *xvl_filename;
55
  GF_DOMParser *xvl_parser;
56
  GF_XMLNode *xvl_node;
57
  GF_XMLNode *xvs_node_in_xvl;
58
  u32 xvl_node_index;
59
60
  /* Pointer to the current validation script file being tested */
61
  char *xvs_filename;
62
  GF_DOMParser *xvs_parser;
63
  GF_XMLNode *xvs_node;
64
    Bool xvs_result;
65
    Bool owns_root;
66
67
  /* test sequence */
68
  char *test_base;
69
  char *test_filename;
70
71
  Bool snapshot_next_frame;
72
  u32 snapshot_number;
73
74
  GF_FSEventListener evt_filter;
75
} GF_Validator;
76
77
static void validator_xvs_close(GF_Validator *validator);
78
static Bool validator_xvs_next(GF_Validator *validator, Bool reverse);
79
80
#ifndef GPAC_DISABLE_AV_PARSERS
81
82
static void validator_xvs_add_snapshot_node(GF_Validator *validator, const char *filename, u32 scene_time)
83
0
{
84
0
  GF_XMLNode *snap_node;
85
0
  GF_XMLAttribute *att;
86
0
  GF_SAFEALLOC(snap_node, GF_XMLNode);
87
0
  if (!snap_node) {
88
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate snapshot\n"));
89
0
    return;
90
0
  }
91
0
  snap_node->name = gf_strdup("snapshot");
92
0
  snap_node->attributes = gf_list_new();
93
94
0
  att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
95
0
  if (!att) {
96
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate snapshot\n"));
97
0
    return;
98
0
  }
99
0
  att->name = gf_strdup("time");
100
0
  att->value = (char*)gf_malloc(100);
101
0
  sprintf(att->value, "%d", scene_time);
102
0
  gf_list_add(snap_node->attributes, att);
103
  
104
0
  att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
105
0
  if (!att) {
106
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate snapshot\n"));
107
0
    return;
108
0
  }
109
0
  att->name = gf_strdup("image");
110
0
  att->value = gf_strdup(filename);
111
0
  gf_list_add(snap_node->attributes, att);
112
0
  gf_list_add(validator->xvs_node->content, snap_node);
113
114
  /* adding an extra text node for line break in serialization */
115
0
  GF_SAFEALLOC(snap_node, GF_XMLNode);
116
0
  if (!snap_node) {
117
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate snapshot\n"));
118
0
    return;
119
0
  }
120
0
  snap_node->type = GF_XML_TEXT_TYPE;
121
0
  snap_node->name = gf_strdup("\n");
122
0
  gf_list_add(validator->xvs_node->content, snap_node);
123
0
}
124
125
static char *validator_get_snapshot_name(GF_Validator *validator, Bool is_reference, u32 number)
126
0
{
127
0
  char *name = validator->test_filename ? validator->test_filename : validator->xvs_filename;
128
0
  char *dot;
129
0
  char dumpname[GF_MAX_PATH];
130
0
  dot = gf_file_ext_start(name);
131
0
  dot[0] = 0;
132
0
  sprintf(dumpname, "%s-%s-%03d.png", name, (is_reference?"reference":"newest"), number);
133
0
  dot[0] = '.';
134
0
  return gf_strdup(dumpname);
135
0
}
136
137
static char *validator_create_snapshot(GF_Validator *validator)
138
0
{
139
0
  GF_Err e;
140
0
  GF_VideoSurface fb;
141
0
  GF_Compositor *compositor = validator->compositor;
142
0
  char *dumpname;
143
144
0
  dumpname = validator_get_snapshot_name(validator, validator->is_recording, validator->snapshot_number);
145
146
0
  e = gf_sc_get_screen_buffer(compositor, &fb, GF_SC_GRAB_DEPTH_NONE);
147
0
  if (e) {
148
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Error dumping screen buffer %s\n", gf_error_to_string(e)));
149
0
  } else {
150
0
    u32 dst_size = fb.width*fb.height*3;
151
0
    char *dst = (char*)gf_malloc(sizeof(char)*dst_size);
152
0
    if (!dst) e = GF_OUT_OF_MEM;
153
154
0
    if (!e)
155
0
      e = gf_img_png_enc(fb.video_buffer, fb.width, fb.height, fb.pitch_y, fb.pixel_format, dst, &dst_size);
156
157
0
    if (!e) {
158
0
      FILE *png = gf_fopen(dumpname, "wb");
159
0
      if (!png) {
160
0
        e = GF_IO_ERR;
161
0
      } else {
162
0
        if (gf_fwrite(dst, dst_size, png)!=dst_size) e = GF_IO_ERR;
163
0
        gf_fclose(png);
164
0
      }
165
0
    }
166
0
    if (dst) gf_free(dst);
167
0
    if (e) {
168
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Error encoding PNG to %s: %s\n", dumpname, gf_error_to_string(e)));
169
0
    } else {
170
0
      GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[Validator] Writing file %s\n", dumpname));
171
0
    }
172
0
    gf_sc_release_screen_buffer(compositor, &fb);
173
0
  }
174
0
  validator->snapshot_number++;
175
0
  return dumpname;
176
0
}
177
178
static GF_Err validator_file_dec(char *png_filename, u32 *hint_codecid, u32 *width, u32 *height, u32 *pixel_format, char **dst, u32 *dst_size)
179
0
{
180
0
  u32 fsize, codecid;
181
0
  char *data;
182
0
  GF_Err e;
183
184
0
  codecid = 0;
185
0
  if (!hint_codecid || ! *hint_codecid) {
186
0
    char *ext = gf_file_ext_start(png_filename);
187
0
    if (!ext) return GF_NOT_SUPPORTED;
188
0
    if (!stricmp(ext, ".png")) codecid = GF_CODECID_PNG;
189
0
    else if (!stricmp(ext, ".jpg") || !stricmp(ext, ".jpeg")) codecid = GF_CODECID_JPEG;
190
0
  } else {
191
0
    codecid = *hint_codecid;
192
0
  }
193
194
0
  e = gf_file_load_data(png_filename, (u8 **)&data, &fsize);
195
0
  if (e) return e;
196
197
0
  e = GF_NOT_SUPPORTED;
198
0
  *dst_size = 0;
199
0
  if (codecid == GF_CODECID_JPEG) {
200
#ifdef GPAC_HAS_JPEG
201
    e = gf_img_jpeg_dec(data, fsize, width, height, pixel_format, NULL, dst_size, 0);
202
    if (*dst_size) {
203
      *dst = gf_malloc(*dst_size);
204
      return gf_img_jpeg_dec(data, fsize, width, height, pixel_format, *dst, dst_size, 0);
205
    }
206
#endif
207
0
  } else if (codecid == GF_CODECID_PNG) {
208
#ifdef GPAC_HAS_PNG
209
    e = gf_img_png_dec(data, fsize, width, height, pixel_format, NULL, dst_size);
210
    if (*dst_size) {
211
      *dst = gf_malloc(*dst_size);
212
      return gf_img_png_dec(data, fsize, width, height, pixel_format, *dst, dst_size);
213
    }
214
#endif
215
0
  }
216
0
  return e;
217
0
}
218
219
static Bool validator_compare_snapshots(GF_Validator *validator)
220
0
{
221
0
  char *ref_name, *new_name;
222
0
  u32 ref_width, ref_height, ref_pixel_format, ref_data_size;
223
0
  u32 new_width, new_height, new_pixel_format, new_data_size;
224
0
  char *ref_data=NULL, *new_data=NULL;
225
0
  Bool result = GF_FALSE;
226
0
  GF_Err e;
227
0
  u32 i;
228
229
0
  u32 snap_number = validator->snapshot_number - 1;
230
0
  ref_name = validator_get_snapshot_name(validator, GF_TRUE, snap_number);
231
0
  new_name = validator_get_snapshot_name(validator, GF_FALSE, snap_number);
232
233
0
  e = validator_file_dec(ref_name, NULL, &ref_width, &ref_height, &ref_pixel_format, &ref_data, &ref_data_size);
234
0
  if (e) {
235
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Cannot decode PNG file %s\n", ref_name));
236
0
    goto end;
237
0
  }
238
0
  e = validator_file_dec(new_name, NULL, &new_width, &new_height, &new_pixel_format, &new_data, &new_data_size);
239
0
  if (e) {
240
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Cannot decode PNG file %s\n", new_name));
241
0
    goto end;
242
0
  }
243
0
  if (!ref_data) ref_data_size = 0;
244
0
  if (!new_data) new_data_size = 0;
245
246
0
  if (ref_width != new_width) {
247
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different widths: %d vs %d\n", ref_width, new_width));
248
0
    goto end;
249
0
  }
250
0
  if (ref_height != new_height) {
251
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different heights: %d vs %d\n", ref_height, new_height));
252
0
    goto end;
253
0
  }
254
0
  if (ref_pixel_format != new_pixel_format) {
255
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different pixel formats: %d vs %d\n", ref_pixel_format, new_pixel_format));
256
0
    goto end;
257
0
  }
258
0
  if (ref_data_size != new_data_size) {
259
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different pixel formats: %d vs %d\n", ref_pixel_format, new_pixel_format));
260
0
    goto end;
261
0
  }
262
263
0
  for (i = 0; i<ref_data_size; i++) {
264
0
    if (ref_data[i] != new_data[i]) {
265
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different pixel values at position %d: %d vs %d\n", i, ref_data[i], new_data[i]));
266
0
      break;
267
0
    }
268
0
  }
269
0
  if (i==ref_data_size) result = GF_TRUE;
270
271
0
end:
272
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Validator] PNG Comparison result: %s\n", (result?"Same":"Different")));
273
0
  if (ref_name) gf_free(ref_name);
274
0
  if (new_name) gf_free(new_name);
275
0
  if (ref_data) gf_free(ref_data);
276
0
  if (new_data) gf_free(new_data);
277
0
  return result;
278
0
}
279
#endif
280
281
static void validator_on_video_frame(void *udta, u32 time)
282
0
{
283
0
  GF_Validator *validator = (GF_Validator *)udta;
284
0
  if (validator->snapshot_next_frame) {
285
0
#ifndef GPAC_DISABLE_AV_PARSERS
286
0
    char *snap_name = validator_create_snapshot(validator);
287
0
    validator_xvs_add_snapshot_node(validator, snap_name, gf_clock_time(validator->root_odm->ck));
288
0
    gf_free(snap_name);
289
0
#endif
290
0
    validator->snapshot_next_frame = GF_FALSE;
291
0
  }
292
0
}
293
294
static void validator_on_video_reconfig(void *udta, u32 width, u32 height, u8 bpp)
295
0
{
296
0
}
297
298
Bool validator_on_event_play(void *udta, GF_Event *event, Bool consumed_by_compositor)
299
0
{
300
0
  GF_Validator *validator = (GF_Validator *)udta;
301
0
  switch (event->type) {
302
0
  case GF_EVENT_CONNECT:
303
0
    if (event->connect.is_connected) {
304
0
      if (!validator->trace_mode) {
305
//deprecated        gf_sc_add_video_listener(validator->compositor, &validator->video_listener);
306
0
      }
307
308
0
      validator->root_odm = validator->compositor->root_scene->root_od;
309
0
    }
310
0
    break;
311
0
  case GF_EVENT_CLICK:
312
0
  case GF_EVENT_MOUSEUP:
313
0
  case GF_EVENT_MOUSEDOWN:
314
0
  case GF_EVENT_MOUSEOVER:
315
0
  case GF_EVENT_MOUSEOUT:
316
0
  case GF_EVENT_MOUSEMOVE:
317
0
  case GF_EVENT_MOUSEWHEEL:
318
0
  case GF_EVENT_KEYDOWN:
319
0
  case GF_EVENT_TEXTINPUT:
320
0
    return GF_TRUE;
321
0
  case GF_EVENT_KEYUP:
322
0
    if ((event->key.key_code == GF_KEY_END)&&(event->key.flags & GF_KEY_MOD_CTRL)) {
323
0
      GF_Event evt;
324
0
      memset(&evt, 0, sizeof(GF_Event));
325
0
      evt.type = GF_EVENT_QUIT;
326
0
      gf_sc_on_event(validator->compositor, &evt);
327
0
    }
328
0
    return GF_TRUE;
329
0
  }
330
0
  return GF_FALSE;
331
0
}
332
333
static void validator_xvs_add_event_dom(GF_Validator *validator, GF_Event *event)
334
0
{
335
0
  GF_XMLNode *evt_node;
336
0
  GF_XMLAttribute *att;
337
338
0
  GF_SAFEALLOC(evt_node, GF_XMLNode);
339
0
  if (!evt_node) {
340
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event\n"));
341
0
    return;
342
0
  }
343
344
0
  switch (event->type) {
345
0
  case GF_EVENT_CLICK:
346
0
  case GF_EVENT_MOUSEUP:
347
0
  case GF_EVENT_MOUSEDOWN:
348
0
  case GF_EVENT_MOUSEOVER:
349
0
  case GF_EVENT_MOUSEOUT:
350
0
  case GF_EVENT_MOUSEMOVE:
351
0
  case GF_EVENT_MOUSEWHEEL:
352
0
  case GF_EVENT_KEYUP:
353
0
  case GF_EVENT_KEYDOWN:
354
0
  case GF_EVENT_TEXTINPUT:
355
0
#ifndef GPAC_DISABLE_SVG
356
0
    evt_node->name = gf_strdup(gf_dom_event_get_name(event->type));
357
0
#endif
358
0
    break;
359
0
  }
360
361
0
  if (!evt_node->name) {
362
0
    gf_free(evt_node);
363
0
    return;
364
0
  }
365
366
0
  evt_node->attributes = gf_list_new();
367
368
0
  att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
369
0
  if (!att) {
370
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event time\n"));
371
0
    return;
372
0
  }
373
0
  att->name = gf_strdup("time");
374
0
  att->value = (char*)gf_malloc(100);
375
0
  sprintf(att->value, "%f", gf_scene_get_time(validator->compositor->root_scene)*1000);
376
0
  gf_list_add(evt_node->attributes, att);
377
378
0
  switch (event->type) {
379
0
  case GF_EVENT_CLICK:
380
0
  case GF_EVENT_MOUSEUP:
381
0
  case GF_EVENT_MOUSEDOWN:
382
0
  case GF_EVENT_MOUSEOVER:
383
0
  case GF_EVENT_MOUSEOUT:
384
0
  case GF_EVENT_MOUSEMOVE:
385
0
  case GF_EVENT_MOUSEWHEEL:
386
0
    if (event->type == GF_EVENT_MOUSEDOWN || event->type == GF_EVENT_MOUSEUP) {
387
0
      att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
388
0
      if (!att) {
389
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
390
0
        return;
391
0
      }
392
0
      att->name = gf_strdup("button");
393
0
      switch (event->mouse.button) {
394
0
      case 0:
395
0
        att->value = gf_strdup("Left");
396
0
        break;
397
0
      case 1:
398
0
        att->value = gf_strdup("Middle");
399
0
        break;
400
0
      case 2:
401
0
        att->value = gf_strdup("Right");
402
0
        break;
403
0
      }
404
0
      gf_list_add(evt_node->attributes, att);
405
0
    }
406
0
    att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
407
0
    if (!att) {
408
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
409
0
      return;
410
0
    }
411
0
    att->name = gf_strdup("x");
412
0
    att->value = (char*)gf_malloc(100);
413
0
    sprintf(att->value, "%d", event->mouse.x);
414
0
    gf_list_add(evt_node->attributes, att);
415
416
0
    att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
417
0
    if (!att) {
418
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
419
0
      return;
420
0
    }
421
0
    att->name = gf_strdup("y");
422
0
    att->value = (char*)gf_malloc(100);
423
0
    sprintf(att->value, "%d", event->mouse.y);
424
0
    gf_list_add(evt_node->attributes, att);
425
0
    if (event->type == GF_EVENT_MOUSEWHEEL) {
426
0
      att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
427
0
      if (!att) {
428
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
429
0
        return;
430
0
      }
431
0
      att->name = gf_strdup("wheel_pos");
432
0
      att->value = (char*)gf_malloc(100);
433
0
      sprintf(att->value, "%f", FIX2FLT( event->mouse.wheel_pos) );
434
0
      gf_list_add(evt_node->attributes, att);
435
0
    }
436
0
    if (event->mouse.key_states & GF_KEY_MOD_SHIFT) {
437
0
      att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
438
0
      if (!att) {
439
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
440
0
        return;
441
0
      }
442
0
      att->name = gf_strdup("shift");
443
0
      att->value = gf_strdup("true");
444
0
      gf_list_add(evt_node->attributes, att);
445
0
    }
446
0
    if (event->mouse.key_states & GF_KEY_MOD_CTRL) {
447
0
      att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
448
0
      if (!att) {
449
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
450
0
        return;
451
0
      }
452
0
      att->name = gf_strdup("ctrl");
453
0
      att->value = gf_strdup("true");
454
0
      gf_list_add(evt_node->attributes, att);
455
0
    }
456
0
    if (event->mouse.key_states & GF_KEY_MOD_ALT) {
457
0
      att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
458
0
      if (!att) {
459
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
460
0
        return;
461
0
      }
462
0
      att->name = gf_strdup("alt");
463
0
      att->value = gf_strdup("true");
464
0
      gf_list_add(evt_node->attributes, att);
465
0
    }
466
0
    break;
467
  /*Key Events*/
468
0
  case GF_EVENT_KEYUP:
469
0
  case GF_EVENT_KEYDOWN:
470
0
  case GF_EVENT_LONGKEYPRESS:
471
0
    att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
472
0
    if (!att) {
473
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
474
0
      return;
475
0
    }
476
0
    att->name = gf_strdup("key_identifier");
477
0
#ifndef GPAC_DISABLE_SVG
478
0
    att->value = gf_strdup(gf_dom_get_key_name(event->key.key_code));
479
0
#endif
480
0
    gf_list_add(evt_node->attributes, att);
481
0
    if (event->key.flags & GF_KEY_MOD_SHIFT) {
482
0
      att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
483
0
      if (!att) {
484
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
485
0
        return;
486
0
      }
487
0
      att->name = gf_strdup("shift");
488
0
      att->value = gf_strdup("true");
489
0
      gf_list_add(evt_node->attributes, att);
490
0
    }
491
0
    if (event->key.flags & GF_KEY_MOD_CTRL) {
492
0
      att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
493
0
      if (!att) {
494
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
495
0
        return;
496
0
      }
497
0
      att->name = gf_strdup("ctrl");
498
0
      att->value = gf_strdup("true");
499
0
      gf_list_add(evt_node->attributes, att);
500
0
    }
501
0
    if (event->key.flags & GF_KEY_MOD_ALT) {
502
0
      att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
503
0
      if (!att) {
504
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
505
0
        return;
506
0
      }
507
0
      att->name = gf_strdup("alt");
508
0
      att->value = gf_strdup("true");
509
0
      gf_list_add(evt_node->attributes, att);
510
0
    }
511
0
    break;
512
0
  case GF_EVENT_TEXTINPUT:
513
0
    att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
514
0
    if (!att) {
515
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
516
0
      return;
517
0
    }
518
0
    att->name = gf_strdup("unicode-char");
519
0
    att->value = (char*)gf_malloc(100);
520
0
    sprintf(att->value, "%d", event->character.unicode_char);
521
0
    gf_list_add(evt_node->attributes, att);
522
0
    break;
523
0
  }
524
0
  gf_list_add(validator->xvs_node->content, evt_node);
525
  /* adding an extra text node for line break in serialization */
526
0
  GF_SAFEALLOC(evt_node, GF_XMLNode);
527
0
  if (!evt_node) {
528
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event\n"));
529
0
    return;
530
0
  }
531
0
  evt_node->type = GF_XML_TEXT_TYPE;
532
0
  evt_node->name = gf_strdup("\n");
533
0
  gf_list_add(validator->xvs_node->content, evt_node);
534
0
}
535
536
Bool validator_on_event_record(void *udta, GF_Event *event, Bool consumed_by_compositor)
537
0
{
538
0
  GF_Validator *validator = (GF_Validator *)udta;
539
0
  Bool rec_event = GF_TRUE;
540
0
  switch (event->type) {
541
0
  case GF_EVENT_CONNECT:
542
0
    if (event->connect.is_connected) {
543
0
      if (!validator->trace_mode) {
544
//deprecated        gf_sc_add_video_listener(validator->compositor, &validator->video_listener);
545
0
      }
546
0
      validator->root_odm = validator->compositor->root_scene->root_od;
547
0
    }
548
0
    break;
549
0
  case GF_EVENT_KEYDOWN:
550
0
    if (event->key.key_code == GF_KEY_INSERT) {
551
0
      rec_event = GF_FALSE;
552
0
    } else if (event->key.key_code == GF_KEY_PAGEDOWN) {
553
0
      rec_event = GF_FALSE;
554
0
    } else if (event->key.key_code == GF_KEY_PAGEUP) {
555
0
      rec_event = GF_FALSE;
556
0
    } else if (event->key.key_code == GF_KEY_END) {
557
0
      rec_event = GF_FALSE;
558
0
    } else if (event->key.key_code == GF_KEY_CONTROL) {
559
0
      rec_event = GF_FALSE;
560
0
    } else if (event->key.flags & GF_KEY_MOD_CTRL) {
561
0
      rec_event = GF_FALSE;
562
0
    }
563
0
    break;
564
0
  case GF_EVENT_KEYUP:
565
0
    if (event->key.flags & GF_KEY_MOD_CTRL) {
566
0
      rec_event = GF_FALSE;
567
0
      if (event->key.key_code == GF_KEY_INSERT) {
568
0
#ifndef GPAC_DISABLE_AV_PARSERS
569
0
        char *snap_name = validator_create_snapshot(validator);
570
0
        validator_xvs_add_snapshot_node(validator, snap_name, gf_clock_time(validator->root_odm->ck));
571
0
        gf_free(snap_name);
572
0
#endif
573
0
      } else if (event->key.key_code == GF_KEY_END) {
574
0
        GF_Event evt;
575
0
        memset(&evt, 0, sizeof(GF_Event));
576
0
        evt.type = GF_EVENT_QUIT;
577
0
        gf_sc_on_event(validator->compositor, &evt);
578
0
      } else if (event->key.key_code == GF_KEY_F1) {
579
0
        validator->snapshot_next_frame = GF_TRUE;
580
0
      }
581
0
    } else if (event->key.key_code == GF_KEY_PAGEDOWN) {
582
0
      rec_event = GF_FALSE;
583
0
      validator_xvs_close(validator);
584
0
      gf_sc_disconnect(validator->compositor);
585
//deprecated      gf_sc_remove_video_listener(validator->compositor, &validator->video_listener);
586
0
      validator_xvs_next(validator, GF_FALSE);
587
0
    } else if (event->key.key_code == GF_KEY_PAGEUP) {
588
0
      rec_event = GF_FALSE;
589
0
      validator_xvs_close(validator);
590
0
      gf_sc_disconnect(validator->compositor);
591
//deprecated      gf_sc_remove_video_listener(validator->compositor, &validator->video_listener);
592
0
      validator_xvs_next(validator, GF_TRUE);
593
0
    } else if (event->key.key_code == GF_KEY_CONTROL) {
594
0
      rec_event = GF_FALSE;
595
0
    }
596
0
    break;
597
0
  }
598
0
  if (rec_event) {
599
0
    validator_xvs_add_event_dom(validator, event);
600
0
  }
601
0
  return GF_FALSE;
602
0
}
603
604
static void validator_xvl_open(GF_Validator *validator)
605
0
{
606
0
  GF_Err e;
607
0
  u32 att_index;
608
0
  validator->xvl_parser = gf_xml_dom_new();
609
0
  e = gf_xml_dom_parse(validator->xvl_parser, validator->xvl_filename, NULL, NULL);
610
0
  if (e != GF_OK) {
611
0
    gf_xml_dom_del(validator->xvl_parser);
612
0
    validator->xvl_parser = NULL;
613
0
    return;
614
0
  }
615
0
  validator->xvl_node = gf_xml_dom_get_root(validator->xvl_parser);
616
0
  if (!validator->xvl_node) {
617
0
    gf_xml_dom_del(validator->xvl_parser);
618
0
    validator->xvl_parser = NULL;
619
0
    return;
620
0
  }
621
0
  att_index = 0;
622
0
  while (1) {
623
0
    GF_XMLAttribute *att = (GF_XMLAttribute*)gf_list_get(validator->xvl_node->attributes, att_index);
624
0
    if (!att) break;
625
0
    if (!strcmp(att->name, "content-base")) {
626
0
      validator->test_base = gf_strdup(att->value);
627
0
    }
628
0
    att_index++;
629
0
  }
630
0
}
631
632
static void validator_xvl_close(GF_Validator *validator)
633
0
{
634
0
  if (validator->xvl_parser) {
635
    /* writing the validation results */
636
0
    if (!validator->is_recording) {
637
0
      FILE *xvl_fp;
638
0
      char *xvl_content;
639
0
      char result_filename[GF_MAX_PATH];
640
0
      char *dot;
641
0
      xvl_content = gf_xml_dom_serialize(validator->xvl_node, GF_FALSE, GF_FALSE);
642
0
      dot = gf_file_ext_start(validator->xvl_filename);
643
0
      dot[0] = 0;
644
0
      sprintf(result_filename, "%s-result.xml", validator->xvl_filename);
645
0
      dot[0] = '.';
646
0
      xvl_fp = gf_fopen(result_filename, "wt");
647
0
      gf_fwrite(xvl_content, strlen(xvl_content), xvl_fp);
648
0
      gf_fclose(xvl_fp);
649
0
      gf_free(xvl_content);
650
0
    }
651
0
    gf_xml_dom_del(validator->xvl_parser);
652
0
    validator->xvl_parser = NULL;
653
0
    validator->xvl_filename = NULL;
654
0
  }
655
0
}
656
657
static void validator_xvl_get_next_xvs(GF_Validator *validator, Bool reverse)
658
0
{
659
0
  u32 xvl_att_index;
660
0
  validator->xvs_node = NULL;
661
0
  validator->xvs_filename = NULL;
662
0
  validator->test_filename = NULL;
663
0
  while (1) {
664
0
    validator->xvs_node_in_xvl = (GF_XMLNode*)gf_list_get(validator->xvl_node->content, validator->xvl_node_index);
665
0
    if (!validator->xvs_node_in_xvl) {
666
0
      return;
667
0
    }
668
0
    if (validator->xvs_node_in_xvl->type != GF_XML_NODE_TYPE) {
669
0
      if (!reverse) validator->xvl_node_index++;
670
0
      else validator->xvl_node_index--;
671
0
      continue;
672
0
    }
673
0
    xvl_att_index = 0;
674
0
    while(1) {
675
0
      GF_XMLAttribute *att = (GF_XMLAttribute*)gf_list_get(validator->xvs_node_in_xvl->attributes, xvl_att_index);
676
0
      if (!att) break;
677
0
      if (!strcmp(att->name, "scenario")) {
678
0
        validator->xvs_filename = att->value;
679
0
      } else if (!strcmp(att->name, "content")) {
680
0
        validator->test_filename = att->value;
681
0
      }
682
0
      xvl_att_index++;
683
0
    }
684
0
    if (!reverse) validator->xvl_node_index++;
685
0
    else validator->xvl_node_index--;
686
0
    break;
687
0
  }
688
0
}
689
690
static Bool validator_xvs_open(GF_Validator *validator)
691
0
{
692
0
  GF_Err e;
693
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Validator] Opening Validation Script: %s\n", validator->xvs_filename));
694
0
  validator->snapshot_number = 0;
695
0
  validator->xvs_parser = gf_xml_dom_new();
696
0
  e = gf_xml_dom_parse(validator->xvs_parser, validator->xvs_filename, NULL, NULL);
697
0
  if (e != GF_OK) {
698
0
    if (validator->is_recording) {
699
0
      GF_SAFEALLOC(validator->xvs_node, GF_XMLNode);
700
0
      if (!validator->xvs_node) {
701
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate root node\n"));
702
0
        return 0;
703
0
      }
704
      
705
0
      validator->xvs_node->name = gf_strdup("TestValidationScript");
706
0
      validator->xvs_node->attributes = gf_list_new();
707
0
            validator->xvs_node->content = gf_list_new();
708
0
            validator->owns_root = GF_TRUE;
709
0
    } else {
710
0
      gf_xml_dom_del(validator->xvs_parser);
711
0
      validator->xvs_parser = NULL;
712
0
      return GF_FALSE;
713
0
    }
714
0
  } else {
715
0
    validator->xvs_node = gf_xml_dom_get_root(validator->xvs_parser);
716
0
  }
717
  /* Get the file name from the XVS if not found in the XVL */
718
0
  if (!validator->test_filename) {
719
0
    GF_XMLAttribute *att;
720
0
    GF_XMLAttribute *att_file;
721
0
    u32 att_index = 0;
722
0
    att_file = NULL;
723
0
    while (1) {
724
0
      att = (GF_XMLAttribute*)gf_list_get(validator->xvs_node->attributes, att_index);
725
0
      if (!att) {
726
0
        break;
727
0
      } else if (!strcmp(att->name, "file")) {
728
0
        att_file = att;
729
0
      }
730
0
      att_index++;
731
0
    }
732
733
0
    if (!att_file) {
734
0
      gf_xml_dom_del(validator->xvs_parser);
735
0
      validator->xvs_parser = NULL;
736
0
      validator->xvs_node = NULL;
737
0
      return GF_FALSE;
738
0
    } else {
739
0
      char *sep;
740
0
      sep = strrchr(att_file->value, GF_PATH_SEPARATOR);
741
0
      if (!sep) {
742
0
        validator->test_filename = att_file->value;
743
0
      } else {
744
0
        sep[0] = 0;
745
0
        validator->test_base = gf_strdup(att_file->value);
746
0
        sep[0] = GF_PATH_SEPARATOR;
747
0
        validator->test_filename = sep+1;
748
0
      }
749
0
    }
750
0
  }
751
0
  if (validator->is_recording) {
752
0
    GF_XMLNode *node;
753
    /* Removing prerecorded interactions */
754
0
    while (gf_list_count(validator->xvs_node->content)) {
755
0
      GF_XMLNode *child = (GF_XMLNode *)gf_list_last(validator->xvs_node->content);
756
0
      gf_list_rem_last(validator->xvs_node->content);
757
0
      gf_xml_dom_node_del(child);
758
0
    }
759
    /* adding an extra text node for line break in serialization */
760
0
    GF_SAFEALLOC(node, GF_XMLNode);
761
0
    if (!node) {
762
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate node\n"));
763
0
      return GF_FALSE;
764
0
    }
765
0
    node->type = GF_XML_TEXT_TYPE;
766
0
    node->name = gf_strdup("\n");
767
0
    gf_list_add(validator->xvs_node->content, node);
768
0
  } else {
769
0
    validator->xvs_result = GF_TRUE;
770
0
  }
771
0
  return GF_TRUE;
772
0
}
773
774
static void validator_xvs_close(GF_Validator *validator)
775
0
{
776
0
  if (validator->xvs_parser) {
777
0
    if (validator->is_recording) {
778
0
      FILE *xvs_fp;
779
0
      char *xvs_content;
780
0
      GF_XMLAttribute *att_file = NULL;
781
0
      u32 att_index = 0;
782
            
783
0
            if (!validator->trace_mode) {
784
0
        GF_XMLAttribute *att;
785
0
                while (1) {
786
0
                    att = (GF_XMLAttribute*)gf_list_get(validator->xvs_node->attributes, att_index);
787
0
                    if (!att) {
788
0
                        break;
789
0
                    } else if (!strcmp(att->name, "file")) {
790
0
                        att_file = att;
791
0
                    }
792
0
                    att_index++;
793
0
                }
794
795
0
                if (!att_file) {
796
0
                    GF_SAFEALLOC(att, GF_XMLAttribute);
797
0
          if (!att) {
798
0
            GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate file attribute\n"));
799
0
            return;
800
0
          }
801
0
                    att->name = gf_strdup("file");
802
0
                    gf_list_add(validator->xvs_node->attributes, att);
803
0
                } else {
804
0
                    att = att_file;
805
0
                    if (att->value) gf_free(att->value);
806
0
                }
807
0
                if (validator->test_base) {
808
0
          char filename[100];
809
0
                    sprintf(filename, "%s%c%s", validator->test_base, GF_PATH_SEPARATOR, validator->test_filename);
810
0
                    att->value = gf_strdup(filename);
811
0
                } else {
812
0
                    att->value = gf_strdup(validator->test_filename);
813
0
                }
814
0
            }
815
0
      xvs_content = gf_xml_dom_serialize(validator->xvs_node, GF_FALSE, GF_FALSE);
816
0
      xvs_fp = gf_fopen(validator->xvs_filename, "wt");
817
0
      gf_fwrite(xvs_content, strlen(xvs_content), xvs_fp);
818
0
      gf_fclose(xvs_fp);
819
0
      gf_free(xvs_content);
820
0
            if (validator->owns_root)
821
0
                gf_xml_dom_node_del(validator->xvs_node);
822
0
    } else {
823
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Validator] XVS Result : %s\n", (validator->xvs_result?"Success":"Failure")));
824
0
      if (validator->xvs_node_in_xvl) {
825
0
        GF_XMLAttribute *att;
826
0
        GF_XMLAttribute *att_result = NULL;
827
0
        u32 att_index = 0;
828
0
        while (1) {
829
0
          att = (GF_XMLAttribute*)gf_list_get(validator->xvs_node_in_xvl->attributes, att_index);
830
0
          if (!att) {
831
0
            break;
832
0
          } else if (!strcmp(att->name, "result")) {
833
0
            att_result = att;
834
0
          }
835
0
          att_index++;
836
0
        }
837
0
        if (!att_result) {
838
0
          GF_SAFEALLOC(att_result, GF_XMLAttribute);
839
0
          if (!att_result) {
840
0
            GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate result attribute\n"));
841
0
            return;
842
0
          }
843
0
          att_result->name = gf_strdup("result");
844
0
          gf_list_add(validator->xvs_node_in_xvl->attributes, att_result);
845
0
        }
846
0
        if (att_result->value) gf_free(att_result->value);
847
0
        att_result->value = gf_strdup(validator->xvs_result ? "pass" : "fail");
848
0
      }
849
0
    }
850
0
    gf_xml_dom_del(validator->xvs_parser);
851
0
    validator->xvs_parser = NULL;
852
0
  }
853
0
  validator->xvs_node = NULL;
854
0
  validator->xvs_node_in_xvl = NULL;
855
0
  validator->xvs_filename = NULL;
856
0
  validator->test_filename = NULL;
857
0
  validator->root_odm = NULL;
858
0
  validator->xvs_event_index = 0;
859
0
  validator->snapshot_number = 0;
860
0
}
861
862
static void validator_test_open(GF_Validator *validator)
863
0
{
864
0
  if (!validator->trace_mode) {
865
0
    char filename[100];
866
0
    if (validator->test_base)
867
0
      sprintf(filename, "%s%c%s", validator->test_base, GF_PATH_SEPARATOR, validator->test_filename);
868
0
    else
869
0
      sprintf(filename, "%s", validator->test_filename);
870
871
//deprecated    gf_sc_add_video_listener(validator->compositor, &validator->video_listener);
872
0
    if (validator->is_recording)
873
0
      validator->snapshot_next_frame = GF_TRUE;
874
0
    gf_sc_connect_from_time(validator->compositor, filename, 0, 0, 0, NULL);
875
876
0
  }
877
//  validator->ck = validator->compositor->root_scene->scene_codec ? validator->compositor->root_scene->scene_codec->ck : validator->compositor->root_scene->dyn_ck;
878
0
}
879
880
static Bool validator_xvs_next(GF_Validator *validator, Bool reverse)
881
0
{
882
0
  if (validator->xvl_node) {
883
0
    validator_xvl_get_next_xvs(validator, reverse);
884
0
    if (validator->xvs_filename) {
885
0
      validator_xvs_open(validator);
886
0
      if (!validator->xvs_node) {
887
0
        return GF_FALSE;
888
0
      }
889
0
      if (validator->test_filename) {
890
0
        validator_test_open(validator);
891
0
      } else {
892
0
        validator_xvs_close(validator);
893
0
        return GF_FALSE;
894
0
      }
895
0
    } else {
896
0
      return GF_FALSE;
897
0
    }
898
0
    return GF_TRUE;
899
0
  } else {
900
0
    return GF_FALSE;
901
0
  }
902
0
}
903
904
static Bool validator_load_event(GF_Validator *validator)
905
0
{
906
0
  GF_XMLNode *event_node;
907
0
  u32 att_index;
908
909
0
  memset(&validator->next_event, 0, sizeof(GF_Event));
910
0
  validator->evt_loaded = GF_FALSE;
911
0
  validator->next_event_snapshot = GF_FALSE;
912
913
0
  if (!validator->xvs_node) {
914
0
    validator->compositor->validator_mode = GF_FALSE;
915
0
    return GF_FALSE;
916
0
  }
917
918
0
  while (1) {
919
0
    event_node = (GF_XMLNode*)gf_list_get(validator->xvs_node->content, validator->xvs_event_index);
920
0
    if (!event_node) {
921
0
      return GF_FALSE;
922
0
    } else if (event_node->type == GF_XML_NODE_TYPE) {
923
0
      validator->xvs_event_index++;
924
0
      break;
925
0
    } else {
926
0
      validator->xvs_event_index++;
927
0
    }
928
0
  }
929
930
0
  if (!strcmp(event_node->name, "snapshot")) {
931
0
    validator->next_event_snapshot = GF_TRUE;
932
0
  } else {
933
0
#ifndef GPAC_DISABLE_SVG
934
0
    validator->next_event.type = gf_dom_event_type_by_name(event_node->name);
935
0
    if (validator->next_event.type == GF_EVENT_UNKNOWN)
936
0
#endif
937
0
    {
938
0
      return GF_TRUE;
939
0
    }
940
0
  }
941
942
0
  att_index = 0;
943
0
  while (1) {
944
0
    GF_XMLAttribute *att = (GF_XMLAttribute*)gf_list_get(event_node->attributes, att_index);
945
0
    if (!att) break;
946
0
    if (!strcmp(att->name, "time")) {
947
0
      validator->next_time = atoi(att->value);
948
0
    } else if (!strcmp(att->name, "button")) {
949
0
      if (!strcmp(att->value, "Left")) {
950
0
        validator->next_event.mouse.button = 0;
951
0
      } else if (!strcmp(att->value, "Middle")) {
952
0
        validator->next_event.mouse.button = 1;
953
0
      } else if (!strcmp(att->value, "Right")) {
954
0
        validator->next_event.mouse.button = 2;
955
0
      }
956
0
    } else if (!strcmp(att->name, "x")) {
957
0
      validator->next_event.mouse.x = atoi(att->value);
958
0
    } else if (!strcmp(att->name, "y")) {
959
0
      validator->next_event.mouse.y = atoi(att->value);
960
0
    } else if (!strcmp(att->name, "wheel_pos")) {
961
0
      validator->next_event.mouse.wheel_pos = FLT2FIX(atof(att->value));
962
0
    } else if (!strcmp(att->name, "shift") && !strcmp(att->value, "true")) {
963
0
      validator->next_event.mouse.key_states |= GF_KEY_MOD_SHIFT;
964
0
    } else if (!strcmp(att->name, "alt") && !strcmp(att->value, "true")) {
965
0
      validator->next_event.mouse.key_states |= GF_KEY_MOD_ALT;
966
0
    } else if (!strcmp(att->name, "ctrl") && !strcmp(att->value, "true")) {
967
0
      validator->next_event.mouse.key_states |= GF_KEY_MOD_CTRL;
968
0
#ifndef GPAC_DISABLE_SVG
969
0
    } else if (!strcmp(att->name, "key_identifier")) {
970
0
      validator->next_event.key.key_code = gf_dom_get_key_type(att->value);
971
0
#endif
972
0
    } else if (!strcmp(att->name, "unicode-char")) {
973
0
      validator->next_event.character.unicode_char = atoi(att->value);
974
0
    }
975
0
    att_index++;
976
0
  }
977
0
  validator->evt_loaded = GF_TRUE;
978
0
  validator->compositor->sys_frames_pending = GF_TRUE;
979
0
  validator->compositor->event_pending = GF_TRUE;
980
0
  return GF_TRUE;
981
0
}
982
983
static Bool validator_process(GF_CompositorExt *termext, u32 action, void *param)
984
0
{
985
0
  const char *opt;
986
0
  GF_Validator *validator = (GF_Validator*)termext->udta;
987
988
0
  switch (action) {
989
990
  /* Upon starting of the compositor, we parse (possibly an XVL file), an XVS file, and start the first test sequence */
991
0
  case GF_COMPOSITOR_EXT_START:
992
0
    validator->compositor = (GF_Compositor *) param;
993
994
    /* Check if the validator should be loaded and in which mode */
995
0
    opt = gf_module_get_key((GF_BaseInterface *)termext, "mode");
996
0
    if (!opt) {
997
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("Validator missing configuration, stopping.\n"));
998
0
      return GF_FALSE;
999
0
    } else if (!strcmp(opt, "play") ) {
1000
0
      GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator starting in playback mode.\n"));
1001
0
      validator->is_recording = GF_FALSE;
1002
      //this will indicate to the compositor to increment scene time even though no new changes
1003
0
      validator->compositor->validator_mode = GF_TRUE;
1004
0
    } else if (!strcmp(opt, "record")) {
1005
0
      GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator starting in recording mode.\n"));
1006
0
      validator->is_recording = GF_TRUE;
1007
0
    } else if (!strcmp(opt, "disable")) {
1008
0
      GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator is disabled.\n"));
1009
0
      return GF_FALSE;
1010
0
    } else {
1011
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("Validator configuration using wrong mode, stopping.\n"));
1012
0
      return GF_FALSE;
1013
0
    }
1014
1015
0
    gf_opts_set_key("validator", "mode", "disable");
1016
1017
    /* initializes the validator and starts */
1018
0
    validator->xvs_filename = NULL;
1019
0
    validator->xvl_filename = (char *)gf_module_get_key((GF_BaseInterface *)termext, "xvl");
1020
0
    if (!validator->xvl_filename) {
1021
0
      validator->xvs_filename = (char *)gf_module_get_key((GF_BaseInterface *)termext, "xvs");
1022
0
      if (!validator->xvs_filename) {
1023
0
        validator->xvs_filename = (char *)gf_module_get_key((GF_BaseInterface *)termext, "trace");
1024
0
        if (!validator->xvs_filename) {
1025
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("Validator configuration without input, stopping.\n"));
1026
0
          return GF_FALSE;
1027
0
        }
1028
0
        validator->test_filename = validator->xvs_filename;
1029
0
        validator->trace_mode = GF_TRUE;
1030
0
        GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator using trace file: %s\n", validator->xvs_filename));
1031
0
      } else {
1032
0
        GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator using scenario file: %s\n", validator->xvs_filename));
1033
0
      }
1034
0
    } else {
1035
0
      GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator using scenario playlist: %s\n", validator->xvl_filename));
1036
0
    }
1037
1038
    /* TODO: if start returns 0, the module is not loaded, so the above init (filter registration) is not removed,
1039
       should probably return 1 all the time, to make sure stop is called */
1040
0
    if (validator->xvl_filename) {
1041
0
      validator_xvl_open(validator);
1042
0
      if (!validator->xvl_node) {
1043
0
        return GF_FALSE;
1044
0
      }
1045
0
      validator_xvs_next(validator, GF_FALSE);
1046
0
      if (!validator->xvs_node) {
1047
0
        return GF_FALSE;
1048
0
      }
1049
0
    } else if (validator->xvs_filename) {
1050
0
      validator_xvs_open(validator);
1051
0
      if (!validator->xvs_node) {
1052
0
        return GF_FALSE;
1053
0
      }
1054
0
      if (validator->test_filename) {
1055
0
        validator_test_open(validator);
1056
0
      } else {
1057
0
        validator_xvs_close(validator);
1058
0
        return GF_FALSE;
1059
0
      }
1060
0
    } else {
1061
0
      return GF_FALSE;
1062
0
    }
1063
1064
0
    validator->evt_filter.udta = validator;
1065
0
    if (!validator->is_recording) {
1066
0
      validator->evt_filter.on_event = validator_on_event_play;
1067
0
      termext->caps |= GF_COMPOSITOR_EXTENSION_NOT_THREADED;
1068
0
    } else {
1069
0
      validator->evt_filter.on_event = validator_on_event_record;
1070
0
    }
1071
0
    gf_filter_add_event_listener(validator->compositor->filter, &validator->evt_filter);
1072
0
    validator->video_listener.udta = validator;
1073
0
    validator->video_listener.on_video_frame = validator_on_video_frame;
1074
0
    validator->video_listener.on_video_reconfig = validator_on_video_reconfig;
1075
1076
1077
0
    if (!validator->is_recording) {
1078
0
      validator_load_event(validator);
1079
0
    }
1080
0
    return GF_TRUE;
1081
1082
  /* when the compositor stops, we close the XVS parser and XVL parser if any, restore the config,
1083
  and free all validator data (the validator will be destroyed when the module is unloaded)
1084
  Note: we don't need to disconnect the compositor since it's already stopping */
1085
0
  case GF_COMPOSITOR_EXT_STOP:
1086
0
    gf_filter_remove_event_listener(validator->compositor->filter, &validator->evt_filter);
1087
0
    validator_xvs_close(validator);
1088
0
    validator_xvl_close(validator);
1089
0
    validator->compositor = NULL;
1090
0
    if (validator->test_base) {
1091
0
      gf_free(validator->test_base);
1092
0
      validator->test_base = NULL;
1093
0
    }
1094
    /*auto-disable the recording by default*/
1095
0
    if (!validator->trace_mode) {
1096
0
      if (validator->is_recording ) {
1097
0
        gf_opts_set_key("validator", "mode", "play");
1098
0
      } else {
1099
0
        gf_opts_set_key("validator", "mode", "disable");
1100
0
      }
1101
0
    }
1102
0
    GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Stopping validator\n"));
1103
0
    break;
1104
1105
  /* When called in the main loop of the compositor, we don't do anything in the recording mode.
1106
     In the playing/validating mode, we need to check if an event needs to be dispatched or if snapshots need to be made,
1107
     until there is no more event, in which case we trigger either the load of the next XVS or the quit */
1108
0
  case GF_COMPOSITOR_EXT_PROCESS:
1109
    /* if the time is right, dispatch the event and load the next one */
1110
0
    while (!validator->is_recording && validator->evt_loaded && validator->root_odm && validator->root_odm->ck && (validator->next_time <= gf_clock_time(validator->root_odm->ck) )) {
1111
0
      Bool has_more_events;
1112
0
      validator->compositor->event_pending = GF_FALSE;
1113
      //u32 diff = gf_clock_time(validator->ck) - validator->next_time;
1114
      //GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Time diff: evt_time=%d  clock_time = %d, diff=%d\n", validator->next_time, gf_clock_time(validator->ck), diff));
1115
0
      if (validator->next_event_snapshot) {
1116
0
#ifndef GPAC_DISABLE_AV_PARSERS
1117
0
        Bool res;
1118
0
        char *snap_name = validator_create_snapshot(validator);
1119
0
        gf_free(snap_name);
1120
0
        res = validator_compare_snapshots(validator);
1121
0
        validator->xvs_result &= res;
1122
0
        validator->next_event_snapshot = GF_FALSE;
1123
0
#endif
1124
0
      } else {
1125
0
        gf_sc_on_event(validator->compositor, &validator->next_event);
1126
0
      }
1127
0
      has_more_events = validator_load_event(validator);
1128
0
      if (!has_more_events) {
1129
0
        validator_xvs_close(validator);
1130
0
        if (! validator->trace_mode) {
1131
0
          gf_sc_disconnect(validator->compositor);
1132
//deprecated          gf_sc_remove_video_listener(validator->compositor, &validator->video_listener);
1133
0
          validator_xvs_next(validator, GF_FALSE);
1134
0
          if (!validator->xvs_node) {
1135
0
            GF_Event evt;
1136
0
            memset(&evt, 0, sizeof(GF_Event));
1137
0
            evt.type = GF_EVENT_QUIT;
1138
0
            gf_sc_on_event(validator->compositor, &evt);
1139
0
          } else {
1140
0
            if (!validator->is_recording) {
1141
0
              validator_load_event(validator);
1142
0
            }
1143
0
          }
1144
0
        }
1145
0
      }
1146
0
    }
1147
0
    break;
1148
0
  }
1149
0
  return GF_FALSE;
1150
0
}
1151
1152
static GF_GPACArg ValidatorArgs[] = {
1153
  GF_DEF_ARG("mode", NULL, "operationg mode\n"
1154
  "- play: replay events\n"
1155
  "- record: record events\n"
1156
  "- disable: disable validator", NULL, NULL, GF_ARG_STRING, 0),
1157
  GF_DEF_ARG("xvl", NULL, "filename for XVL", NULL, NULL, GF_ARG_STRING, 0),
1158
  GF_DEF_ARG("xvs", NULL, "filename for XVS", NULL, NULL, GF_ARG_STRING, 0),
1159
  GF_DEF_ARG("trace", NULL, "filename for event trace", NULL, NULL, GF_ARG_STRING, 0),
1160
  {0},
1161
};
1162
1163
GF_CompositorExt *validator_new()
1164
0
{
1165
0
  GF_CompositorExt *dr;
1166
0
  GF_Validator *validator;
1167
0
  dr = (GF_CompositorExt*)gf_malloc(sizeof(GF_CompositorExt));
1168
0
  memset(dr, 0, sizeof(GF_CompositorExt));
1169
0
  GF_REGISTER_MODULE_INTERFACE(dr, GF_COMPOSITOR_EXT_INTERFACE, "validator", "gpac distribution");
1170
1171
0
  GF_SAFEALLOC(validator, GF_Validator);
1172
0
  if (!validator) {
1173
0
    gf_free(dr);
1174
0
    return NULL;
1175
0
  }
1176
0
  dr->process = validator_process;
1177
0
  dr->udta = validator;
1178
0
  dr->description = "Compositor Validator Extension";
1179
0
  dr->args = ValidatorArgs;
1180
0
  return dr;
1181
0
}
1182
1183
1184
void validator_delete(GF_BaseInterface *ifce)
1185
0
{
1186
0
  GF_CompositorExt *dr = (GF_CompositorExt *) ifce;
1187
0
  GF_Validator *validator = (GF_Validator*)dr->udta;
1188
0
  gf_free(validator);
1189
0
  gf_free(dr);
1190
0
}
1191
1192
GPAC_MODULE_EXPORT
1193
const u32 *QueryInterfaces()
1194
0
{
1195
0
  static u32 si [] = {
1196
0
    GF_COMPOSITOR_EXT_INTERFACE,
1197
0
    0
1198
0
  };
1199
0
  return si;
1200
0
}
1201
1202
GPAC_MODULE_EXPORT
1203
GF_BaseInterface *LoadInterface(u32 InterfaceType)
1204
0
{
1205
0
  if (InterfaceType == GF_COMPOSITOR_EXT_INTERFACE) return (GF_BaseInterface *)validator_new();
1206
0
  return NULL;
1207
0
}
1208
1209
GPAC_MODULE_EXPORT
1210
void ShutdownInterface(GF_BaseInterface *ifce)
1211
0
{
1212
0
  switch (ifce->InterfaceType) {
1213
0
  case GF_COMPOSITOR_EXT_INTERFACE:
1214
0
    validator_delete(ifce);
1215
0
    break;
1216
0
  }
1217
0
}
1218
1219
GPAC_MODULE_STATIC_DECLARATION( validator )
1220
1221
1222
#endif //#ifndef GPAC_DISABLE_COMPOSITOR
1223