| /* | |
| * 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 | |