mirror of
https://github.com/gpac/gpac.git
synced 2026-01-12 00:05:22 +08:00
1224 lines
39 KiB
C
1224 lines
39 KiB
C
/*
|
|
* GPAC - Multimedia Framework C SDK
|
|
*
|
|
* Authors: Cyril Concolato, Jean Le Feuvre
|
|
* Copyright (c) Telecom ParisTech 2010-2023
|
|
* All rights reserved
|
|
*
|
|
* This file is part of GPAC / Test Suite Validator Recorder sub-project
|
|
*
|
|
* GPAC is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* GPAC is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include <gpac/modules/compositor_ext.h>
|
|
#include <gpac/filters.h>
|
|
#include <gpac/internal/compositor_dev.h>
|
|
#include <gpac/internal/media_dev.h>
|
|
#include <gpac/xml.h>
|
|
|
|
#ifndef GPAC_DISABLE_COMPOSITOR
|
|
|
|
typedef struct __validation_module
|
|
{
|
|
GF_Compositor *compositor;
|
|
|
|
Bool is_recording;
|
|
Bool trace_mode;
|
|
|
|
/* Clock used to synchronize events in recording and playback*/
|
|
GF_ObjectManager *root_odm;
|
|
|
|
/* Next event to process */
|
|
Bool next_event_snapshot;
|
|
GF_Event next_event;
|
|
u32 xvs_event_index;
|
|
u32 next_time;
|
|
Bool evt_loaded;
|
|
|
|
GF_VideoListener video_listener;
|
|
|
|
/* XML Validation List (the list of files to be tested) */
|
|
char *xvl_filename;
|
|
GF_DOMParser *xvl_parser;
|
|
GF_XMLNode *xvl_node;
|
|
GF_XMLNode *xvs_node_in_xvl;
|
|
u32 xvl_node_index;
|
|
|
|
/* Pointer to the current validation script file being tested */
|
|
char *xvs_filename;
|
|
GF_DOMParser *xvs_parser;
|
|
GF_XMLNode *xvs_node;
|
|
Bool xvs_result;
|
|
Bool owns_root;
|
|
|
|
/* test sequence */
|
|
char *test_base;
|
|
char *test_filename;
|
|
|
|
Bool snapshot_next_frame;
|
|
u32 snapshot_number;
|
|
|
|
GF_FSEventListener evt_filter;
|
|
} GF_Validator;
|
|
|
|
static void validator_xvs_close(GF_Validator *validator);
|
|
static Bool validator_xvs_next(GF_Validator *validator, Bool reverse);
|
|
|
|
#ifndef GPAC_DISABLE_AV_PARSERS
|
|
|
|
static void validator_xvs_add_snapshot_node(GF_Validator *validator, const char *filename, u32 scene_time)
|
|
{
|
|
GF_XMLNode *snap_node;
|
|
GF_XMLAttribute *att;
|
|
GF_SAFEALLOC(snap_node, GF_XMLNode);
|
|
if (!snap_node) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate snapshot\n"));
|
|
return;
|
|
}
|
|
snap_node->name = gf_strdup("snapshot");
|
|
snap_node->attributes = gf_list_new();
|
|
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate snapshot\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("time");
|
|
att->value = (char*)gf_malloc(100);
|
|
sprintf(att->value, "%d", scene_time);
|
|
gf_list_add(snap_node->attributes, att);
|
|
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate snapshot\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("image");
|
|
att->value = gf_strdup(filename);
|
|
gf_list_add(snap_node->attributes, att);
|
|
gf_list_add(validator->xvs_node->content, snap_node);
|
|
|
|
/* adding an extra text node for line break in serialization */
|
|
GF_SAFEALLOC(snap_node, GF_XMLNode);
|
|
if (!snap_node) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate snapshot\n"));
|
|
return;
|
|
}
|
|
snap_node->type = GF_XML_TEXT_TYPE;
|
|
snap_node->name = gf_strdup("\n");
|
|
gf_list_add(validator->xvs_node->content, snap_node);
|
|
}
|
|
|
|
static char *validator_get_snapshot_name(GF_Validator *validator, Bool is_reference, u32 number)
|
|
{
|
|
char *name = validator->test_filename ? validator->test_filename : validator->xvs_filename;
|
|
char *dot;
|
|
char dumpname[GF_MAX_PATH];
|
|
dot = gf_file_ext_start(name);
|
|
dot[0] = 0;
|
|
sprintf(dumpname, "%s-%s-%03d.png", name, (is_reference?"reference":"newest"), number);
|
|
dot[0] = '.';
|
|
return gf_strdup(dumpname);
|
|
}
|
|
|
|
static char *validator_create_snapshot(GF_Validator *validator)
|
|
{
|
|
GF_Err e;
|
|
GF_VideoSurface fb;
|
|
GF_Compositor *compositor = validator->compositor;
|
|
char *dumpname;
|
|
|
|
dumpname = validator_get_snapshot_name(validator, validator->is_recording, validator->snapshot_number);
|
|
|
|
e = gf_sc_get_screen_buffer(compositor, &fb, GF_SC_GRAB_DEPTH_NONE);
|
|
if (e) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Error dumping screen buffer %s\n", gf_error_to_string(e)));
|
|
} else {
|
|
u32 dst_size = fb.width*fb.height*3;
|
|
char *dst = (char*)gf_malloc(sizeof(char)*dst_size);
|
|
if (!dst) e = GF_OUT_OF_MEM;
|
|
|
|
if (!e)
|
|
e = gf_img_png_enc(fb.video_buffer, fb.width, fb.height, fb.pitch_y, fb.pixel_format, dst, &dst_size);
|
|
|
|
if (!e) {
|
|
FILE *png = gf_fopen(dumpname, "wb");
|
|
if (!png) {
|
|
e = GF_IO_ERR;
|
|
} else {
|
|
if (gf_fwrite(dst, dst_size, png)!=dst_size) e = GF_IO_ERR;
|
|
gf_fclose(png);
|
|
}
|
|
}
|
|
if (dst) gf_free(dst);
|
|
if (e) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Error encoding PNG to %s: %s\n", dumpname, gf_error_to_string(e)));
|
|
} else {
|
|
GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[Validator] Writing file %s\n", dumpname));
|
|
}
|
|
gf_sc_release_screen_buffer(compositor, &fb);
|
|
}
|
|
validator->snapshot_number++;
|
|
return dumpname;
|
|
}
|
|
|
|
static GF_Err validator_file_dec(char *png_filename, u32 *hint_codecid, u32 *width, u32 *height, u32 *pixel_format, char **dst, u32 *dst_size)
|
|
{
|
|
u32 fsize, codecid;
|
|
char *data;
|
|
GF_Err e;
|
|
|
|
codecid = 0;
|
|
if (!hint_codecid || ! *hint_codecid) {
|
|
char *ext = gf_file_ext_start(png_filename);
|
|
if (!ext) return GF_NOT_SUPPORTED;
|
|
if (!stricmp(ext, ".png")) codecid = GF_CODECID_PNG;
|
|
else if (!stricmp(ext, ".jpg") || !stricmp(ext, ".jpeg")) codecid = GF_CODECID_JPEG;
|
|
} else {
|
|
codecid = *hint_codecid;
|
|
}
|
|
|
|
e = gf_file_load_data(png_filename, (u8 **)&data, &fsize);
|
|
if (e) return e;
|
|
|
|
e = GF_NOT_SUPPORTED;
|
|
*dst_size = 0;
|
|
if (codecid == GF_CODECID_JPEG) {
|
|
#ifdef GPAC_HAS_JPEG
|
|
e = gf_img_jpeg_dec(data, fsize, width, height, pixel_format, NULL, dst_size, 0);
|
|
if (*dst_size) {
|
|
*dst = gf_malloc(*dst_size);
|
|
return gf_img_jpeg_dec(data, fsize, width, height, pixel_format, *dst, dst_size, 0);
|
|
}
|
|
#endif
|
|
} else if (codecid == GF_CODECID_PNG) {
|
|
#ifdef GPAC_HAS_PNG
|
|
e = gf_img_png_dec(data, fsize, width, height, pixel_format, NULL, dst_size);
|
|
if (*dst_size) {
|
|
*dst = gf_malloc(*dst_size);
|
|
return gf_img_png_dec(data, fsize, width, height, pixel_format, *dst, dst_size);
|
|
}
|
|
#endif
|
|
}
|
|
return e;
|
|
}
|
|
|
|
static Bool validator_compare_snapshots(GF_Validator *validator)
|
|
{
|
|
char *ref_name, *new_name;
|
|
u32 ref_width, ref_height, ref_pixel_format, ref_data_size;
|
|
u32 new_width, new_height, new_pixel_format, new_data_size;
|
|
char *ref_data=NULL, *new_data=NULL;
|
|
Bool result = GF_FALSE;
|
|
GF_Err e;
|
|
u32 i;
|
|
|
|
u32 snap_number = validator->snapshot_number - 1;
|
|
ref_name = validator_get_snapshot_name(validator, GF_TRUE, snap_number);
|
|
new_name = validator_get_snapshot_name(validator, GF_FALSE, snap_number);
|
|
|
|
e = validator_file_dec(ref_name, NULL, &ref_width, &ref_height, &ref_pixel_format, &ref_data, &ref_data_size);
|
|
if (e) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Cannot decode PNG file %s\n", ref_name));
|
|
goto end;
|
|
}
|
|
e = validator_file_dec(new_name, NULL, &new_width, &new_height, &new_pixel_format, &new_data, &new_data_size);
|
|
if (e) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Cannot decode PNG file %s\n", new_name));
|
|
goto end;
|
|
}
|
|
if (!ref_data) ref_data_size = 0;
|
|
if (!new_data) new_data_size = 0;
|
|
|
|
if (ref_width != new_width) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different widths: %d vs %d\n", ref_width, new_width));
|
|
goto end;
|
|
}
|
|
if (ref_height != new_height) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different heights: %d vs %d\n", ref_height, new_height));
|
|
goto end;
|
|
}
|
|
if (ref_pixel_format != new_pixel_format) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different pixel formats: %d vs %d\n", ref_pixel_format, new_pixel_format));
|
|
goto end;
|
|
}
|
|
if (ref_data_size != new_data_size) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different pixel formats: %d vs %d\n", ref_pixel_format, new_pixel_format));
|
|
goto end;
|
|
}
|
|
|
|
for (i = 0; i<ref_data_size; i++) {
|
|
if (ref_data[i] != new_data[i]) {
|
|
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]));
|
|
break;
|
|
}
|
|
}
|
|
if (i==ref_data_size) result = GF_TRUE;
|
|
|
|
end:
|
|
GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Validator] PNG Comparison result: %s\n", (result?"Same":"Different")));
|
|
if (ref_name) gf_free(ref_name);
|
|
if (new_name) gf_free(new_name);
|
|
if (ref_data) gf_free(ref_data);
|
|
if (new_data) gf_free(new_data);
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
static void validator_on_video_frame(void *udta, u32 time)
|
|
{
|
|
GF_Validator *validator = (GF_Validator *)udta;
|
|
if (validator->snapshot_next_frame) {
|
|
#ifndef GPAC_DISABLE_AV_PARSERS
|
|
char *snap_name = validator_create_snapshot(validator);
|
|
validator_xvs_add_snapshot_node(validator, snap_name, gf_clock_time(validator->root_odm->ck));
|
|
gf_free(snap_name);
|
|
#endif
|
|
validator->snapshot_next_frame = GF_FALSE;
|
|
}
|
|
}
|
|
|
|
static void validator_on_video_reconfig(void *udta, u32 width, u32 height, u8 bpp)
|
|
{
|
|
}
|
|
|
|
Bool validator_on_event_play(void *udta, GF_Event *event, Bool consumed_by_compositor)
|
|
{
|
|
GF_Validator *validator = (GF_Validator *)udta;
|
|
switch (event->type) {
|
|
case GF_EVENT_CONNECT:
|
|
if (event->connect.is_connected) {
|
|
if (!validator->trace_mode) {
|
|
//deprecated gf_sc_add_video_listener(validator->compositor, &validator->video_listener);
|
|
}
|
|
|
|
validator->root_odm = validator->compositor->root_scene->root_od;
|
|
}
|
|
break;
|
|
case GF_EVENT_CLICK:
|
|
case GF_EVENT_MOUSEUP:
|
|
case GF_EVENT_MOUSEDOWN:
|
|
case GF_EVENT_MOUSEOVER:
|
|
case GF_EVENT_MOUSEOUT:
|
|
case GF_EVENT_MOUSEMOVE:
|
|
case GF_EVENT_MOUSEWHEEL:
|
|
case GF_EVENT_KEYDOWN:
|
|
case GF_EVENT_TEXTINPUT:
|
|
return GF_TRUE;
|
|
case GF_EVENT_KEYUP:
|
|
if ((event->key.key_code == GF_KEY_END)&&(event->key.flags & GF_KEY_MOD_CTRL)) {
|
|
GF_Event evt;
|
|
memset(&evt, 0, sizeof(GF_Event));
|
|
evt.type = GF_EVENT_QUIT;
|
|
gf_sc_on_event(validator->compositor, &evt);
|
|
}
|
|
return GF_TRUE;
|
|
}
|
|
return GF_FALSE;
|
|
}
|
|
|
|
static void validator_xvs_add_event_dom(GF_Validator *validator, GF_Event *event)
|
|
{
|
|
GF_XMLNode *evt_node;
|
|
GF_XMLAttribute *att;
|
|
|
|
GF_SAFEALLOC(evt_node, GF_XMLNode);
|
|
if (!evt_node) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event\n"));
|
|
return;
|
|
}
|
|
|
|
switch (event->type) {
|
|
case GF_EVENT_CLICK:
|
|
case GF_EVENT_MOUSEUP:
|
|
case GF_EVENT_MOUSEDOWN:
|
|
case GF_EVENT_MOUSEOVER:
|
|
case GF_EVENT_MOUSEOUT:
|
|
case GF_EVENT_MOUSEMOVE:
|
|
case GF_EVENT_MOUSEWHEEL:
|
|
case GF_EVENT_KEYUP:
|
|
case GF_EVENT_KEYDOWN:
|
|
case GF_EVENT_TEXTINPUT:
|
|
#ifndef GPAC_DISABLE_SVG
|
|
evt_node->name = gf_strdup(gf_dom_event_get_name(event->type));
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
if (!evt_node->name) {
|
|
gf_free(evt_node);
|
|
return;
|
|
}
|
|
|
|
evt_node->attributes = gf_list_new();
|
|
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event time\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("time");
|
|
att->value = (char*)gf_malloc(100);
|
|
sprintf(att->value, "%f", gf_scene_get_time(validator->compositor->root_scene)*1000);
|
|
gf_list_add(evt_node->attributes, att);
|
|
|
|
switch (event->type) {
|
|
case GF_EVENT_CLICK:
|
|
case GF_EVENT_MOUSEUP:
|
|
case GF_EVENT_MOUSEDOWN:
|
|
case GF_EVENT_MOUSEOVER:
|
|
case GF_EVENT_MOUSEOUT:
|
|
case GF_EVENT_MOUSEMOVE:
|
|
case GF_EVENT_MOUSEWHEEL:
|
|
if (event->type == GF_EVENT_MOUSEDOWN || event->type == GF_EVENT_MOUSEUP) {
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("button");
|
|
switch (event->mouse.button) {
|
|
case 0:
|
|
att->value = gf_strdup("Left");
|
|
break;
|
|
case 1:
|
|
att->value = gf_strdup("Middle");
|
|
break;
|
|
case 2:
|
|
att->value = gf_strdup("Right");
|
|
break;
|
|
}
|
|
gf_list_add(evt_node->attributes, att);
|
|
}
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("x");
|
|
att->value = (char*)gf_malloc(100);
|
|
sprintf(att->value, "%d", event->mouse.x);
|
|
gf_list_add(evt_node->attributes, att);
|
|
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("y");
|
|
att->value = (char*)gf_malloc(100);
|
|
sprintf(att->value, "%d", event->mouse.y);
|
|
gf_list_add(evt_node->attributes, att);
|
|
if (event->type == GF_EVENT_MOUSEWHEEL) {
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("wheel_pos");
|
|
att->value = (char*)gf_malloc(100);
|
|
sprintf(att->value, "%f", FIX2FLT( event->mouse.wheel_pos) );
|
|
gf_list_add(evt_node->attributes, att);
|
|
}
|
|
if (event->mouse.key_states & GF_KEY_MOD_SHIFT) {
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("shift");
|
|
att->value = gf_strdup("true");
|
|
gf_list_add(evt_node->attributes, att);
|
|
}
|
|
if (event->mouse.key_states & GF_KEY_MOD_CTRL) {
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("ctrl");
|
|
att->value = gf_strdup("true");
|
|
gf_list_add(evt_node->attributes, att);
|
|
}
|
|
if (event->mouse.key_states & GF_KEY_MOD_ALT) {
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("alt");
|
|
att->value = gf_strdup("true");
|
|
gf_list_add(evt_node->attributes, att);
|
|
}
|
|
break;
|
|
/*Key Events*/
|
|
case GF_EVENT_KEYUP:
|
|
case GF_EVENT_KEYDOWN:
|
|
case GF_EVENT_LONGKEYPRESS:
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("key_identifier");
|
|
#ifndef GPAC_DISABLE_SVG
|
|
att->value = gf_strdup(gf_dom_get_key_name(event->key.key_code));
|
|
#endif
|
|
gf_list_add(evt_node->attributes, att);
|
|
if (event->key.flags & GF_KEY_MOD_SHIFT) {
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("shift");
|
|
att->value = gf_strdup("true");
|
|
gf_list_add(evt_node->attributes, att);
|
|
}
|
|
if (event->key.flags & GF_KEY_MOD_CTRL) {
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("ctrl");
|
|
att->value = gf_strdup("true");
|
|
gf_list_add(evt_node->attributes, att);
|
|
}
|
|
if (event->key.flags & GF_KEY_MOD_ALT) {
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("alt");
|
|
att->value = gf_strdup("true");
|
|
gf_list_add(evt_node->attributes, att);
|
|
}
|
|
break;
|
|
case GF_EVENT_TEXTINPUT:
|
|
att = (GF_XMLAttribute *) gf_malloc(sizeof(GF_XMLAttribute));
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event info\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("unicode-char");
|
|
att->value = (char*)gf_malloc(100);
|
|
sprintf(att->value, "%d", event->character.unicode_char);
|
|
gf_list_add(evt_node->attributes, att);
|
|
break;
|
|
}
|
|
gf_list_add(validator->xvs_node->content, evt_node);
|
|
/* adding an extra text node for line break in serialization */
|
|
GF_SAFEALLOC(evt_node, GF_XMLNode);
|
|
if (!evt_node) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate event\n"));
|
|
return;
|
|
}
|
|
evt_node->type = GF_XML_TEXT_TYPE;
|
|
evt_node->name = gf_strdup("\n");
|
|
gf_list_add(validator->xvs_node->content, evt_node);
|
|
}
|
|
|
|
Bool validator_on_event_record(void *udta, GF_Event *event, Bool consumed_by_compositor)
|
|
{
|
|
GF_Validator *validator = (GF_Validator *)udta;
|
|
Bool rec_event = GF_TRUE;
|
|
switch (event->type) {
|
|
case GF_EVENT_CONNECT:
|
|
if (event->connect.is_connected) {
|
|
if (!validator->trace_mode) {
|
|
//deprecated gf_sc_add_video_listener(validator->compositor, &validator->video_listener);
|
|
}
|
|
validator->root_odm = validator->compositor->root_scene->root_od;
|
|
}
|
|
break;
|
|
case GF_EVENT_KEYDOWN:
|
|
if (event->key.key_code == GF_KEY_INSERT) {
|
|
rec_event = GF_FALSE;
|
|
} else if (event->key.key_code == GF_KEY_PAGEDOWN) {
|
|
rec_event = GF_FALSE;
|
|
} else if (event->key.key_code == GF_KEY_PAGEUP) {
|
|
rec_event = GF_FALSE;
|
|
} else if (event->key.key_code == GF_KEY_END) {
|
|
rec_event = GF_FALSE;
|
|
} else if (event->key.key_code == GF_KEY_CONTROL) {
|
|
rec_event = GF_FALSE;
|
|
} else if (event->key.flags & GF_KEY_MOD_CTRL) {
|
|
rec_event = GF_FALSE;
|
|
}
|
|
break;
|
|
case GF_EVENT_KEYUP:
|
|
if (event->key.flags & GF_KEY_MOD_CTRL) {
|
|
rec_event = GF_FALSE;
|
|
if (event->key.key_code == GF_KEY_INSERT) {
|
|
#ifndef GPAC_DISABLE_AV_PARSERS
|
|
char *snap_name = validator_create_snapshot(validator);
|
|
validator_xvs_add_snapshot_node(validator, snap_name, gf_clock_time(validator->root_odm->ck));
|
|
gf_free(snap_name);
|
|
#endif
|
|
} else if (event->key.key_code == GF_KEY_END) {
|
|
GF_Event evt;
|
|
memset(&evt, 0, sizeof(GF_Event));
|
|
evt.type = GF_EVENT_QUIT;
|
|
gf_sc_on_event(validator->compositor, &evt);
|
|
} else if (event->key.key_code == GF_KEY_F1) {
|
|
validator->snapshot_next_frame = GF_TRUE;
|
|
}
|
|
} else if (event->key.key_code == GF_KEY_PAGEDOWN) {
|
|
rec_event = GF_FALSE;
|
|
validator_xvs_close(validator);
|
|
gf_sc_disconnect(validator->compositor);
|
|
//deprecated gf_sc_remove_video_listener(validator->compositor, &validator->video_listener);
|
|
validator_xvs_next(validator, GF_FALSE);
|
|
} else if (event->key.key_code == GF_KEY_PAGEUP) {
|
|
rec_event = GF_FALSE;
|
|
validator_xvs_close(validator);
|
|
gf_sc_disconnect(validator->compositor);
|
|
//deprecated gf_sc_remove_video_listener(validator->compositor, &validator->video_listener);
|
|
validator_xvs_next(validator, GF_TRUE);
|
|
} else if (event->key.key_code == GF_KEY_CONTROL) {
|
|
rec_event = GF_FALSE;
|
|
}
|
|
break;
|
|
}
|
|
if (rec_event) {
|
|
validator_xvs_add_event_dom(validator, event);
|
|
}
|
|
return GF_FALSE;
|
|
}
|
|
|
|
static void validator_xvl_open(GF_Validator *validator)
|
|
{
|
|
GF_Err e;
|
|
u32 att_index;
|
|
validator->xvl_parser = gf_xml_dom_new();
|
|
e = gf_xml_dom_parse(validator->xvl_parser, validator->xvl_filename, NULL, NULL);
|
|
if (e != GF_OK) {
|
|
gf_xml_dom_del(validator->xvl_parser);
|
|
validator->xvl_parser = NULL;
|
|
return;
|
|
}
|
|
validator->xvl_node = gf_xml_dom_get_root(validator->xvl_parser);
|
|
if (!validator->xvl_node) {
|
|
gf_xml_dom_del(validator->xvl_parser);
|
|
validator->xvl_parser = NULL;
|
|
return;
|
|
}
|
|
att_index = 0;
|
|
while (1) {
|
|
GF_XMLAttribute *att = (GF_XMLAttribute*)gf_list_get(validator->xvl_node->attributes, att_index);
|
|
if (!att) break;
|
|
if (!strcmp(att->name, "content-base")) {
|
|
validator->test_base = gf_strdup(att->value);
|
|
}
|
|
att_index++;
|
|
}
|
|
}
|
|
|
|
static void validator_xvl_close(GF_Validator *validator)
|
|
{
|
|
if (validator->xvl_parser) {
|
|
/* writing the validation results */
|
|
if (!validator->is_recording) {
|
|
FILE *xvl_fp;
|
|
char *xvl_content;
|
|
char result_filename[GF_MAX_PATH];
|
|
char *dot;
|
|
xvl_content = gf_xml_dom_serialize(validator->xvl_node, GF_FALSE, GF_FALSE);
|
|
dot = gf_file_ext_start(validator->xvl_filename);
|
|
dot[0] = 0;
|
|
sprintf(result_filename, "%s-result.xml", validator->xvl_filename);
|
|
dot[0] = '.';
|
|
xvl_fp = gf_fopen(result_filename, "wt");
|
|
gf_fwrite(xvl_content, strlen(xvl_content), xvl_fp);
|
|
gf_fclose(xvl_fp);
|
|
gf_free(xvl_content);
|
|
}
|
|
gf_xml_dom_del(validator->xvl_parser);
|
|
validator->xvl_parser = NULL;
|
|
validator->xvl_filename = NULL;
|
|
}
|
|
}
|
|
|
|
static void validator_xvl_get_next_xvs(GF_Validator *validator, Bool reverse)
|
|
{
|
|
u32 xvl_att_index;
|
|
validator->xvs_node = NULL;
|
|
validator->xvs_filename = NULL;
|
|
validator->test_filename = NULL;
|
|
while (1) {
|
|
validator->xvs_node_in_xvl = (GF_XMLNode*)gf_list_get(validator->xvl_node->content, validator->xvl_node_index);
|
|
if (!validator->xvs_node_in_xvl) {
|
|
return;
|
|
}
|
|
if (validator->xvs_node_in_xvl->type != GF_XML_NODE_TYPE) {
|
|
if (!reverse) validator->xvl_node_index++;
|
|
else validator->xvl_node_index--;
|
|
continue;
|
|
}
|
|
xvl_att_index = 0;
|
|
while(1) {
|
|
GF_XMLAttribute *att = (GF_XMLAttribute*)gf_list_get(validator->xvs_node_in_xvl->attributes, xvl_att_index);
|
|
if (!att) break;
|
|
if (!strcmp(att->name, "scenario")) {
|
|
validator->xvs_filename = att->value;
|
|
} else if (!strcmp(att->name, "content")) {
|
|
validator->test_filename = att->value;
|
|
}
|
|
xvl_att_index++;
|
|
}
|
|
if (!reverse) validator->xvl_node_index++;
|
|
else validator->xvl_node_index--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static Bool validator_xvs_open(GF_Validator *validator)
|
|
{
|
|
GF_Err e;
|
|
GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Validator] Opening Validation Script: %s\n", validator->xvs_filename));
|
|
validator->snapshot_number = 0;
|
|
validator->xvs_parser = gf_xml_dom_new();
|
|
e = gf_xml_dom_parse(validator->xvs_parser, validator->xvs_filename, NULL, NULL);
|
|
if (e != GF_OK) {
|
|
if (validator->is_recording) {
|
|
GF_SAFEALLOC(validator->xvs_node, GF_XMLNode);
|
|
if (!validator->xvs_node) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate root node\n"));
|
|
return 0;
|
|
}
|
|
|
|
validator->xvs_node->name = gf_strdup("TestValidationScript");
|
|
validator->xvs_node->attributes = gf_list_new();
|
|
validator->xvs_node->content = gf_list_new();
|
|
validator->owns_root = GF_TRUE;
|
|
} else {
|
|
gf_xml_dom_del(validator->xvs_parser);
|
|
validator->xvs_parser = NULL;
|
|
return GF_FALSE;
|
|
}
|
|
} else {
|
|
validator->xvs_node = gf_xml_dom_get_root(validator->xvs_parser);
|
|
}
|
|
/* Get the file name from the XVS if not found in the XVL */
|
|
if (!validator->test_filename) {
|
|
GF_XMLAttribute *att;
|
|
GF_XMLAttribute *att_file;
|
|
u32 att_index = 0;
|
|
att_file = NULL;
|
|
while (1) {
|
|
att = (GF_XMLAttribute*)gf_list_get(validator->xvs_node->attributes, att_index);
|
|
if (!att) {
|
|
break;
|
|
} else if (!strcmp(att->name, "file")) {
|
|
att_file = att;
|
|
}
|
|
att_index++;
|
|
}
|
|
|
|
if (!att_file) {
|
|
gf_xml_dom_del(validator->xvs_parser);
|
|
validator->xvs_parser = NULL;
|
|
validator->xvs_node = NULL;
|
|
return GF_FALSE;
|
|
} else {
|
|
char *sep;
|
|
sep = strrchr(att_file->value, GF_PATH_SEPARATOR);
|
|
if (!sep) {
|
|
validator->test_filename = att_file->value;
|
|
} else {
|
|
sep[0] = 0;
|
|
validator->test_base = gf_strdup(att_file->value);
|
|
sep[0] = GF_PATH_SEPARATOR;
|
|
validator->test_filename = sep+1;
|
|
}
|
|
}
|
|
}
|
|
if (validator->is_recording) {
|
|
GF_XMLNode *node;
|
|
/* Removing prerecorded interactions */
|
|
while (gf_list_count(validator->xvs_node->content)) {
|
|
GF_XMLNode *child = (GF_XMLNode *)gf_list_last(validator->xvs_node->content);
|
|
gf_list_rem_last(validator->xvs_node->content);
|
|
gf_xml_dom_node_del(child);
|
|
}
|
|
/* adding an extra text node for line break in serialization */
|
|
GF_SAFEALLOC(node, GF_XMLNode);
|
|
if (!node) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate node\n"));
|
|
return GF_FALSE;
|
|
}
|
|
node->type = GF_XML_TEXT_TYPE;
|
|
node->name = gf_strdup("\n");
|
|
gf_list_add(validator->xvs_node->content, node);
|
|
} else {
|
|
validator->xvs_result = GF_TRUE;
|
|
}
|
|
return GF_TRUE;
|
|
}
|
|
|
|
static void validator_xvs_close(GF_Validator *validator)
|
|
{
|
|
if (validator->xvs_parser) {
|
|
if (validator->is_recording) {
|
|
FILE *xvs_fp;
|
|
char *xvs_content;
|
|
GF_XMLAttribute *att_file = NULL;
|
|
u32 att_index = 0;
|
|
|
|
if (!validator->trace_mode) {
|
|
GF_XMLAttribute *att;
|
|
while (1) {
|
|
att = (GF_XMLAttribute*)gf_list_get(validator->xvs_node->attributes, att_index);
|
|
if (!att) {
|
|
break;
|
|
} else if (!strcmp(att->name, "file")) {
|
|
att_file = att;
|
|
}
|
|
att_index++;
|
|
}
|
|
|
|
if (!att_file) {
|
|
GF_SAFEALLOC(att, GF_XMLAttribute);
|
|
if (!att) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate file attribute\n"));
|
|
return;
|
|
}
|
|
att->name = gf_strdup("file");
|
|
gf_list_add(validator->xvs_node->attributes, att);
|
|
} else {
|
|
att = att_file;
|
|
if (att->value) gf_free(att->value);
|
|
}
|
|
if (validator->test_base) {
|
|
char filename[100];
|
|
sprintf(filename, "%s%c%s", validator->test_base, GF_PATH_SEPARATOR, validator->test_filename);
|
|
att->value = gf_strdup(filename);
|
|
} else {
|
|
att->value = gf_strdup(validator->test_filename);
|
|
}
|
|
}
|
|
xvs_content = gf_xml_dom_serialize(validator->xvs_node, GF_FALSE, GF_FALSE);
|
|
xvs_fp = gf_fopen(validator->xvs_filename, "wt");
|
|
gf_fwrite(xvs_content, strlen(xvs_content), xvs_fp);
|
|
gf_fclose(xvs_fp);
|
|
gf_free(xvs_content);
|
|
if (validator->owns_root)
|
|
gf_xml_dom_node_del(validator->xvs_node);
|
|
} else {
|
|
GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Validator] XVS Result : %s\n", (validator->xvs_result?"Success":"Failure")));
|
|
if (validator->xvs_node_in_xvl) {
|
|
GF_XMLAttribute *att;
|
|
GF_XMLAttribute *att_result = NULL;
|
|
u32 att_index = 0;
|
|
while (1) {
|
|
att = (GF_XMLAttribute*)gf_list_get(validator->xvs_node_in_xvl->attributes, att_index);
|
|
if (!att) {
|
|
break;
|
|
} else if (!strcmp(att->name, "result")) {
|
|
att_result = att;
|
|
}
|
|
att_index++;
|
|
}
|
|
if (!att_result) {
|
|
GF_SAFEALLOC(att_result, GF_XMLAttribute);
|
|
if (!att_result) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Failed to allocate result attribute\n"));
|
|
return;
|
|
}
|
|
att_result->name = gf_strdup("result");
|
|
gf_list_add(validator->xvs_node_in_xvl->attributes, att_result);
|
|
}
|
|
if (att_result->value) gf_free(att_result->value);
|
|
att_result->value = gf_strdup(validator->xvs_result ? "pass" : "fail");
|
|
}
|
|
}
|
|
gf_xml_dom_del(validator->xvs_parser);
|
|
validator->xvs_parser = NULL;
|
|
}
|
|
validator->xvs_node = NULL;
|
|
validator->xvs_node_in_xvl = NULL;
|
|
validator->xvs_filename = NULL;
|
|
validator->test_filename = NULL;
|
|
validator->root_odm = NULL;
|
|
validator->xvs_event_index = 0;
|
|
validator->snapshot_number = 0;
|
|
}
|
|
|
|
static void validator_test_open(GF_Validator *validator)
|
|
{
|
|
if (!validator->trace_mode) {
|
|
char filename[100];
|
|
if (validator->test_base)
|
|
sprintf(filename, "%s%c%s", validator->test_base, GF_PATH_SEPARATOR, validator->test_filename);
|
|
else
|
|
sprintf(filename, "%s", validator->test_filename);
|
|
|
|
//deprecated gf_sc_add_video_listener(validator->compositor, &validator->video_listener);
|
|
if (validator->is_recording)
|
|
validator->snapshot_next_frame = GF_TRUE;
|
|
gf_sc_connect_from_time(validator->compositor, filename, 0, 0, 0, NULL);
|
|
|
|
}
|
|
// validator->ck = validator->compositor->root_scene->scene_codec ? validator->compositor->root_scene->scene_codec->ck : validator->compositor->root_scene->dyn_ck;
|
|
}
|
|
|
|
static Bool validator_xvs_next(GF_Validator *validator, Bool reverse)
|
|
{
|
|
if (validator->xvl_node) {
|
|
validator_xvl_get_next_xvs(validator, reverse);
|
|
if (validator->xvs_filename) {
|
|
validator_xvs_open(validator);
|
|
if (!validator->xvs_node) {
|
|
return GF_FALSE;
|
|
}
|
|
if (validator->test_filename) {
|
|
validator_test_open(validator);
|
|
} else {
|
|
validator_xvs_close(validator);
|
|
return GF_FALSE;
|
|
}
|
|
} else {
|
|
return GF_FALSE;
|
|
}
|
|
return GF_TRUE;
|
|
} else {
|
|
return GF_FALSE;
|
|
}
|
|
}
|
|
|
|
static Bool validator_load_event(GF_Validator *validator)
|
|
{
|
|
GF_XMLNode *event_node;
|
|
u32 att_index;
|
|
|
|
memset(&validator->next_event, 0, sizeof(GF_Event));
|
|
validator->evt_loaded = GF_FALSE;
|
|
validator->next_event_snapshot = GF_FALSE;
|
|
|
|
if (!validator->xvs_node) {
|
|
validator->compositor->validator_mode = GF_FALSE;
|
|
return GF_FALSE;
|
|
}
|
|
|
|
while (1) {
|
|
event_node = (GF_XMLNode*)gf_list_get(validator->xvs_node->content, validator->xvs_event_index);
|
|
if (!event_node) {
|
|
return GF_FALSE;
|
|
} else if (event_node->type == GF_XML_NODE_TYPE) {
|
|
validator->xvs_event_index++;
|
|
break;
|
|
} else {
|
|
validator->xvs_event_index++;
|
|
}
|
|
}
|
|
|
|
if (!strcmp(event_node->name, "snapshot")) {
|
|
validator->next_event_snapshot = GF_TRUE;
|
|
} else {
|
|
#ifndef GPAC_DISABLE_SVG
|
|
validator->next_event.type = gf_dom_event_type_by_name(event_node->name);
|
|
if (validator->next_event.type == GF_EVENT_UNKNOWN)
|
|
#endif
|
|
{
|
|
return GF_TRUE;
|
|
}
|
|
}
|
|
|
|
att_index = 0;
|
|
while (1) {
|
|
GF_XMLAttribute *att = (GF_XMLAttribute*)gf_list_get(event_node->attributes, att_index);
|
|
if (!att) break;
|
|
if (!strcmp(att->name, "time")) {
|
|
validator->next_time = atoi(att->value);
|
|
} else if (!strcmp(att->name, "button")) {
|
|
if (!strcmp(att->value, "Left")) {
|
|
validator->next_event.mouse.button = 0;
|
|
} else if (!strcmp(att->value, "Middle")) {
|
|
validator->next_event.mouse.button = 1;
|
|
} else if (!strcmp(att->value, "Right")) {
|
|
validator->next_event.mouse.button = 2;
|
|
}
|
|
} else if (!strcmp(att->name, "x")) {
|
|
validator->next_event.mouse.x = atoi(att->value);
|
|
} else if (!strcmp(att->name, "y")) {
|
|
validator->next_event.mouse.y = atoi(att->value);
|
|
} else if (!strcmp(att->name, "wheel_pos")) {
|
|
validator->next_event.mouse.wheel_pos = FLT2FIX(atof(att->value));
|
|
} else if (!strcmp(att->name, "shift") && !strcmp(att->value, "true")) {
|
|
validator->next_event.mouse.key_states |= GF_KEY_MOD_SHIFT;
|
|
} else if (!strcmp(att->name, "alt") && !strcmp(att->value, "true")) {
|
|
validator->next_event.mouse.key_states |= GF_KEY_MOD_ALT;
|
|
} else if (!strcmp(att->name, "ctrl") && !strcmp(att->value, "true")) {
|
|
validator->next_event.mouse.key_states |= GF_KEY_MOD_CTRL;
|
|
#ifndef GPAC_DISABLE_SVG
|
|
} else if (!strcmp(att->name, "key_identifier")) {
|
|
validator->next_event.key.key_code = gf_dom_get_key_type(att->value);
|
|
#endif
|
|
} else if (!strcmp(att->name, "unicode-char")) {
|
|
validator->next_event.character.unicode_char = atoi(att->value);
|
|
}
|
|
att_index++;
|
|
}
|
|
validator->evt_loaded = GF_TRUE;
|
|
validator->compositor->sys_frames_pending = GF_TRUE;
|
|
validator->compositor->event_pending = GF_TRUE;
|
|
return GF_TRUE;
|
|
}
|
|
|
|
static Bool validator_process(GF_CompositorExt *termext, u32 action, void *param)
|
|
{
|
|
const char *opt;
|
|
GF_Validator *validator = (GF_Validator*)termext->udta;
|
|
|
|
switch (action) {
|
|
|
|
/* Upon starting of the compositor, we parse (possibly an XVL file), an XVS file, and start the first test sequence */
|
|
case GF_COMPOSITOR_EXT_START:
|
|
validator->compositor = (GF_Compositor *) param;
|
|
|
|
/* Check if the validator should be loaded and in which mode */
|
|
opt = gf_module_get_key((GF_BaseInterface *)termext, "mode");
|
|
if (!opt) {
|
|
GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("Validator missing configuration, stopping.\n"));
|
|
return GF_FALSE;
|
|
} else if (!strcmp(opt, "play") ) {
|
|
GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator starting in playback mode.\n"));
|
|
validator->is_recording = GF_FALSE;
|
|
//this will indicate to the compositor to increment scene time even though no new changes
|
|
validator->compositor->validator_mode = GF_TRUE;
|
|
} else if (!strcmp(opt, "record")) {
|
|
GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator starting in recording mode.\n"));
|
|
validator->is_recording = GF_TRUE;
|
|
} else if (!strcmp(opt, "disable")) {
|
|
GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator is disabled.\n"));
|
|
return GF_FALSE;
|
|
} else {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("Validator configuration using wrong mode, stopping.\n"));
|
|
return GF_FALSE;
|
|
}
|
|
|
|
gf_opts_set_key("validator", "mode", "disable");
|
|
|
|
/* initializes the validator and starts */
|
|
validator->xvs_filename = NULL;
|
|
validator->xvl_filename = (char *)gf_module_get_key((GF_BaseInterface *)termext, "xvl");
|
|
if (!validator->xvl_filename) {
|
|
validator->xvs_filename = (char *)gf_module_get_key((GF_BaseInterface *)termext, "xvs");
|
|
if (!validator->xvs_filename) {
|
|
validator->xvs_filename = (char *)gf_module_get_key((GF_BaseInterface *)termext, "trace");
|
|
if (!validator->xvs_filename) {
|
|
GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("Validator configuration without input, stopping.\n"));
|
|
return GF_FALSE;
|
|
}
|
|
validator->test_filename = validator->xvs_filename;
|
|
validator->trace_mode = GF_TRUE;
|
|
GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator using trace file: %s\n", validator->xvs_filename));
|
|
} else {
|
|
GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator using scenario file: %s\n", validator->xvs_filename));
|
|
}
|
|
} else {
|
|
GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator using scenario playlist: %s\n", validator->xvl_filename));
|
|
}
|
|
|
|
/* TODO: if start returns 0, the module is not loaded, so the above init (filter registration) is not removed,
|
|
should probably return 1 all the time, to make sure stop is called */
|
|
if (validator->xvl_filename) {
|
|
validator_xvl_open(validator);
|
|
if (!validator->xvl_node) {
|
|
return GF_FALSE;
|
|
}
|
|
validator_xvs_next(validator, GF_FALSE);
|
|
if (!validator->xvs_node) {
|
|
return GF_FALSE;
|
|
}
|
|
} else if (validator->xvs_filename) {
|
|
validator_xvs_open(validator);
|
|
if (!validator->xvs_node) {
|
|
return GF_FALSE;
|
|
}
|
|
if (validator->test_filename) {
|
|
validator_test_open(validator);
|
|
} else {
|
|
validator_xvs_close(validator);
|
|
return GF_FALSE;
|
|
}
|
|
} else {
|
|
return GF_FALSE;
|
|
}
|
|
|
|
validator->evt_filter.udta = validator;
|
|
if (!validator->is_recording) {
|
|
validator->evt_filter.on_event = validator_on_event_play;
|
|
termext->caps |= GF_COMPOSITOR_EXTENSION_NOT_THREADED;
|
|
} else {
|
|
validator->evt_filter.on_event = validator_on_event_record;
|
|
}
|
|
gf_filter_add_event_listener(validator->compositor->filter, &validator->evt_filter);
|
|
validator->video_listener.udta = validator;
|
|
validator->video_listener.on_video_frame = validator_on_video_frame;
|
|
validator->video_listener.on_video_reconfig = validator_on_video_reconfig;
|
|
|
|
|
|
if (!validator->is_recording) {
|
|
validator_load_event(validator);
|
|
}
|
|
return GF_TRUE;
|
|
|
|
/* when the compositor stops, we close the XVS parser and XVL parser if any, restore the config,
|
|
and free all validator data (the validator will be destroyed when the module is unloaded)
|
|
Note: we don't need to disconnect the compositor since it's already stopping */
|
|
case GF_COMPOSITOR_EXT_STOP:
|
|
gf_filter_remove_event_listener(validator->compositor->filter, &validator->evt_filter);
|
|
validator_xvs_close(validator);
|
|
validator_xvl_close(validator);
|
|
validator->compositor = NULL;
|
|
if (validator->test_base) {
|
|
gf_free(validator->test_base);
|
|
validator->test_base = NULL;
|
|
}
|
|
/*auto-disable the recording by default*/
|
|
if (!validator->trace_mode) {
|
|
if (validator->is_recording ) {
|
|
gf_opts_set_key("validator", "mode", "play");
|
|
} else {
|
|
gf_opts_set_key("validator", "mode", "disable");
|
|
}
|
|
}
|
|
GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Stopping validator\n"));
|
|
break;
|
|
|
|
/* When called in the main loop of the compositor, we don't do anything in the recording mode.
|
|
In the playing/validating mode, we need to check if an event needs to be dispatched or if snapshots need to be made,
|
|
until there is no more event, in which case we trigger either the load of the next XVS or the quit */
|
|
case GF_COMPOSITOR_EXT_PROCESS:
|
|
/* if the time is right, dispatch the event and load the next one */
|
|
while (!validator->is_recording && validator->evt_loaded && validator->root_odm && validator->root_odm->ck && (validator->next_time <= gf_clock_time(validator->root_odm->ck) )) {
|
|
Bool has_more_events;
|
|
validator->compositor->event_pending = GF_FALSE;
|
|
//u32 diff = gf_clock_time(validator->ck) - validator->next_time;
|
|
//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));
|
|
if (validator->next_event_snapshot) {
|
|
#ifndef GPAC_DISABLE_AV_PARSERS
|
|
Bool res;
|
|
char *snap_name = validator_create_snapshot(validator);
|
|
gf_free(snap_name);
|
|
res = validator_compare_snapshots(validator);
|
|
validator->xvs_result &= res;
|
|
validator->next_event_snapshot = GF_FALSE;
|
|
#endif
|
|
} else {
|
|
gf_sc_on_event(validator->compositor, &validator->next_event);
|
|
}
|
|
has_more_events = validator_load_event(validator);
|
|
if (!has_more_events) {
|
|
validator_xvs_close(validator);
|
|
if (! validator->trace_mode) {
|
|
gf_sc_disconnect(validator->compositor);
|
|
//deprecated gf_sc_remove_video_listener(validator->compositor, &validator->video_listener);
|
|
validator_xvs_next(validator, GF_FALSE);
|
|
if (!validator->xvs_node) {
|
|
GF_Event evt;
|
|
memset(&evt, 0, sizeof(GF_Event));
|
|
evt.type = GF_EVENT_QUIT;
|
|
gf_sc_on_event(validator->compositor, &evt);
|
|
} else {
|
|
if (!validator->is_recording) {
|
|
validator_load_event(validator);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return GF_FALSE;
|
|
}
|
|
|
|
static GF_GPACArg ValidatorArgs[] = {
|
|
GF_DEF_ARG("mode", NULL, "operationg mode\n"
|
|
"- play: replay events\n"
|
|
"- record: record events\n"
|
|
"- disable: disable validator", NULL, NULL, GF_ARG_STRING, 0),
|
|
GF_DEF_ARG("xvl", NULL, "filename for XVL", NULL, NULL, GF_ARG_STRING, 0),
|
|
GF_DEF_ARG("xvs", NULL, "filename for XVS", NULL, NULL, GF_ARG_STRING, 0),
|
|
GF_DEF_ARG("trace", NULL, "filename for event trace", NULL, NULL, GF_ARG_STRING, 0),
|
|
{0},
|
|
};
|
|
|
|
GF_CompositorExt *validator_new()
|
|
{
|
|
GF_CompositorExt *dr;
|
|
GF_Validator *validator;
|
|
dr = (GF_CompositorExt*)gf_malloc(sizeof(GF_CompositorExt));
|
|
memset(dr, 0, sizeof(GF_CompositorExt));
|
|
GF_REGISTER_MODULE_INTERFACE(dr, GF_COMPOSITOR_EXT_INTERFACE, "validator", "gpac distribution");
|
|
|
|
GF_SAFEALLOC(validator, GF_Validator);
|
|
if (!validator) {
|
|
gf_free(dr);
|
|
return NULL;
|
|
}
|
|
dr->process = validator_process;
|
|
dr->udta = validator;
|
|
dr->description = "Compositor Validator Extension";
|
|
dr->args = ValidatorArgs;
|
|
return dr;
|
|
}
|
|
|
|
|
|
void validator_delete(GF_BaseInterface *ifce)
|
|
{
|
|
GF_CompositorExt *dr = (GF_CompositorExt *) ifce;
|
|
GF_Validator *validator = (GF_Validator*)dr->udta;
|
|
gf_free(validator);
|
|
gf_free(dr);
|
|
}
|
|
|
|
GPAC_MODULE_EXPORT
|
|
const u32 *QueryInterfaces()
|
|
{
|
|
static u32 si [] = {
|
|
GF_COMPOSITOR_EXT_INTERFACE,
|
|
0
|
|
};
|
|
return si;
|
|
}
|
|
|
|
GPAC_MODULE_EXPORT
|
|
GF_BaseInterface *LoadInterface(u32 InterfaceType)
|
|
{
|
|
if (InterfaceType == GF_COMPOSITOR_EXT_INTERFACE) return (GF_BaseInterface *)validator_new();
|
|
return NULL;
|
|
}
|
|
|
|
GPAC_MODULE_EXPORT
|
|
void ShutdownInterface(GF_BaseInterface *ifce)
|
|
{
|
|
switch (ifce->InterfaceType) {
|
|
case GF_COMPOSITOR_EXT_INTERFACE:
|
|
validator_delete(ifce);
|
|
break;
|
|
}
|
|
}
|
|
|
|
GPAC_MODULE_STATIC_DECLARATION( validator )
|
|
|
|
|
|
#endif //#ifndef GPAC_DISABLE_COMPOSITOR
|
|
|